Request对象

Request(url[, callback, method='GET', headers, body, cookies, meta,
                       encoding='utf-8', priority=0, dont_filter=False, errback])

Response对象用来描述一个HTTP响应,Response只是一个基类,根据响应内容的不同有如下子类:

● TextResponse

● HtmlResponse

● XmlResponse

下面介绍HtmlResponse对象的属性及方法。

● url

HTTP响应的url地址,str类型。

● status

HTTP响应的状态码,int类型,例如200,404。

● headers

HTTP响应的头头部,类字典类型,可以调用get或getlist方法对其进行访问,例如:

response.headers.get('Content-Type')
response.headers.getlist('Set-Cookie')

● body

HTTP响应正文,bytes类型。

● text

文本形式的 HTTP 响应正文,str 类型,它是由 response.body 使用response.encoding 解码得到的,即

reponse.text = response.body.decode(response.encoding)

● encoding

HTTP响应正文的编码,它的值可能是从HTTP响应头部或正文中解析出来的。

● request

产生该HTTP响应的Request对象。

● meta

即response.request.meta,在构造Request对象时,可将要传递给响应处理函数的信息通过meta参数传入;响应处理函数处理响应时,通过response.meta将信息取出。

● selector

Selector对象用于在Response中提取数据(选择器相关话题在后面章节详细讲解)。

● xpath(query)

使用XPath选择器在Response中提取数据,实际上它是response.selector.xpath方法的快捷方式(选择器相关话题在后面章节详细讲解)。

● css(query)

使用CSS选择器在Response中提取数据,实际上它是response.selector.css方法的快捷方式(选择器相关话题在后面章节详细讲解)。

● urljoin(url)

用于构造绝对url。当传入的url参数是一个相对地址时,根据response.url计算出相应的绝对url。例如,response.url为http://www.example.com/a,url为b/index.html,调用response.urljoin(url)的结果为http://www.example.com/a/b/index.html。

虽然 HtmlResponse 对象有很多属性,但最常用的是以下的3个方法:

● xpath(query)

● css(query)

● urljoin(url)

前两个方法用于提取数据,后一个方法用于构造绝对 url 。

提取数据

调用 Selector 或 SelectorLis 对象的以下方法可将选中的内容提取:

● extract()

● re()

● extract_first() ( SelectorList 专有)

● re_first() ( SelectorList 专有)

Item 和 Field

● Item基类

自定义数据类(如BookItem)的基类。

● Field类

用来描述自定义数据类包含哪些字段(如 name、price 等)。

Item 支持字典接口,因此 BookItem 在使用上和Python字典类似,可按以下方式创建 BookItem 对象:

>>> from scrapy import Item, Field
>>>> class BookItem(Item):
...    name = Field()
...    price = Field()
>>> book1 = BookItem(name='Needful Things', price=45.0)
>>> book1
{'name': 'Needful Things', 'price': 45.0}
>>> book2 = BookItem()
>>> book2
{}
>>> book2['name'] = 'Life of Pi'
>>> book2['price'] = 32.5
{'name': 'Life of Pi', 'price': 32.5}

扩展 item

>>> class ForeignBookItem(BookItem):
...   translator = Field()
...
>>> book = ForeignBookItem()
>>> book['name'] = '巴黎圣母院'
>>> book['price'] = 20.0
>>> book['translator'] = '陈敬容

Field 元数据

class ExampleItem(Item):
        x = Field(a='hello', b=[1, 2, 3])       # x 有两个元数据,a是个字符串,b是个列表
        y = Field(a=lambda x: x ** 2)           # y 有一个元数据,a是个函数

可以通过 authors 字段的元数据告诉 CsvItemExporter 如何对 authors 字段串行化:

class BookItem(Item):
	...
	authors = Field(serializer=lambda x: '|'.join(x))
	...

元数据的键 serializer 是 CsvItemExporter 规定好的,它会用该键获取元数据,即一个串行化函数对象,并使用这个串行化函数将 authors 字段串行化成一个字符串。

使用 Item Pipeline 处理数据

Pipeline 不需要继承特定基类,只需要实现某些特定方法,例如process_item、open_spider、close_spider。

