Scrapy 中间件

Scrapy 中间件的作用

中间件(Middleware)是一种可以介入到Scrapy的请求(request)-响应(response)处理过程中的钩子机制,允许开发者在执行特定任务时对请求或响应进行自定义处理。中间件可以看作是位于Scrapy引擎和爬虫、下载器之间的一个处理层,用于修改或扩展Scrapy的默认行为。

Scrapy 中间件的分类

1. 下载器中间件(Downloader Middleware)

下载器中间件用于处理发出的请求和下载的响应,它们在请求到达下载器之前或响应到达引擎之前执行。这类中间件可以用于:

  • 修改请求头(比如User-Agent、Cookies等)
  • 设置代理(如IP代理)
  • 处理重定向
  • 管理请求失败的重试机制
  • 捕获和处理下载过程中发生的异常

2. 爬虫中间件(Spider Middleware)

爬虫中间件用于处理爬虫生成的请求以及接收到的响应。在引擎把响应交给爬虫处理之前,或者爬虫生成的请求交给引擎之前,爬虫中间件都有机会对这些对象进行处理。具体应用场景包括:

  • 对爬虫产生的请求进行过滤或修改
  • 处理从下载器返回的响应,比如进行一些前置的数据清理
  • 修改从爬虫传出的项目(item)

下载器中间件方法

java 复制代码
/**
 * 处理每一个经过下载器的请求
 *
 * @param request 待处理的请求对象(scrapy.http.Request)
 * @param spider  当前正在运行的爬虫实例
 * @return 
 * --------------- 可以返回 None,表示继续处理这个请求。
 * --------------- 可以返回 scrapy.http.Response,则中间件会返回这个响应对象并跳过下载过程,直接把响应传递给爬虫。
 * --------------- 可以返回 scrapy.exceptions.IgnoreRequest 异常,则请求会被丢弃,触发 spider 的 request_dropped 信号。
 */
process_request(self, request, spider)
java 复制代码
/**
 * 处理下载器返回的响应
 * 
 * @param request  产生这个响应的请求对象(scrapy.http.Request)
 * @param response 当前的响应对象(scrapy.http.Response)
 * @param spider   当前的爬虫实例
 * @return 
 * --------------- 可以返回 response 对象(或者修改后的新 response 对象),继续交给下一个中间件处理或直接给爬虫。
 * --------------- 可以返回一个新的 scrapy.http.Request,该请求会重新被调度和下载。
 * --------------- 可以抛出 scrapy.exceptions.IgnoreRequest 异常,表示忽略此请求。
 */
process_response(self, request, response, spider)
java 复制代码
/**
 * 当下载器或者 process_request/process_response 方法抛出异常时,调用此方法处理异
 *
 * @param request   产生这个响应的请求对象(scrapy.http.Request)
 * @param exception 抛出的异常对象
 * @param spider    当前的爬虫实例
 * @return 
 * --------------- 可以返回 None,继续交由其他中间件处理。
 * --------------- 可以返回一个 scrapy.http.Response 对象,表示已经处理了该异常并提供了替代的响应。
 * --------------- 可以返回一个 scrapy.http.Request 对象,以重新调度此请求。
 */
process_exception(self, request, exception, spider)

爬虫中间件方法

java 复制代码
/**
 * 当下载器将响应传递给爬虫之前,调用此方法处理响应
 *
 * @param response 下载器返回的响应对象
 * @param spider   当前的爬虫实例
 * @return 
 * --------------- 如果返回 None,响应将继续传递给爬虫处理。
 * --------------- 可以抛出 scrapy.exceptions.IgnoreRequest,表示忽略此请求和响应,不会传递给爬虫。
 */
process_spider_input(self, response, spider)
java 复制代码
/**
 * 处理爬虫返回的结果(通常是 item 或新的 request)
 *
 * @param response 传递给爬虫的响应对象
 * @param result   爬虫返回的结果,通常是生成器或列表,包含 Item 对象、Request 对象等
 * @param spider   当前的爬虫实例
 * @return 必须返回一个可迭代对象,包含 Item 对象或 Request 对象。如果需要,可以对结果进行过滤或修改。
 */
process_spider_output(self, response, result, spider)
java 复制代码
/**
 * 当爬虫在处理响应过程中抛出异常时,调用此方法处理异常
 *
 * @param response  导致异常的响应对象
 * @param exception 抛出的异常对象
 * @param spider    当前的爬虫实例
 * @return 
 * --------------- 如果返回 None,Scrapy将继续处理该异常。
 * --------------- 如果返回一个可迭代对象,将替代爬虫返回的结果。
 */
process_spider_exception(self, response, exception, spider)
java 复制代码
/**
 * 处理爬虫开始时生成的初始请求
 *
 * @param start_requests 爬虫开始时生成的初始请求(可迭代对象)
 * @param spider         当前的爬虫实例
 * @return 必须返回一个可迭代对象,包含 Request 对象。
 */
process_start_requests(self, start_requests, spider)

功能实现

实现随机 User-Agent

  1. 在 Scrapy 项目的 middlewares.py 文件中自定义一个下载器中间件类,在这个中间件中,process_request 方法会为每个请求随机选择一个 User-Agent。
python 复制代码
import random

class RandomUserAgentMiddleware:
    def __init__(self, user_agents):
        self.user_agents = user_agents

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            # 获取 settings.py 中的配置
            user_agents=crawler.settings.get('USER_AGENTS')
        )

    def process_request(self, request, spider):
        # 从集合中随机取一个元素
        user_agent = random.choice(self.user_agents)
        if user_agent:
            request.headers['User-Agent'] = user_agent
  1. 在 Scrapy 项目的 settings.py 文件中配置中间件,并提供一个 User-Agent 列表。
