爬虫框架 Scrapy 轻松入门~

爬虫框架 Scrapy 轻松入门~

Scroll Down

在入坑爬虫的学习中,第一个接触的爬虫框架就是 Scrapy 。在使用了相比于其他的框架而言,如 PySpider、Crawley 等,个人觉得 Scrapy 在使用上更简洁、体系上更完善。

因为平时也是用业余的时间,写写比较简单的爬虫或者 github 上找一些比较有意思的项目研究一下之外,在主要的工作时间里也是比较少接触到这一块。所以有时隔了段时间再去看的话,多少都会有点陌生或者遗忘。

碰巧,最近因为公司的唯一爬虫同事走了,项目组中唯一比较熟悉 python 的我顺其而然就接这个盘了 -->_-->

于是决定再把文档重新撸一遍,做点笔记加深印象,也方便后面温习。

快速开始

  1. 创建工程

    scrapy startproject tutorial 创建一个 tutorial 的工程

    工程的目录如下:

    tutorial/
        scrapy.cfg            # deploy configuration file
    
        tutorial/             # project's Python module, you'll import your code from here
            __init__.py
    
            items.py          # project items definition file
    
            middlewares.py    # project middlewares file
    
            pipelines.py      # project pipelines file
    
            settings.py       # project settings file
    
            spiders/          # a directory where you'll later put your spiders
                __init__.py
    
  2. 编写spider

    tutorial/spiders目录下新建quotes_spider.py :

    import scrapy
    
    class QuotesSpider(scrapy.Spider):
        name = "quotes"
    
        def start_requests(self):
            urls = [
                'http://quotes.toscrape.com/page/1/',
                'http://quotes.toscrape.com/page/2/',
            ]
            for url in urls:
                yield scrapy.Request(url=url, callback=self.parse)
    
        def parse(self, response):
            page = response.url.split("/")[-2]
            filename = 'quotes-%s.html' % page
            with open(filename, 'wb') as f:
                f.write(response.body)
            self.log('Saved file %s' % filename)
    
    • name:spider 的唯一标识,每个 spider 都必须是唯一的
    • start_requests(): 爬取的地址
    • parse():爬取内容结果的解析
  3. 运行

    scrapy crawl quotes

    quotes 为上文所说的 name 标识

基础概念

Spider

功能:

定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。

