爬虫中间件
- 特点:主要处理蜘蛛(
Spider
)和下载器(Downloader
)之间的请求和响应。可以对蜘蛛生成的请求进行拦截、修改或过滤,也可以对下载器返回给蜘蛛的响应进行处理。 - 适用场景:
- 请求过滤与修改:当需要根据蜘蛛的某些条件对生成的请求进行过滤或修改时,例如根据蜘蛛的状态、爬取深度等决定是否发送某个请求,或者修改请求的参数、URL 等。
- 响应处理:对下载器返回的响应进行统一的预处理,比如检查响应的状态码,根据不同的状态码进行不同的处理;或者对响应内容进行初步的清洗、解析,提取一些公共信息供蜘蛛后续使用。
- 实现分布式爬虫:在分布式爬虫场景中,用于协调不同节点之间的爬取任务,例如根据节点的负载情况分配请求,或者对分布式环境下的请求和响应进行特殊处理,确保爬取任务的高效执行。
下载中间件
- 特点:主要作用于下载器层面,用于处理
HTTP
请求和响应。可以在请求发送到服务器之前对请求进行修改,如添加请求头、代理设置等;也可以在响应返回后对响应进行处理,如解压、解码等操作。 - 适用场景:
- 请求头设置:像前面提到的随机设置
User-Agent
,以及添加其他自定义的请求头信息,以伪装请求来源或满足网站的特定要求。 - 代理设置:当需要使用代理服务器来发送请求时,下载中间件是设置代理的合适位置。可以根据不同的条件动态选择代理服务器,或者对代理的使用进行管理和监控,如处理代理的认证、检测代理的可用性等。
- 响应处理:对响应进行一些与数据传输和格式相关的处理,例如对压缩的响应进行解压(如处理gzip压缩的响应),对编码后的响应进行解码,确保蜘蛛接收到的是正确格式的响应内容。
- 请求头设置:像前面提到的随机设置
在实际的爬虫项目中,通常会同时使用爬虫中间件和下载中间件。下载中间件用于处理与 HTTP
请求和响应相关的底层操作,而爬虫中间件则更侧重于处理与蜘蛛逻辑相关的请求和响应,两者结合可以满足复杂的爬虫需求。例如,在一个爬取电商网站的项目中,可能会使用下载中间件来设置代理和随机User-Agent
,以避免被网站封禁;同时使用爬虫中间件来根据商品的分类过滤请求,只爬取特定类别的商品信息,并对响应中的通用信息进行提取和处理。
DOWNLOADER_MIDDLEWARES = {# 其他中间件...'your_project_name.middlewares.ProxyMiddleware': 543,
}
543
:这是中间件的优先级,数值越小,中间件越先被执行。
代理中间件
import random
import base64class ProxyMiddleware:def __init__(self):self.proxy_list = ['http://proxy1.example.com:8080','http://proxy2.example.com:8080',]self.username = 'your_username'self.password = 'your_password'def process_request(self, request, spider):proxy = random.choice(self.proxy_list)request.meta['proxy'] = proxy# 添加代理认证信息if self.username and self.password:auth = f'{self.username}:{self.password}'auth_encoded = base64.b64encode(auth.encode('utf-8')).decode('utf-8')request.headers['Proxy-Authorization'] = f'Basic {auth_encoded}'return Nonedef process_exception(self, request, exception, spider):# 处理代理失败的情况proxy = request.meta.get('proxy')if proxy in self.proxy_list:self.proxy_list.remove(proxy)return request
在 Scrapy
的下载器中间件中,process_request
方法的返回值有特定的含义:
return None
:这是最常见的返回值。当返回None
时,Scrapy 会继续处理这个请求,也就是会将请求发送给下一个中间件(如果有的话),最终由下载器去执行请求操作。return Response
:若返回一个Response
对象,Scrapy 会停止处理这个请求的后续中间件,并且直接将这个Response
对象作为请求的响应返回给蜘蛛(Spider)进行处理。return Request
:要是返回一个新的Request
对象,Scrapy 会停止处理当前请求,然后将新的Request对象重新加入到请求队列中,等待后续处理。
UserAgent中间件
class UserAgentMiddleware:def process_request(self, request, spider):user_agant = random.choice(USER_AGENT_LIST)# 添加请求头request.headers['User-Agent'] = user_agantreturn None
Selenium中间件
from DrissionPage import Chromiumclass SeleniumMiddleware:def __init__(self, crawler):self.crawler = crawler# 连接 spider_closed 信号和 spider_closed 方法self.crawler.signals.connect(self.spider_closed, signal=signals.spider_closed)self.browser = Chromium()self.tab = self.browser.latest_tab@classmethoddef from_crawler(cls, crawler):return cls(crawler)def process_request(self, request, spider):self.tab.get(request.url)body = self.tab.htmlreturn HtmlResponse(url=request.url, body=body, request=request, encoding='utf-8')def spider_closed(self, spider):self.tab.close()self.browser.quit()