一个 Item Pipeline 必须实现一个 process_item(item,spider) 方法,该方法用来处理每一项由Spider爬取到的数据,其中的两个参数:

  • Item 爬取到的一项数据( Item 或字典)。
  • Spider 爬取此项数据的 Spider 对象。

● 如果process_item在处理某项item时返回了一项数据(Item或字典),返回的数据会递送给下一级Item Pipeline(如果有)继续处理。

● 如果 process_item 在处理某项item时抛出(raise)一个 DropItem 异常(scrapy.exceptions.DropItem),该项 item 便会被抛弃,不再递送给后面的 Item Pipeline 继续处理,也不会导出到文件。通常,我们在检测到无效数据或想要过滤数据时,抛出 DropItem 异常。

除了必须实现的process_item方法外,还有3个比较常用的方法,可根据需求选择实现:

● open_spider(self, spider)

Spider 打开时(处理数据前)回调该方法,通常该方法用于在开始处理数据之前完成某些初始化工作,如连接数据库。

● close_spider(self, spider)

Spider 关闭时(处理数据后)回调该方法,通常该方法用于在处理完所有数据之后完成某些清理工作,如关闭数据库。

● from_crawler(cls, crawler)

创建 Item Pipeline 对象时回调该类方法。通常,在该方法中通过 crawler.settings 读取配置,根据配置创建 Item Pipeline 对象。

创建好 Item Pipeline 后,在 settings.py 中开启。

过滤重复数据例子
from scrapy.exceptions import DropItem


     class DuplicatesPipeline(object):


        def __init__(self):
        	self.book_set = set()


     	def process_item(self, item, spider):
        	name = item['name']
        	if name in self.book_set:
            	raise DropItem("Duplicate book found: %s" % item)


        	self.book_set.add(name)
        	return item
写入 MongoDB 的例子
from scrapy.item import Item
     import pymongo


     class MongoDBPipeline(object):


        @classmethod
        def from_crawler(cls, crawler):
           # 有默认值
           cls.DB_URI = crawler.settings.get('MONGO_DB_URI',
                                        'mongodb://localhost:27017/')
           cls.DB_NAME = crawler.settings.get('MONGO_DB_NAME', 'scrapy_data')


           return cls()


        def open_spider(self, spider):
           self.client = pymongo.MongoClient(self.DB_URI)
           self.db = self.client[self.DB_NAME]
     def close_spider(self, spider):
        self.client.close()


     def process_item(self, item, spider):
        collection = self.db[spider.name]
        post = dict(item) if isinstance(item, Item) else item
        collection.insert_one(post)


        return item

● 增加类方法 from_crawler(cls, crawler),替代在类属性中定义 DB_URI 和 DB_NAME。

● 如果一个 Item Pipeline 定义了 from_crawler 方法,Scrapy 就会调用该方法来创建Item Pipeline对象。该方法有两个参数:

cls Item Pipeline 类的对象(这里为 MongoDBPipeline 类对象)。

crawler Crawler 是 Scrapy 中的一个核心对象,可以通过 crawler 的 settings 属性访问配置文件。

● 在 from_crawler 方法中,读取配置文件中的 MONGO_DB_URI 和 MONGO_DB_NAME (不存在使用默认值),赋给 cls 的属性,即 MongoDBPipeline 类属性。

● 其他代码并没有任何改变,因为这里只是改变了设置 MongoDBPipeline 类属性的方式。

settings.py

MONGO_DB_URI = 'mongodb://192.168.1.105:27017/'
MONGO_DB_NAME = 'liushuo_scrapy_data'

使用Exporter导出数据

在 Scrapy 中,负责导出数据的组件被称为 Exporter(导出器),Scrapy 内部实现了多个 Exporter ,每个 Exporter 实现一种数据格式的导出,支持的数据格式如下(括号中为相应的 Exporter ):

(1)JSON (JsonItemExporter)

(2)JSON lines (JsonLinesItemExporter)

(3)CSV (CsvItemExporter)

(4)XML (XmlItemExporter)

(5)Pickle (PickleItemExporter)

(6)Marshal (MarshalItemExporter)