爬取过程:

  1. 以初始的 URL 初始化 Request ,并设置回调函数。 当该 request 下载完毕并返回时,将生成 response,并作为参数传给该回调函数。

    spider 中初始的 request 是通过调用start_requests()来获取的。 start_requests()读取 start_urls 中的 URL, 并以 parse 为回调函数生成 Request 。

  2. 在回调函数内分析返回的(网页)内容,返回 Item 对象、dict、 Request 或者一个包括三者的可迭代容器。 返回的Request 对象之后会经过 Scrapy 处理,下载相应的内容,并调用设置的callback函数(函数可相同)。

  3. 在回调函数内,您可以使用 选择器(Selectors) (您也可以使用 BeautifulSoup , lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成 item。

  4. 最后,由 spider 返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。

常用属性:

  • name

    唯一标识

  • allowed_domains

    包含了spider允许爬取的域名(domain)列表(list)

  • custom_settings

    该设置是一个dict.当启动spider时,该设置将会覆盖项目级的设置. 由于设置必须在初始化(instantiation)前被更新,所以该属性 必须定义为class属性.

  • start_requests()

    该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取的第一个Request。

    当spider启动爬取并且未制定URL时,该方法被调用

  • parse(response)

    当response没有指定回调函数时,该方法是Scrapy处理下载的response的默认方法。

Selector

功能:

Selector(选择器)通过特定的 XPath 或者 CSS 表达式来“选择” HTML 文件中的某个部分。简单来说就是解析爬取的网页HTML数据。

使用:

>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'

解锁更多的使用方式和 API 可以去官网查阅

Items

功能:

为了从非结构性的数据源提取结构性数据,定义常用的输出数据

使用:

# 定义
import scrapy

class Product(scrapy.Item):
    name = scrapy.Field()
    price = scrapy.Field()
    stock = scrapy.Field()
    last_updated = scrapy.Field(serializer=str)
    
# 创建
product = Product(name='Desktop PC', price=1000)

# 获取
>>> product['name']
Desktop PC
>>> product.get('name')
Desktop PC

# 赋值
>>> product['last_updated'] = 'today'

Item Loaders

功能:

Item Loaders 提供了一种便捷的方式填充抓取到的:Items。虽然 Items 可以使用自带的类字典形式的 API 填充,但是 Item Loaders 提供了更便捷的 API,可以分析原始数据并对 Item 进行填充。

Items 提供保存抓取数据的容器,而 Item Loaders 提供的是填充容器的机制

Item Loaders 提供的是一种灵活,高效的机制,可以更方便的被 spider 或 source format (HTML,XML,etc)扩展,并 override 更易于维护的、不同的内容分析规则。

使用:

from scrapy.loader import ItemLoader
from myproject.items import Product

def parse(self, response):
    l = ItemLoader(item=Product(), response=response)
    l.add_xpath('name', '//div[@class="product_name"]')
    l.add_xpath('name', '//div[@class="product_title"]')
    l.add_xpath('price', '//p[@id="price"]')
    l.add_css('stock', 'p#stock]')
    l.add_value('last_updated', 'today') # you can also use literal values
    return l.load_item()

简单说,数据通过用 add_xpath() 的方法,把XPath 位置提取的数据收集起来。

最终,当所有数据被收集起来后,调用 ItemLoader.load_item() 方法,实际上填充并返回了之前通过调用 add_xpath(),add_css(),和 add_value() 所提取和收集到的数据的 Item。

Input and Output processors

功能:

Item Loader 在每个(Item)字段中都包含了一个输入处理器和一个输出处理器。输入处理器收到数据时立刻提取数据(通过 add_xpath(),add_css(),和 add_value() 方法)之后输入处理器的结果被收集起来并且保存在 ItemLoader 内。收集到所有数据后,调用 ItemLoader.load_item() 方法来填充,并且得到填充后的 Item 对象。这是当输出处理器被和之前收集到的数据(和用输入处理器处理的)被调用,输出处理器的结果是被分配到 Item 的最终值。

使用:

# 自定义ItemLoader
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose, Join

class ProductLoader(ItemLoader):

    default_output_processor = TakeFirst()

    name_in = MapCompose(unicode.title)
    name_out = Join()

    price_in = MapCompose(unicode.strip)

    # ...

Pipeline

功能:

  1. 清理HTML数据
  2. 验证解析到的数据(检查Item是否包含必要的字段)
  3. 检查是否是重复数据(如果重复就删除)
  4. 将解析到的数据存储到数据库中

使用:

  • process_item(item, spider)

    每一个 item 管道组件都会调用该方法,并且必须返回一个item对象实例或 raise DropItem 异常。被丢掉的 item 将不会在管道组件进行执行。此方法有两个参数,一个是 item ,即要处理的 Item 对象,另一个参数是 spider ,即爬虫。
    此外,我们也可以在类中实现以下方法

    Scrapy会先通过 getattr 判断我们是否自定义了 from_crawler ,有则调它来完成实例化,早于 __init__ 方法执行自己要的参数要去 settings.py 文件配置

  • open_spider(spider)

    当 spider 执行的时候将调用该方法

  • close_spider(spider)

    当 spider 关闭的时候将调用该方法

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

每个 pipeline 后面有一个数值,这个数组的范围是 0-1000,这个数值是这些在 pipeline 中定义的类的优先级,越小越优先。

实践项目

笔者写了一个简单的爬虫例子,用于爬取表情包的。