SpiderMiddleWare的定义
spiderMiddleware 是一个Scrapy的spider处理机制的钩子框架,你可以插入自定义的功能用来处理engine发往spider的response和spider发往engine的request和item
激活SpiderMiddleware
想要激活SpiderMiddleware,需要在settings里配置SPIDER_MIDDLEWARES,这是一个字典dict,key:value对应的是 类路径:顺序,顺序由大到小,越小的越靠近spider,由大的越靠近engine,也就是说,由spider到engine是升序调用,从engine到spider为降序调用
SPIDER_MIDDLEWARES = { 'myproject.middlewares.CustomSpiderMiddleware': 543, #如果想禁用一个内置的spider,需要将其顺序设置为None 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,}复制代码
定义自己的spider
class scrapy.spidermiddlewares.SpiderMiddleware: """ 这个方法在将response发往spider的过程中被调用 这个方法应该返回None或者raise一个异常 * response:正在响应的处理 * spider:该响应的目标spider """ def process_spider_input(response, spider): pass """ 这个方法将在requset或者item发往engine的过程中被调用 这个方法必须返回一个reponse,dict,item * response:产生该响应对应的response * result:(一个request,dict,item)由这个spider返回的结果 * spider:该响应的目标spider """ def process_spider_output(response, result, spider): pass """ 这个方法将在一个spider或者一个process_spider_input方法抛出异常的时候被调用 这个方法应该返回None或者一个reponse,dict,item * response :当前的相应 * exception:抛出的异常 * spider:当前的爬虫 """ process_spider_exception(response, exception, spider): pass """ 这个方法用来处理首次发往engine的请求,和process_spider_output唯一不同的地方是,不接受response,并且只能返回一个request """ process_start_requests(start_requests, spider): pass """ 如果这个方法存在,将用来创建一个middleware实例,并且应该将改实例返回,这个方法接收一个crawler对象,改对象提供了spider的所有核心配置 这提供了一种方法让middleware可以访问其配置并hook住其他的scrapy组件 """ @classmethod from_crawler(cls, crawler): pass复制代码
内置的SpiderMiddleware
scrapy内置的SpiderMiddleware 都在scrapy.spiderMiddlewares下面
看一下HttpErrorMiddleware的源码
class HttpErrorMiddleware(object): @classmethod def from_crawler(cls, crawler): """ 这个方法将settings传给构造函数,并返回了创建的实例 """ return cls(crawler.settings) def __init__(self, settings): """ 构造函数 """ # 获取HTTPERROR_ALLOW_ALL配置,改配置是一个布尔类型,声明是否需要spider自己处理所有的http相应状态码 self.handle_httpstatus_all = settings.getbool('HTTPERROR_ALLOW_ALL') # 获取HTTPERROR_ALLOWED_CODES配合,该配置是一个list,声明spider需要自己处理的http响应状态码 self.handle_httpstatus_list = settings.getlist('HTTPERROR_ALLOWED_CODES') def process_spider_input(self, response, spider): """ 处理发往spider的相应 """ # 如果是200-300之间的状态码,之间返回由spider处理 if 200 <= response.status < 300: # common case return # 获取元信息 meta = response.meta # 如果在元信息里包含handle_httpstatus_all这个参数, # 则说明当前请求相应的所有状态码都需要自己处理,直接返回 if 'handle_httpstatus_all' in meta: return # 同上,只不过这里变成了list if 'handle_httpstatus_list' in meta: allowed_statuses = meta['handle_httpstatus_list'] # 如果配置里声明了需要处理所有的请求,则直接返回 elif self.handle_httpstatus_all: return else: # 如果上面条件全都不满足,则获取当前spider的handle_httpstatus_list属性 allowed_statuses = getattr(spider, 'handle_httpstatus_list', self.handle_httpstatus_list) # 如果当前的status在运行自己处理的status列表里,则直接返回 if response.status in allowed_statuses: return # 如果上面都没有满足,则说明当前status需要由scrapy框架处理,所以抛出一个异常 raise HttpError(response, 'Ignoring non-200 response') def process_spider_exception(self, response, exception, spider): """ 处理HttpError的异常 """ # 只处理HttpError的异常 if isinstance(exception, HttpError): # 当前异常状态码出现次数加1 spider.crawler.stats.inc_value('httperror/response_ignored_count') spider.crawler.stats.inc_value( 'httperror/response_ignored_status_count/%s' % response.status ) 打印日志 logger.info( "Ignoring response %(response)r: HTTP status code is not handled or not allowed", { 'response': response}, extra={ 'spider': spider}, ) return []复制代码