其中,前4种是极为常用的文本数据格式,而后两种是 Python 特有的。在大多数情况下,使用 Scrapy 内部提供的 Exporter 就足够了,需要以其他数据格式(上述6种以外)导出数据时,可以自行实现 Exporter 。

● FEED_URI

导出文件路径。

FEED_URI = 'export_data/%(name)s.data'

● FEED_FORMAT

导出数据格式。

FEED_FORMAT = 'csv'

● FEED_EXPORT_ENCODING

导出文件编码(默认情况下json文件使用数字编码,其他使用utf-8编码)。

FEED_EXPORT_ENCODING = 'gbk'

● FEED_EXPORT_FIELDS

导出数据包含的字段(默认情况下导出所有字段),并指定次序。

FEED_EXPORT_FIELDS = ['name', 'author', 'price']

● FEED_EXPORTERS

用户自定义 Exporter 字典,添加新的导出数据格式时使用。

FEED_EXPORTERS = {'excel': 'my_project.my_exporters.ExcelItemExporter'}

FilesPipeline 和 ImagesPipeline3

FilesPipeline 的使用
<html>
        <body>
           ...
           <a href='/book/sg.pdf'>下载《三国演义》</a>
           <a href='/book/shz.pdf'>下载《水浒传》</a>
           <a href='/book/hlm.pdf'>下载《红楼梦》</a>
           <a href='/book/xyj.pdf'>下载《西游记》</a>
           ...
        </body>
     </html>

步骤01 在配置文件 settings.py 中启用 FilesPipeline ,通常将其置于其他 Item Pipeline 之前:

ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}

步骤02 在配置文件 settings.py 中,使用 FILES_STORE 指定文件下载目录,如:

FILES_STORE = '/home/liushuo/Download/scrapy'

步骤03 在 Spider 解析一个包含文件下载链接的页面时,将所有需要下载文件的 url 地址收集到一个列表,赋给 item 的 file_urls 字段(item[‘file_urls’])。

class ExampleItem(scrapy.Item):
        file_urls = scrapy.Field()
        files = scrapy.Field()

FilesPipeline 在处理每一项 item 时,会读取 item[‘file_urls’] ,对其中每一个 url 进行下载,Spider 示例代码如下:

class DownloadBookSpider(scrapy.Spider):
        ...
        def parse(response):
            item = {}
            # 下载列表
            item['file_urls'] = []
     for url in response.xpath('//a/@href').extract():
        download_url = response.urljoin(url)
        # 将url 填入下载列表
        item['file_urls'].append(download_url)
     yield item

当 FilesPipeline 下载完 item[‘file_urls’] 中的所有文件后,会将各文件的下载结果信息收集到另一个列表,赋给 item 的 files 字段 (item[‘files’])。下载结果信息包括以下内容:

● Path 文件下载到本地的路径(相对于 FILES_STORE 的相对路径)。

● Checksum 文件的校验和。

● url 文件的 url 地址。

ImagesPipeline 的使用说明

java response主要看哪个属性 response对应的类是_HTTP


ImagesPipeline 在 FilesPipleline 的基础上针对图片增加了一些特有的功能:

● 为图片生成缩略图

开启该功能,只需在配置文件 settings.py 中设置IMAGES_THUMBS,它是一个字典,每一项的值是缩略图的尺寸,代码如下:

IMAGES_THUMBS = {
    'small': (50, 50),
    'big': (270, 270),
 }

开启该功能后,下载一张图片时,本地会出现3张图片(1张原图片,2张缩略图),路径如下:

[IMAGES_STORE]/full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
 [IMAGES_STORE]/thumbs/small/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
 [IMAGES_STORE]/thumbs/big/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg

● 过滤掉尺寸过小的图片

开启该功能,需在配置文件 settings.py 中设置IMAGES_MIN_WIDTH 和 IMAGES_MIN_HEIGHT ,它们分别指定图片最小的宽和高,代码如下:

IMAGES_MIN_WIDTH = 110
 IMAGES_MIN_HEIGHT = 110

开启该功能后,如果下载了一张105×200的图片,该图片就会被抛弃掉,因为它的宽度不符合标准。