Spider Middleware爬虫中间件详解-scrapy爬虫框架学习


Spider Middleware位于Spider和Engine之间,当Download生成Response之后会发送给Spider、在发送给Spider之前,Response会先经过Spider Middleware处、当Spider处理生成Item和Request之后,Item和Request还会经过Spider Middleware的处理。

image.png

Spider Middleware有以下几个用处:

  • Downloader 生成Response之后、Engine会将其发送给Spider进行解析,在此之前可以对Response进行处理
  • Spider在生成Request之后会发送给Engine、然后Request会被转发到Scheduler,在Request发送给Engine之前可以借助Spider Middleware对Request进行处理
  • Spider生成Item之后会被发送到Engine,然后Item会被转发到Item Pipeline,在Engine在转发Item之前、可以借助Spider Middleware对Item进行处理

总结:Spider Middleware可以处理Spider的Response和Spider输出的Item以及Request。

自定义Spider Middleware

同样、scrapy已经内置了很多Spider Middleware、与Downloader Middleware类似、它们被定义在SPIDER_MIDDLEWARES_BASE变量中。同样自定义的Spider Middleware要放在SPIDER_MIDDLEWARES变量里、如果要禁用默认的Spider Middleware中间件、在SPIDER_MIDDLEWARES变量里设置为None即可。

默认的Spider Middleware

# 默认值:
SPIDER_MIDDLEWARES_BASE = {
    "scrapy.spidermiddlewares.httperror.HttpErrorMiddleware": 50,
    "scrapy.spidermiddlewares.referer.RefererMiddleware": 700,
    "scrapy.spidermiddlewares.urllength.UrlLengthMiddleware": 800,
    "scrapy.spidermiddlewares.depth.DepthMiddleware": 900,
}

# 自定义Spider Middleware
SPIDER_MIDDLEWARES = {
    pass
}

核心方法

我们想要拓展scrapy内置的基础的Spider Middleware、只需要实现以下几个方法即可

  • process_spider_input(response, spider)

    当Response通过Spider Middleware时、该方法被调用、处理该Response有两个参数

    • response (Response 对象) – 正在处理的响应
    • spider (Spider 对象) – 此响应的目标 Spider
  • process_spider_output(response, result, spider)

    当Spider处理Response返回结果时、该方法被调用、有三个参数

    • response (Response 对象) – 生成 Spider 此输出的响应
    • result (Request 对象和 项目对象 的可迭代对象) – Spider 返回的结果
    • spider (Spider 对象) – 正在处理其结果的 Spider
  • process_spider_exception(response, exception,spider)

    当Spider或Spider Middleware的process_spider_input方法抛出异常时、该方法被调用

    • response (Response 对象) – 发生异常时正在处理的响应
    • exception (Exception 对象) – 引发的异常
    • spider (Spider 对象) – 引发异常的蜘蛛
  • process_start_requests(start_requests,spider)

    process_start_requests方法以Spider启动的Request为参数调用,执行的过程类至于process_spider_output,只不过他没有关联的Response并且必须返回Request、有两个参数:

    • start_requests (Request 的可迭代对象) – 起始请求
    • spider (Spider 对象) – 起始请求所属的蜘蛛

实战

老样子新建一个scrapy项目

scrapy startproject scrapyspidermiddlewaredemo

cd scrapyspidermiddlewaredemo
scrapy genspider httpbin www.httpbin.org

目录如下:

E:.
│  scrapy.cfg
│
└─scrapyspidermiddlewaredemo
    │  items.py
    │  middlewares.py
    │  pipelines.py
    │  settings.py
    │  __init__.py
    │
    ├─spiders
    │  │  httpbin.py
    │  │  __init__.py
    │  │
    │  └─__pycache__
    │          __init__.cpython-313.pyc
    │
    └─__pycache__
            settings.cpython-313.pyc
            __init__.cpython-313.pyc

__pycache__文件忽略

import scrapy  
from scrapy import Request  
  
  
class HttpbinSpider(scrapy.Spider):  
    name = "httpbin"  
    allowed_domains = ["www.httpbin.org"]  
    start_url = "https://www.httpbin.org/get"
  
    def parse(self, response):  
        print(response.text)  
  
    def start_requests(self):  
        for i in range(5):  
            url = f'{self.start_url}?query={i}'  
            yield Request(url, callback=self.parse)

