scrapy抓取中國新聞網新聞


目標說明

利用scrapy抓取中新網新聞,關於自然災害滑坡的全部國內新聞;要求主題為滑坡類新聞,包含災害造成的經濟損失等相關內容,並結合textrank算法,得到每篇新聞的關鍵詞,便於后續文本挖掘分析。

網站分析

目標網站:http://sou.chinanews.com/advSearch.do

結合中新搜索平台的高級搜索的特點,搜索關鍵詞設置為:滑坡 經濟損失(以空格隔開),設置分類頻道為國內,排序方式按照相關度。得到所有檢索到的新聞如下:

共1000多條數據。

分析網站特點發現,給請求為異步加載,通過抓包工具Fiddler得到:

分析:

  POST提交,每次提交目標的url為:http://sou.chinanews.com/search.do

  提交參數如上所示,其中q表示關鍵詞(抓包測試時只輸入了一個關鍵詞);

  ps表示每次顯示的調試,adv=1表示高級搜索;day1,day2表示搜索時間,默認不寫表示全部時間,channel=gn表示國內;

  繼續點擊下一頁,通過對照得到一個新的參數:start,其中當start=0時,默認省略,表示第一頁,每次下一頁都增加10(設置的頁面顯示10)

   分析得到,每次點擊下一頁都是一個POST提交,參數相同,不同的是start

代碼邏輯

使用命令:scrapy start project NewsChina創建項目,編寫items.py文件,明確抓取字段(常用套路,第一步都是明確抓取字段)。這里只抓取標題和正文。常規操作:添加數據來源和抓取時間信息。

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class NewschinaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()

    # 數據來源
    source = scrapy.Field()
    # 抓取時間
    utc_time = scrapy.Field()
    # 新聞標題
    title = scrapy.Field()
    # 新聞內容
    content = scrapy.Field()
    # 關鍵詞
    keywords = scrapy.Field()

 

明確抓取字段后,使用命令:scrapy genspider newsChina 生成爬蟲,開始編寫爬蟲邏輯,結合抓取網站的特點,做以下操作:

針對該網站的反爬措施,添加請求延遲、重試次數等待配置;

通過修改POST請求的time_scope字段,得到每一頁數據,並解析數據中詳情頁的鏈接,然后對詳情頁鏈接請求,解析待抓取數據;

至於循環抓取和終止循環條件,結合實際網站各有不同,在代碼中已有說明。

# -*- coding: utf-8 -*-
import re
import scrapy
from NewsChina.items import NewschinaItem

class NewschinaSpider(scrapy.Spider):
    name = 'newsChina'
    # allowed_domains = ['sou.chinanews.com']
    # start_urls = ['http://http://sou.chinanews.com/']

    #爬蟲設置
    # handle_httpstatus_list = [403]  # 403錯誤時拋出異常
    custom_settings = {
        "DOWNLOAD_DELAY": 2,
        "RETRY_ENABLED": True,
    }

    page = 0
    # 提交參數
    formdata = {
        'field': 'content',
        'q': '滑坡 經濟損失',
        'ps': '10',
        'start': '{}'.format(page * 10),
        'adv': '1',
        'time_scope': '0',
        'day1': '',
        'day2': '',
        'channel': 'gn',
        'creator': '',
        'sort': '_score'
    }
    # 提交url
    url = 'http://sou.chinanews.com/search.do'

    def start_requests(self):

        yield scrapy.FormRequest(
            url=self.url,
            formdata=self.formdata,
            callback=self.parse
        )

    def parse(self, response):
        try:
            last_page = response.xpath('//div[@id="pagediv"]/span/text()').extract()[-1]
            # 匹配到尾頁退出迭代
            if last_page is '尾頁':
                return
        except:
            # 當匹配不到last_page時,說明已經爬取所有頁面,xpath匹配失敗
            # 拋出異常,這就是我們的循環終止條件
            # print("last_page:", response.url)
            return

        link_list = response.xpath('//div[@id="news_list"]/table//tr/td/ul/li/a/@href').extract()
        for link in link_list:
            if link:
                item = NewschinaItem()
                # 訪問詳情頁
                yield scrapy.Request(link, callback=self.parse_detail, meta={'item': item})

        # 循環調用,訪問下一頁
        self.page += 1

        # 下一頁的開始,修改該參數得到新數據
        self.formdata['start'] = '{}'.format(self.page * 10)
        yield scrapy.FormRequest(
            url=self.url,
            formdata=self.formdata,
            callback=self.parse
        )

    # 從詳情頁中解析數據
    def parse_detail(self, response):
        """
        分析發現,中新網年份不同,所以網頁的表現形式不同,
        由於抓取的是所有的數據,因此同一個xpath可能只能匹配到部分的內容;
        經過反復測試發現提取規則只有如下幾條。提取標題有兩套規則
        提取正文有6套規則。
        :param response: 
        :return: 
        """
        item = response.meta['item']

        # 提取標題信息
        if response.xpath('//h1/text()'):
            item['title'] = response.xpath('//h1/text()').extract_first().strip()
        elif response.xpath('//title/text()'):
            item['title'] = response.xpath('//title/text()').extract_first().strip()
        else:
            print('title:', response.url)

        # 提取正文信息
        try:
            if response.xpath('//div[@id="ad0"]'):
                item['content'] = response.xpath('//div[@id="ad0"]').xpath('string(.)').extract_first().strip()
            elif response.xpath('//div[@class="left_zw"]'):
                item['content'] = response.xpath('//div[@class="left_zw"]').xpath('string(.)').extract_first().strip()
            elif response.xpath('//font[@id="Zoom"]'):
                item['content'] = response.xpath('//font[@id="Zoom"]').xpath('string(.)').extract_first().strip()
            elif response.xpath('//div[@id="qb"]'):
                item['content'] = response.xpath('//div[@id="qb"]').xpath('string(.)').extract_first().strip()
            elif response.xpath('//div[@class="video_con1_text_top"]/p'):
                item['content'] = response.xpath('//div[@class="video_con1_text_top"]/p').xpath('string(.)').extract_first().strip()
            else:
                print('content:', response.url)
        except:
            # 測試發現中新網有一個網頁的鏈接是空的,因此提前不到正文,做異常處理
            print(response.url)
            item['content'] = ''

        yield item

 