python 复制代码
# 配置多个 USER_AGENT
USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.3.5211 SLBChan/25",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/57.0 Safari/537.3"
]

# 开启下载器中间件
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.RandomUserAgentMiddleware": 543,
}

# 关闭 robots.txt 检查
ROBOTSTXT_OBEY = False
  1. 验证是否成功
python 复制代码
import scrapy

class BaiduSpider(scrapy.Spider):
    name = "baidu"
    allowed_domains = ["www.baidu.com"]
    start_urls = ["https://www.baidu.com"]

    def parse(self, response):
        print("随机 User-Agent:", response.request.headers['User-Agent'])

实现随机 IP 代理

  1. 在 Scrapy 项目的 middlewares.py 文件中自定义一个下载器中间件类。
python 复制代码
import random
import base64

class RandomProxyMiddleware:
    def __init__(self, proxies):
        self.proxies = proxies

    @classmethod
    def from_crawler(cls, crawler):
        # 从 settings.py 中获取 PROXIES 列表
        return cls(
            proxies=crawler.settings.get('PROXIES')
        )

    def process_request(self, request, spider):
        # 随机选择一个代理
        proxy = random.choice(self.proxies)

        if '@' in proxy:
            # 配置带账号密码的
            credentials, proxy_url = proxy.split('@')
            proto, credentials = credentials.split('//')
            proto = proto + "//"
            auth = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
            request.headers['Proxy-Authorization'] = 'Basic ' + auth
            request.meta['proxy'] = proto + proxy_url
        else:
            # 配置无账号密码的
            request.meta['proxy'] = proxy
  1. 在 Scrapy 项目的 settings.py 文件中配置中间件,并提供一个代理列表。
python 复制代码
# 定义代理列表,代理太多可以存在数据库中
PROXIES = [
    # 无密码的写法
    'http://122.116.xxx.xxx:8888',
    # 带账号密码的写法,代理ip需要在服务商处进行购买
    'http://user:pass@14.29.xxx.xxx:16817',
]

# 开启下载器中间件
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.RandomProxyMiddleware": 543,
}

# 关闭 robots.txt 检查
ROBOTSTXT_OBEY = False

实现 selenium 动态数据加载渲染完成后再爬取数据

  1. 在 Scrapy 项目的 middlewares.py 文件中自定义一个下载器中间件类。
python 复制代码
from selenium import webdriver
import time
from scrapy.http import HtmlResponse


class SeleniumMiddleware:

    def __init__(self):
        # 打开一个无头模式的谷歌浏览器
        # options = webdriver.ChromeOptions()
        # options.add_argument('--headless')
        # options.add_argument('--disable-gpu')  # 如果你使用的是 Windows 系统,需要加上这行
        # driver = webdriver.Chrome(options=options)

        # 打开一个 谷歌浏览器
        self.driver = webdriver.Chrome()

    def process_request(self, request, spider):
        url = request.url
        # 比如 http://abc.com/123?day=111 这个url中的数据要加载很久才能渲染完成显示出来,这时就需要对这个url使用 selenium 动态加载
        if 'day' in url:  # 判断url中包含的字符串,用于找到这个url地址
            # 打开url
            self.driver.get(url)
            # 等待渲染完成时间
            time.sleep(5)
            # 拿到渲染完成后的页面数据
            data = self.driver.page_source
            # 关闭浏览器
            self.driver.close()
            # 封装新的response对象返回给爬虫,让爬虫从data中爬取数据
            response = HtmlResponse(url=url, body=data, encoding='utf-8', request=request)
            return response
  1. 在 Scrapy 项目的 settings.py 文件中配置中间件。
python 复制代码
# 开启下载器中间件
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.SeleniumMiddleware": 543,
}

# 关闭 robots.txt 检查
ROBOTSTXT_OBEY = False
相关推荐
数据小爬虫@8 小时前
如何利用java爬虫获得淘宝商品评论
java·开发语言·爬虫
好看资源平台14 小时前
网络爬虫——爬虫项目案例
爬虫·python
躺平的花卷17 小时前
Python爬虫案例八:抓取597招聘网信息并用xlutils进行excel数据的保存
爬虫·excel
爱分享的码瑞哥17 小时前
Python爬虫中的IP封禁问题及其解决方案
爬虫·python·tcp/ip
不做超级小白19 小时前
工欲善其事,必先利其器;爬虫路上,我用抓包
爬虫
Smartdaili China19 小时前
如何在 Microsoft Edge 中设置代理: 快速而简单的方法
前端·爬虫·安全·microsoft·edge·社交·动态住宅代理
好看资源平台1 天前
网络爬虫——综合实战项目:多平台房源信息采集与分析系统
爬虫·python
Tech Synapse1 天前
Python网络爬虫实践案例:爬取猫眼电影Top100
开发语言·爬虫·python
数据小爬虫@2 天前
利用Python爬虫获取淘宝店铺详情
开发语言·爬虫·python
B站计算机毕业设计超人2 天前
计算机毕业设计SparkStreaming+Kafka新能源汽车推荐系统 汽车数据分析可视化大屏 新能源汽车推荐系统 汽车爬虫 汽车大数据 机器学习
数据仓库·爬虫·python·数据分析·kafka·数据可视化·推荐算法