我们重构了start_requests方法、构造了几个Request、回调方法还是pase方法,并将Response的文本打印出来
运行:

scrapy crawl httpbin

部分输出:

{
  "args": {
    "query": "4"
  },
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Accept-Language": "en",
    "Host": "www.httpbin.org",
    "User-Agent": "Scrapy/2.12.0 (+https://scrapy.org)",
    "X-Amzn-Trace-Id": "Root=1-67fe6950-3e2f549a0f1c8d490fe814ae"
  },
  "origin": "154.19.47.141",
  "url": "https://www.httpbin.org/get?query=4"
}

可以看到scrapy发送的Request的内容被传输了"url": " https://www.httpbin.org/get?query=4"

另外定义一个Item、取四个字段

import scrapy  
  
  
class DemoItem(scrapy.Item):  
    origin = scrapy.Field()  
    headers = scrapy.Field()  
    args = scrapy.Field()  
    url = scrapy.Field()

同时把Spider中parse方法中返回的Response的内容转化为对应的DemoItem、parse修改如下:

def parse(self, response):  
    item = DemoItem(**response.json())  
    yield item

部分输出:

{'args': {'query': '1'},
 'headers': {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Encoding': 'gzip, deflate',
             'Accept-Language': 'en',
             'Host': 'www.httpbin.org',
             'User-Agent': 'Scrapy/2.12.0 (+https://scrapy.org)',
             'X-Amzn-Trace-Id': 'Root=1-67fe6b9d-484a868b441c8bee787d9d81'},
 'origin': '154.19.47.141',
 'url': 'https://www.httpbin.org/get?query=1'}

运行后、scrapy就会产生对应的DemoItem了、可以看到原本的json类型被输出为DemoItem类型。

接下来实现对一个Spider Middleware、对Response、Item、Request的处理。

在Middlewares.py中重新声明一个CustomizeMiddleware类(自定义中间件)
代码如下:

class CustomizeMiddleware:  
    def process_start_requests(self,start_requests,spider):  
        for request in start_requests:  
            url = request.url  
            url += '&name=xiaoming'  
            request = request.replace(url=url)  
            yield request

注意、我们还要在setting中配置我们自定义的中间件

# setting.py
SPIDER_MIDDLEWARES = {  
   "scrapyspidermiddlewaredemo.middlewares.CustomizeMiddleware": 543,  
}

这样我们自定义处理Request的中间件就写好了。
运行、输出如下:

{'args': {'name': 'xiaoming', 'query': '4'},
 'headers': {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Encoding': 'gzip, deflate',
             'Accept-Language': 'en',
             'Host': 'www.httpbin.org',
             'User-Agent': 'Scrapy/2.12.0 (+https://scrapy.org)',
             'X-Amzn-Trace-Id': 'Root=1-67fe6f41-5a7bbd934298f2885bfb0506'},
 'origin': '154.19.47.141',
 'url': 'https://www.httpbin.org/get?query=4&name=xiaoming'}

可以看到Request的url被修改了

对Response和ite的改写

在CustomizeMiddleware类中新增如下定义:

def process_spider_input(self,response,spider):  
    response.status = 201  
    def process_spider_output(self,response,result,spider):  
    for i in  result:  
        if isinstance(i,DemoItem):  
            i['origin'] = None  
            yield i

对于process_spider_output来说、输出就是Request或Item、但是两者混在一起了、添加一个if判断、如果是DemoItem实例就把origin这是为None、当然这里还可以对Request做类似的处理

另外再pase方法中输出Response状态码

print('Status:', response.status)

重新运行、部分输出如下:

Status: 201
2025-04-15 22:49:05 [scrapy.core.scraper] DEBUG: Scraped from <201 https://www.httpbin.org/get?query=2&name=xiaoming>
{'args': {'name': 'xiaoming', 'query': '2'},
 'headers': {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
             'Accept-Encoding': 'gzip, deflate',
             'Accept-Language': 'en',
             'Host': 'www.httpbin.org',
             'User-Agent': 'Scrapy/2.12.0 (+https://scrapy.org)',
             'X-Amzn-Trace-Id': 'Root=1-67fe71dc-31f320d35573ad0d087c2a9a'},
 'origin': None,
 'url': 'https://www.httpbin.org/get?query=2&name=xiaoming'}

可以看到Response的状态码修改为了201,origin为None,

亲学习到此结束、早点休息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值