• 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

  • 相关阅读:
    jQuery获取option的一些常用方法
    111
    网站架构
    软件测试分类
    Python之import的用法
    解决:appium-doctor不是内部或外部命令
    mysql四种常见的数据库引擎
    mysql之数据类型
    彻底清除mysql(windows平台)
    Tkinter基于Combobox控件实现二级菜单联动
  • 原文地址:https://www.cnblogs.com/pythoner6833/p/9234983.html
Copyright © 2020-2023  润新知