共计 10313 个字符,预计需要花费 26 分钟才能阅读完成。
1 Item Pipeline
当 spider 爬取到 item 后,它被发送到我的项目管道(Item Pipeline),通过几个组件按程序进行解决。每一个 Item Pipeline 是一个实现了简略办法的 Python 类,它接管到一个 item 并对其执行一个操作,也要决定该 item 是否应该持续通过管道,或者被抛弃,不再进行解决。
Item Pipeline 典型的用处是:
1. 清理 HTML 数据
2. 验证爬取的数据(查看 items 是否蕴含某些字段)
3. 查看正本(并删除它们)
4. 将 item 数据存储在数据库中
1.1 编写本人的 Item Pipeline
每个 Item Pipeline 都是一个 Python 类,它必须实现以下办法:
process\_item(self, item, spider)
这个办法能够被每个 Item Pipeline 调用,process\_item()必须是: 返回一个字典类型数据、返回一个条目 (或任何子类) 对象,返回一个 Twisted Deferred 或者 DropItem 异样,抛弃的 item 不再由进一步的 Item Pipeline 解决。
参数含意:
item:Item 对象或字典,爬取的 item
spider:spider 对象,爬取了这个 item 的 spider
此外,他们还能够实现以下办法:
open\_spider(self, spider)
当 spider 关上时,函数就会被调用,spider 参数含意:被关上的 spider
close\_spider(self, spider)
当 spider 敞开是,函数会被调用
from\_crawler(cls, crawler)
如果存在,这个类办法被调用来从一个 Crawler 创立一个 spider 实例。它必须返回管道的一个新实例,Crawler 对象提供对所有的 scrapy 外围组件的拜访,比方设置和信号; 这是管道拜访它们并将其性能连贯到 scrapy 的一种形式。
1.2 Pipeline 示例
1.2.1 价格验证示例
from scrapy.exceptions import DropItem
class PricePipeline(object):
vat_factor = 1.15
def process_item(self, item, spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)
1.2.2 写入 json 文件
上面的 Pipeline 将所有通过的我的项目 (从所有的 spiders) 存储到一个 item.jl 文件中,其中每行以 JSON 格局序列化:
import json
class JsonWriterPipeline(object):
def open_spider(self, spider):
self.file = open('items.jl', 'w')
def close_spider(self, spider):
self.file.close()
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
1.2.3 写入 MongoDB
在本例中,咱们将应用 pymongo 将 items 写入 MongoDB。MongoDB 地址和数据库名称在 scrapy settings 中指定;MongoDB 汇合以 item 类命名。本例的次要目标是展现如何应用 from\_crawler()办法以及如何正确地清理资源。
import pymongo
class MongoPipeline(object):
collection_name = 'scrapy_items'
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db[self.collection_name].insert_one(dict(item))
return item
1.2.4 反复过滤器
用于查找反复 items 的筛选器,并删除已解决的 item, 假如咱们的 items 有一个惟一的 id,然而咱们的 spider 返回的是具备雷同 id 的多个 items:
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def __init__(self):
self.ids_seen = set()
def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item
1.2.5 激活 Item Pipeline
激活 Item Pipeline 必须要在 setting 中设置 ITEM\_PIPELINES,示例如下:
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
在这个设置中调配给类的整数值决定了它们运行的程序:item 从低到高执行,整数值范畴是 0 -1000。
2 Feed exports
执行 scrapy 时最常须要的个性之一就是可能正确地存储爬取出来的数据,scrapy 提供了这个性能,容许应用多种序列化格局来生成一个 Feed。
2.1 序列化格局
用于序列化 scrapy 的数据格式次要有以下几种类型:
- JSON
- JSON lines
- CSV
- XML
你也能够通过 setting 中的 FEED\_EXPORTERS 字段来扩大反对的格局。
JSON
FEED\_FORMAT: json
应用的类: JsonItemExporter
JSON lines
FEED\_FORMAT: jsonlines
应用的类: JsonLinesItemExporter
CSV
FEED\_FORMAT: csv
应用的类: CsvItemExporter
XML
FEED\_FORMAT: xml
应用的类: XmlItemExporter
Pickle
FEED\_FORMAT: pickle
应用的类: PickleItemExporter
Marshal
FEED\_FORMAT: marshal
应用的类: MarshalItemExporter
2.2 应用办法
进入我的项目目录,执行命令:
scrapy crawl tushu -o tushu.json
通过 - o 参数前面接要输入的格局即可。
3 下载和解决文件和图像
scrapy 提供了可重用的 item pipelines,用于下载与特定 item 相干的文件(例如,当你爬取了产品并想要在本地下载它们的图像时),这些 pipelines 共享一些性能和构造 (咱们将它们称为 media pipelines),然而通常要么应用 Files Pipeline 要么应用 Images Pipeline。
这两个 Pipeline 都实现了这些个性:
- 防止从新下载最近下载的媒体
- 指定存储介质的地位(文件系统目录等)
Image Pipeline 有一些额定的性能用于解决图像:
- 将所有下载的图像转换为通用格局 (JPG) 和模式(RGB)
- 生成缩略图
- 查看图像宽度 / 高度以确保它们满足最小约束条件
Pipeline 为正筹备下载的 media url 的保留了外部队列,将蕴含雷同媒体的 response 连贯到该队列,这样能够防止在多个 items 共享的状况下下载雷同的媒体。
3.1 应用 Files Pipeline
应用 Files Pipeline 典型的工作流程如下:
1. 在一个 spider 中,你将一个 item 提取并且将所需的 urls 放入 file\_urls 字段中;
2.item 将从 spider 返回并进入 item pipeline;
3. 当 item 达到 FilePipeline,在 file\_urls 字段中的 urls 会应用规范 scrapy 调度器和下载器下载(这意味着调度程序和下装程序中间件被重用),如果优先级更高,会在其余页面被抓取前解决。item 会在这个特定的 pipline 中放弃“locker”状态,晓得实现下载(或因为某些起因未实现下载)。
4. 当下载文件时,将应用后果填充另一个字段(files),这个字段将蕴含一个对于下载文件的信息的字典,例如下载门路、原始 url(来自 file\_urls 字段)和文件校验。文件字段列表中的 files 将保留原来的 file\_urls 字段的雷同程序,如果有下载失败的文件,谬误将会被记录,而 file 不会被记录到 files 字段中。
3.2 应用 Images Pipeline
Images Pipeline 的应用形式与 File Pipeline 形式相似,只是默认的字段名称不同,应用 image\_urls 作为一个 item 的图片 urls,它将填充一个图像 image 字段,以获取对于下载的图像的信息。
应用 ImagesPipeline 对于解决 image files 的长处是,您能够配置一些额定的性能,比方生成缩略图和依据它们的大小过滤图像。
Images Pipeline 程序应用 Pillow 模块格式化图片为 JPEG/RGB 格局,所以你还须要装置 Pillow 模块,大多数状况下咱们应用 PIL,但家喻户晓,在某些状况下会引起麻烦,所以咱们倡议用 Pillow。
3.3 应用 Media Pipeline
如果要应用 Media Pipeline 你必须要在我的项目的 setting 中减少 ITEM\_PIPELINES 设置,对于 Images Pipeline,应用:
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}
Files Pipeline,应用:
ITEM_PIPELINES = {'scrapy.pipelines.files.FilesPipeline': 1}
留神:Images Pipeline 和 Files Pipeline 能够同时应用。
而后,将指标存储设置配置为一个有效值,该值将用于存储下载的图像。否则即便你配置了 ITEM\_PIPELINES, 也是被禁用的。
如果是 File Pipeline,在 setting 中减少 FILES\_STORE:
FILES_STORE = '/path/to/valid/dir'
如果是 Image Pipeline,在 setting 中减少 IMAGES\_STORE:
IMAGES_STORE = '/path/to/valid/dir'
3.4 反对的存储
目前官网惟一反对的是文件系统,然而也反对相似的 Amazon S3 and 和 Google Cloud Storage.
3.5 示例
1. 首先应用 media pipline 首先要启用它,在 setting 中配置:
ITEM_PIPELINES = {'scrapy.pipelines.images.ImagesPipeline': 1}
2. 而后设置字段 images 和 image\_urls:
import scrapy
class MyItem(scrapy.Item):
# ... other item fields ...
image_urls = scrapy.Field()
images = scrapy.Field()
3. 在 setting 中增加下载门路和字段:
# 图片下载存储门路
ITEM_STORE = 'E:\\'
为了防止下载最近下载的文件,能够设置 FILES\_EXPIRES 或 IMAGES\_EXPIRES 来配置缓存工夫:
# 120 天后过期
FILES_EXPIRES = 120
# 30 天后过期
IMAGES_EXPIRES = 30
Images Pipline 能够主动创立下载图像的缩略图,在 setting 中减少 IMAGES\_THUMBS 参数, 参数为一个字典,其中的键是缩略图名称,而值是它们的维数:
IMAGES_THUMBS = {'small': (50, 50),
'big': (270, 270),
}
如果想过滤掉小图片,通过设置 IMAGES\_MIN\_HEIGHT 和 IMAGES\_MIN\_WIDTH 来指定图像大小:
IMAGES_MIN_HEIGHT = 110
IMAGES_MIN_WIDTH = 110
这个值的配置不会影响缩略图的生成。
通过下面的配置咱们就能够为咱们的爬虫增加下载图片性能了。
4 小爬虫
下面说了那么多,大家可能感觉曾经一头雾水了,接下来咱们就用一个小我的项目来具体阐明一下,咱们要爬取的网站是(搜房网二手房页面中的各个房源图片)如下图:
获取网页列表中,每个条目标具体页中的图片。
4.1 启用 pipeline
setting.py 中减少如下内容:
# 新增内容从这里开始 #################################################################
# 启动 pipline
ITEM_PIPELINES = {
# 留神,如果自定义图片名称时,此条内容要正文,不然自定义图片名不失效
'scrapy.pipelines.images.ImagesPipeline': 1,
# 自定义图片名称后,能够勾销正文此条
# 'sp.pipelines.SpDownimagePipeline': 200,
}
# 图片保留地址
IMAGES_STORE = 'E:\\'
# 图片过期工夫 30 天
IMAGES_EXPIRES = 30
# 设置缩略图
# IMAGES_THUMBS = {# 'small': (50, 50),
# 'big': (270, 270),
# }
# 过滤小图片
# IMAGES_MIN_HEIGHT = 110
# IMAGES_MIN_WIDTH = 110
# 容许重定向
MEDIA_ALLOW_REDIRECTS = True
# 管制工夫,下载等待时间 3 秒
DOWNLOAD_DELAY = 3
# 申请 user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0'
# 新增内容从这里完结 #################################################################
4.2 配置 items
设置要爬取的网页名字段 image 和爬取网页内的图片链接字段 image\_urls,items.py 代码如下:
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class SpItem(scrapy.Item):
"""定义 item 字段"""
# 网页名称
image = scrapy.Field()
# 网页内图片链接
image_urls = scrapy.Field()
4.3 spider
咱们的爬虫 ftx.py 代码如下:
# -*- coding: utf-8 -*-
import scrapy
from sp.items import SpItem
class MyBlog(scrapy.Spider):
name = 'ftx'
start_urls = ['http://esf.fang.com']
def parse(self, response):
"""
爬取初始 start_urls 列表我的项目的 url(相对路径),通过 response.follow 生成
request,作为参数传入回掉函数 parse_item
"""
# 获取首页所有二手房的链接地址(绝对地址)page_urls = response.css("p.title a::attr(href)").extract()
for page_url in page_urls:
# 绝对地址应用 response.follow 形式拼接 url
request = response.follow(page_url, callback=self.parse_item)
# 如果获取的连贯是相对地址,用上面这条办法
# request = scrapy.Request(page_url, callback=self.parse_item)
yield request
def parse_item(self, response):
"""
解决 item 函数
:param response: 申请的网页内容
:return: item
"""
# 导入 item 类
item = SpItem()
# 每个集体页面中的图片
# image 是每个具体页的题目,image_urls 是每个具体页内图片的 url
item['image'] = response.css("div.floatl::text").extract_first().strip()
item['image_urls'] = response.css("img.loadimg::attr(data-src)").extract()
yield item
4.4 自定义 image pipeline
间接上代码 pipelines.py:
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
import scrapy
from scrapy.pipelines.images import ImagesPipeline
class SpDownimagePipeline(ImagesPipeline):
"""自定义图片下载类"""
def get_media_requests(self, item, info):
"""
ImagesPipeline 类的办法, 必须返回每个图像 URL 的 Request
:param item: 获取的 item
"""
# 从 item 中获取图片 url 并发送申请,image_urls 就是 items.py 中定义的字段
for image_url in item['image_urls']:
# meta 作用就是能够将 item 的值传给下一个函数应用,相似于先缓存起来
yield scrapy.Request(image_url, meta={'item': item})
def item_completed(self, results, item, info):
"""此处没有做批改,只是把 ImagesPipeline 的办法拿过去用,必须返回 item"""
if isinstance(item, dict) or self.images_result_field in item.fields:
item[self.images_result_field] = [x for ok, x in results if ok]
return item
def file_path(self, request, response=None, info=None):
"""
file_path 为 ImagePipeline 自带的办法,这里咱们重写这个办法,为了可能自定义图片的名称,如果不重写,SHA1 hash 格局,相似 full/63bbfea82b8880ed33cdb762aa11fab722a90a24.jpg
"""
# 获取 item,从 get_media_requests 的 Request 中获取
item = request.meta['item']
# 图片名称,一版用 split(‘/’)宰割后取最初一个值也就是 -1,这里没用 - 1 是因为图片最初一个字段不是随机数
# 是长乘以宽如:452x340c.jpg,容易重名,所以用的 -2,倒数第二个字段
image_guid = request.url.split('/')[-2] + '.jpg'
# 全名,包含门路和图片名
fullname = "full/%s/%s" % (item['image'], image_guid)
return fullname
对于 ImagesPipeline 的两个办法 get\_media\_requests 和 item\_completed 这里解释一下:
get\_media\_requests(item, info)
pipeline 会获取 image 的 urls 从 item 下载它,因而咱们能够重写 get\_media\_requests 办法并且返回每一个 url 的 request:
def get_media_requests(self, item, info):
for file_url in item['file_urls']:
yield scrapy.Request(file_url)
这些申请将由 pipeline 解决, 当实现下载时后果将会以 2 - 元素的元组模式被发送到 item\_completed 办法,每个元组将蕴含(success,file\_info\_or\_error)相似上面这种模式:
[(True,
{'checksum': '2b00042f7481c7b056c4b410d28f33cf',
'path': 'full/0a79c461a4062ac383dc4fade7bc09f1384a3910.jpg',
'url': 'http://www.example.com/files/product1.pdf'}),
(False,
Failure(...))]
success:布尔值,如果下载图片胜利,返回 True, 如果下载图片失败,返回 False。file\_info\_or\_error:返回的是一个字典,其中包含,url、path 和 checksum, 如果呈现问题返回 Twisted Failure。
- url 代表文件从哪里下载的,这是从 get\_media\_requests 返回的 request 的 url
- path 代表文件存储门路
- checksum 代表图像内容的 MD5 hash
item\_completed(results, item, info)
当一个独自我的项目中所有图片申请实现时(下载实现或者下载失败),此办法将会被调用,其中 results 参数为 get\_media\_requests 下载实现后返回的后果,item\_completed 必须返回输入发送到下一个阶段的 pipeline。所以你必须返回或删除 item,和之前其它 pipeline 操作一样。
上面的一个示例,咱们将下载的文件门路 (在 results 中传递) 存储在 file\_path item 字段中,如果不蕴含任何文件,则删除该我的项目。
from scrapy.exceptions import DropItem
def item_completed(self, results, item, info):
file_paths = [x['path'] for ok, x in results if ok]
if not file_paths:
raise DropItem("Item contains no files")
item['file_paths'] = file_paths
return item
上面是一个残缺的自定义 Image pipeline 示例:
import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
class MyImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url)
def item_completed(self, results, item, info):
image_paths = [x['path'] for ok, x in results if ok]
if not image_paths:
raise DropItem("Item contains no images")
item['image_paths'] = image_paths
return item