編寫中間件,添加隨機頭信息:

# -*- coding: utf-8 -*-

# Define here the models for your spider middleware
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/spider-middleware.html
import random
from NewsChina.settings import USER_AGENTS as ua


class NewsChinaSpiderMiddleware(object):

    def process_request(self, request, spider):
        """
        給每一個請求隨機分配一個代理
        :param request:
        :param spider:
        :return:
        """
        user_agent = random.choice(ua)
        request.headers['User-Agent'] = user_agent

 

編寫數據保存邏輯:

結合python的jieba模塊的textrank算法,實現新聞的關鍵詞抽取,並保存到excel或數據庫中

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from datetime import datetime
from jieba import analyse
from openpyxl import Workbook
import pymysql

class KeyswordPipeline(object):
    """
    添加數據來源及抓取時間;
    結合textrank算法,抽取新聞中最重要的5個詞,作為關鍵詞
    """
    def process_item(self, item, spider):

        # 數據來源
        item['source'] = spider.name
        # 抓取時間
        item['utc_time'] = str(datetime.utcnow())

        content = item['content']
        keywords = ' '.join(analyse.textrank(content, topK=5))

        # 關鍵詞
        item['keywords'] = keywords

        return item

class NewsChinaExcelPipeline(object):
    """
    數據保存
    """
    def __init__(self):
        self.wb = Workbook()
        self.ws = self.wb.active
        self.ws.append(['標題', '關鍵詞', '正文', '數據來源', '抓取時間'])

    def process_item(self, item, spider):

        data = [item['title'], item['keywords'], item['content'], item['source'], item['utc_time']]

        self.ws.append(data)
        self.wb.save('./news.xls')

        return item

# class NewschinaPipeline(object):
#     def __init__(self):
#         self.conn = pymysql.connect(
#             host='.......',
#             port=3306,
#             database='news_China',
#             user='z',
#             password='136833',
#             charset='utf8'
#         )
#         # 實例一個游標
#         self.cursor = self.conn.cursor()
#
#     def process_item(self, item, spider):
#         sql = """
#               insert into ChinaNews(ID, 標題, 關鍵詞, 正文, 數據來源, 抓取時間)
#                values (%s, %s, %s, %s, %s, %s);"""
#
#         values = [
#             item['title'],
#             item['keywords'],
#             item['content'],
#
#             item['source'],
#             item['utc_time']
#         ]
#
#         self.cursor.execute(sql, values)
#         self.conn.commit()
#
#         return item
#
#     def close_spider(self, spider):
#         self.cursor.close()
#         self.conn.close()

運行結果

完成代碼

 參見:https://github.com/zInPython/NewsChina

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM