Scrapy开发指南

2016-12-31 17:17

一、Scrapy简介 

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy基于事件驱动网络框架 Twisted 编写。因此,Scrapy基于并发性考虑由非阻塞(即异步)的实现。
Scrapy开发指南0

组件

Scrapy Engine

引擎负责控制数据流。

调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders

Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。

Item Pipeline

Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。

下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

二、Scrapy环境配置

安装以下程序

  • Python 2.7
  • Python Package: pip and setuptools. 现在 pip 依赖 setuptools ,如果未安装,则会自动安装setuptools 。
  • lxml. 大多数Linux发行版自带了lxml。如果缺失,请查看http://lxml.de/installation.html
  • OpenSSL. 除了Windows(请查看 平台安装指南)之外的系统都已经提供。 
    pip install pyopenssl
  • Visual C++2008
  • 安装PyWin32
  • pip install scrapy
  • 创建工程模板

    命令行执行:scrapy startproject 工程名

    创建好的工程结构如下图:

      Scrapy开发指南1

    Idea中配置scrapy启动

    Scrapy开发指南2

     

    二、常用API说明

    开发中主要涉及spider,item(相当于java bean),Pipeline,settings模块的开发。需要扩展插件则开发extions模块。

    Spider类

    继承scrapy.Spider

    属性

    name : 爬虫名字,用于区别spider,唯一。
    start_urls : Spider在启动时进行爬取的url列表。后续的URL则从初始的URL获取到的数据中提取。

    方法
    parse(self, response) :每个初始url爬取到的数据将通过response参数传递过来。此方法负责解析数据(response), 提取数据(生成Item),生成需要进一步处理的URL请求(request)。

      scrapy.Request(url=link, errback=self.errback_http, callback=self.parse_article)
    

    框架会对url=link的地址发起请求,如果请求出现错误执行用户自定义的errback_http方法,如果请求成功则执行用户自定义的parse_article方法。

    提取Item
    需要继承scrapy.Item。

    Scrapy Selector基于xpath和css提取元素。

  • xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表 。
  • css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表.
  • extract(): 序列化该节点为unicode字符串并返回list。
  • re(): 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。
  • 运行爬虫
    在项目目录下运行以下命令,即可执行爬虫:
    scrapy crawl NAME(爬虫名字)

    元素容器

    自定义元素(Item)在items.py中定义,需要继承scrapy.Item,可以将元素理解成key-value的HashMap。
    Spider代码尽可能关注内容抽取,并保存在Item中,后续框架将Item交给管道处理,这种方式很好地对数据抽取和处理进行解耦。
    例如上述的 parse_article 方法, API要求返回一个元素或字典类型对象,在管道中可获取该对象并进行数据处理。

    配置文件

    配置信息都定义在settings中,可以把它理解成Java应用中的application.properties文件,数据库,缓存等信息都保存在这里。
    如果需要使用该配置文件中定义的属性,类(爬虫,管道,插件)需要增加额外的类方法: from_crawler(cls, crawler)
    框架在实例化类对象时候回调该方法,并传入crawler对象,后者可以获取到定义在settings中的属性值,例如:

     crawler.settings.get('URL_FILE')
    

    settings中的属性也可以通过在代码中引入from scrapy.utils.project import get_project_settings,显示的调用
    settings = get_project_settings()方法然后就可以使用settings中的属性值了。如下:
    conn = MySQLdb.connect(
    host=settings['MYSQL_HOST'],
    user=settings['MYSQL_USER'], 
    passwd=settings['MYSQL_PASSWD'], 
    db=settings['MYSQL_DBNAME'],
    use_unicode=True, 
    charset="utf8" 
    )

    管道开发

    管道类似过滤处理链,根据自定义业务依次处理Spider解析后的数据,例如数据验证(去重、转换),计算存储(DB,NOSQL),发送消息(Kafka,MQ),报表生成。
    开发自定义管道类需要两步骤:

    1. 在pipelines中定义类并实现 process_item(self, item, spider) 方法, 其中item对象为spider解析后待处理的数据。
    2. 在settings中开启管道配置信息,ITEM_PIPELINES 中配置自定义管道类名和执行序列。

    说明
    自定义管道根据序列号从小到大依次执行请求,如果抛出DropItem异常,后续管道将不会执行,例如数据出现重复主键,可以抛出DropItem异常。

    日志开发

    使用以下代码在管道中定义日志名称

    logger = logging.getLogger('pipelogger')
    

    同时可以在包初始化文件__init__.py中定义日志级别 : LOG_LEVEL = 'INFO'

    日志启用也可以在settings中设置如下属性
    LOG_ENABLED = True #启用日志
    LOG_ENCODING = 'utf-8' #设置日志字符集
    LOG_FILE = 'e://workspace/log/csdncrawl.log' #指定日志文件及路径
    LOG_LEVEL = 'INFO' #定义日志级别
    LOG_STDOUT = True #是否将print语句打印内容输出到日志

    扩展插件

    开发者可自定义运行在不同阶段的插件,例如打开爬虫、关闭爬虫、数据抓取等。
    插件只需要关注:在什么时候做什么事情,即 状态-方法。
    开发插件只需要2步:

    1. 开发插件类,可定义在extensions.py文件中,在from_crawler中增加状态-方法的映射关系,例如在打开爬虫的时候执行spider_opened方法可这样配置:
              crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
      
    1. 在settings中配置插件类,和管道定义类似, 其KEY为EXTENSIONS
     
    四、代码示例

    Spider示例

    import scrapy
    
    class DmozSpider(scrapy.Spider):
        name = "dmoz"
        allowed_domains = ["dmoz.org"]
        start_urls = [
            "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
            "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
        ]
    
        def parse(self, response):
            for sel in response.xpath('//ul/li'):
                title = sel.xpath('a/text()').extract()
                link = sel.xpath('a/@href').extract()
                desc = sel.xpath('text()').extract()
                print title, link, desc

     

    日志Logging

    import logginglogger = logging.getLogger('mycustomlogger')
    logger.warning("This is a warning")
    
    import logging
    import scrapy
    
    logger = logging.getLogger('mycustomlogger')
    
    class MySpider(scrapy.Spider):
    
     name = 'myspider'
     start_urls = ['http://scrapinghub.com']
    
     def parse(self, response):
     logger.info('Parse function called on %s', response.url)

     参考资料:

    中文版scrapy资料地址:https://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/tutorial.html