scrapy中下载中间件的核心规则

1 阅读2分钟

一.首先我们要了解的是:

请求出去的时候走:process_request

响应回来的时候走:process_response

引擎会按顺序经过所有中间件,每个中间件都能拦截、修改、重定向请求 / 响应。

二.详细介绍:process_request

process_request(请求出去时触发)

什么时候触发?

爬虫发起请求 → 还没发到下载器 → 先经过这个方法

它的返回值只有 3 种情况:

  1. 返回 None(最常用):return None

或者不写 return

✅ 意思:放行!

请求继续往下走,传给下一个中间件,最终交给下载器发请求。

  1. 返回 Response 对象:return Response("我是假响应")

❌ 意思:拦截!不发请求了!

直接自己造一个响应返回给爬虫,根本不会访问网站。

  1. 返回 Request 对象

return Request("新的URL")

🔁 意思:替换请求!

把当前请求扔掉,换成新请求,丢回调度器重新排队。 后面的中间件不再执行!

三、详细介绍:process_response

process_response(响应回来时触发)

什么时候触发?

下载器拿到网页响应 → 回传给爬虫前 → 经过这个方法

它的返回值只有 2 种情况:

  1. 返回 Response 对象:return response

✅ 意思:放行响应!

响应继续往下走,最终交给爬虫的 parse 函数处理。

  1. 返回 Request 对象:return Request("新URL")

🔁 意思:扔掉响应,重新发请求!

当前响应不用了,把新请求丢回调度器。 后面的中间件不再执行!

四.二者总结

process_request(请求出去)

return None → 放行,继续发请求

return Response → 拦截,直接返回假页面

return Request → 换个请求重新发

process_response(响应回来)

return Response → 放行,交给爬虫解析

return Request → 不想要这个响应,重新请求

五.拓展疑问

真实需求: 同一个中间件 有的爬虫要经过 有的爬虫不要经过 而且: 不能直接在中间件开头 return None,不然所有爬虫都跳过了! 我该怎么做呢???

解决方案: 用 spider.name 判断! 核心思路: 在中间件里,判断当前是哪个爬虫在运行

是需要的爬虫 → 执行中间件逻辑

是不需要的爬虫 → 直接 return None 跳过

代码示例:

class UserAgentDownloaderMiddleware:

USER_AGENTS_LIST = [

    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",

    # ... 你的UA列表

]
def process_request(self, request, spider):
    """
    spider 就是当前正在运行的爬虫!
    """

    # 如果爬虫名字 不是 qingting → 直接跳过!

    if spider.name != "qingting":  

        return None  # 跳过中间件,直接放行

  
    # 只有爬虫叫 qingting 的时候,才会执行到这里
    user_agent = random.choice(self.USER_AGENTS_LIST)

    request.headers['User-Agent'] = user_agent

    return None

更灵活的写法:

def process_request(self, request, spider):

    # 白名单:这些爬虫使用UA中间件

     allowed_spiders = ["qingting", "music", "news"]


     if spider.name not in allowed_spiders:
        return None  # 不在白名单,直接跳过

# 执行中间件逻辑...