本章将介绍 scrapy 框架里面的 spider 中间件,更多内容请参考: > 本章将介绍 Request 与 Response, 更多内容请参考: Python 学习指南
Scrapy 数据流
Scrapy 中的数据流由执行引擎控制,其过程如下:
函数会被调用到。
- process_spider_input()
函数会被调用到。
- process_spider_output()
Spider 中间件是介入到 Scrapy 中的 spider 处理机制的钩子框架,可以插入自定义功能来处理发送给 Spiders 的 response, 以及 spider 产生的 item 和 request。
要启用 Spider 中间件 (Spider Middlewares),可以将其加入到 SPIDER_MIDDLEWARES 设置中。该设置是一个字典,键为中间件的路径,值为中间件的顺序 (order)。
样例:
- SPIDER_MIDDLEWARES = {
- 'myproject.middlewares.CustomSpiderMiddleware': 543,
- }
SPIDER_MIDDLEWARES 设置会与 Scrapy 定义的
设置合并 (但不是覆盖),而后根据顺序(order) 进行排序,最后得到启用中间件的有序列表;第一个中间件是最靠近引擎的,最后一个中间就爱你是最靠近 spider 的。
- SPIDER_MIDDLEWARES_BASE
关于如何分配中间的顺序请查看
设置,而后根据您想要放置中间件的位置选择一个值。由于每个中间件执行不同的动作,您的中间件可能会依赖于之前 (或者之后) 执行的中间件,因此顺序是最重要的。
- SPIDER_MIDDLEWARES_BASE
如果您想禁止内置的 (在
中设置并默认启用的) 中间件,您必须在项目的 SPIDER_MIDDLEWARES 设置中定义该中间件,并将其赋值为 None,例如,如果您想要关闭 off-site 中间件:
- SPIDER_MIDDLEWARES_BASE
- SPIDER_MIDDLEWARES = {
- 'myproject.middlewares.CustomSpiderMiddleware': 543,
- 'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware': None,
- }
最后,请注意,有些中间件需要通过特定的设置来启用。更多内容请查看相关中间件文档。
编写中间件十分简单,每个中间件组件是一个定义了以下一个或多个方法的 Python 类:
- class scrapy.contrib.spidermiddleware.SpiderMiddleware
- process_spider_input(response, spider)
当 response 通过 spider 中间件时,该方法被调用,处理该 response。
- process_spider_input()
应该返回一个 None 或者抛出一个异常 (exception)。
方法来处理,当其抛出异常时则带调用
- process_spider_output()
。
- process_spider_exception()
参数: response(Response 对象) - 被处理的 response spider(Spider 对象) - 该 response 对应的 spider
- process_spider_out(response, result, spider)
当 Spider 处理 response 返回 result 时,该方法被调用。
必须返回包含 Request 或 Item 对象的可迭代对象 (iterable)。
- process_spider_output()
参数: response(Response 对象) - 生成该输出的 response result(包含 Reques 或 Item 对象的可迭代对象 (iterable)) - spider 返回的 result spider(Spider 对象) - 其结果被处理的 spider
- process_spider_exception(response, exception, spider)
当 spider 或 (其它 spider 中间件的)process_spider_input() 抛出异常时,该方法被调用
process_spider_exception() 必须要么返回 None,要么返回一个包含 Response 或 Item 对象的可迭代对象 (iterable)。
通过其返回 None,Scrapy 将继续处理该异常,调用中间件链中的其它中间件的
- process_spider_exception()
如果其返回一个可迭代对象,则中间件链的
方法被调用,其他的
- process_spider_output()
将不会被调用。
- process_spider_exception()
- response(Response对象) - 异常被抛出时被处理的response
- exception(Exception对象) - 被抛出的异常
- spider(Spider对象) - 抛出异常的spider
- process_start_requests(start_requests, spider)
该方法以 spider 启动的 request 为参数被调用,执行的过程类似于
,只不过其没有相关联的 response 并且必须返回 request(不是 item)。
- process_spider_output()
其接受一个可迭代的对象 (start_requests 参数) 且必须返回一个包含 Request 对象的可迭代对象。
- 当在您的spider中间件实现该方法时,您必须返回一个可迭代对象(类似于参数start_requests)且不要遍历所有的start_requests。该迭代器会很大(甚至是无限),进而导致内存溢出。Scrapy引擎再其具有能力处理start_requests时将会拉起request,因此start_requests迭代器会变得无限,而由其它参数来停止spider(例如时间限制或者item/page计数)。
- 参数:
- start_requests(b包含Request的可迭代对象) - start requests
- spider(Spider对象) - start request所属的spider
2. 中间件代码:
- ##file:MeizituSpider.py
- #-*- coding:utf-8 -*-
- import scrapy
- from scrapy.spiders import Request
- import logging
- import re
- from cnblogSpider.items import SaveGirlImageItem
- logger = logging.getLogger(__name__)
- class MeiziTuSpider(scrapy.Spider):
- name = "meizitu"
- allowed_domains = ['meizitu.com']
- user_header = {
- "Referer": "http://www.meizitu.com/tag/nvshen_460_1.html",
- "Upgrade-Insecure-Requests" : "1",
- "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0"
- }
- def start_requests(self):
- logging.debug("###### 妹子图Spider开始启动.....%s"%self)
- return [Request(url="http://www.meizitu.com/tag/nvshen_460_1.html", callback=self.parse, headers = self.user_header)]
- @staticmethod
- def __remove_html_tags(str):
- return re.sub(r'<[^>]+>', '', str)
- def parse(self, response):
- # print(response.body)
- for picdiv in response.CSS('div[class="pic"]'):
- image_urls = picdiv.css('a[target="_blank"] img::attr(src)').extract_first()
- image_split = image_urls.split("/")
- image_name = image_split[-3]+ image_split[-2]+ image_split[-1]
- yield SaveGirlImageItem({
- 'name' : MeiziTuSpider.__remove_html_tags(picdiv.css('a[target="_blank"] img::attr(alt)').extract()[0]),#获取这组相片的名称
- 'url' : picdiv.css('a[target="_blank"] img::attr(src)').extract_first(), #获取这组照片的链接
- 'image_urls' : [picdiv.css('a[target="_blank"] img::attr(src)').extract_first()],
- 'images' : image_name
- })
- next_page = response.xpath(u'//div[@class="navigation"]//li/a[contains(.,"下一页")]/@href').extract_first()
- if next_page is not None:
- requesturl = "http://www.meizitu.com" + next_page
- yield Request(requesturl, callback = self.parse, headers=self.user_header)
- ##file:middlewares.py
- import logging
- ###下面是妹子图案例的spider中间件
- logger = logging.getLogger(__name__)
- ##start_requests函数调用这个spider中间件
- class ModifyStartRequest(object):
- def process_start_requests(self, start_requests, spider):
- logging.info("#### 22222222 #####strat_requests %s, spider %s ####"%(start_requests, spider))
- last_request = []
- for one_request in start_requests:
- logging.info("#### one_request %s, spider %s ####"%(one_request, spider))
- last_request.append(one_request)
- logging.info("#### 2222222 ####last_request %s, spider %s ####"%(last_request, spider))
- return last_request
3. item 文件:
- #file:spiderMiddleware.py
- import logging
- logger = logging.getLogger(__name__)
- ###
- class SpiderInputMiddleware(object):
- def process_spider_input(self, response, spider):
- logging.info("#### 3333 response %s, spider %s ####"%(response, spider))
- return
- class SpiderOutputMiddleware(object):
- def process_spider_output(self, response, result, spider):
- logging.info("#### 4444 response %s, spider %s ####" %(response, spider))
- return result
4. settings 设置:
- #file:items.py
- class SaveGirlImageItem(scrapy.Item):
- name = scrapy.Field()
- url = scrapy.Field()
- image_urls = scrapy.Field()
- images = scrapy.Field()
- #file:settings.py
- LOG_LEVEL = "INFO"
- #禁用Cookie
- COOKIES_ENABLED = False
- #spider中间件
- SPIDER_MIDDLEWARES = {
- # 'cnblogSpider.middlewares.CnblogspiderSpiderMiddleware': 543,
- 'cnblogSpider.middlewares.ModifyStartRequest' : 643,
- 'cnblogSpider.spiderMiddleware.SpiderInputMiddleware' : 743,
- 'cnblogSpider.spiderMiddleware.SpiderOutputMiddleware': 843
- }
- #管道中间件
- ITEM_PIPELINES = {
- 'cnblogSpider.pipelines.MeizituPipelineJson' :10,
- 'scrapy.pipelines.images.ImagesPipeline' : 1
- }
- #使用图片管道文件下载图片
- IMAGES_STORE="/home/chenqi/python/python_code/python_Spider/chapter04/cnblogs/cnblogSpider/cnblogSpider/images"
- IMAGES_URLS_FIELD = "image_urls"
- IMAGES_RESULT_FIELD="images"
来源: http://www.jianshu.com/p/4d8862522fa7