You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scrapy inline-requests搭配async函数无法等待请求完成问题

问题根因

scrapy-inline-requests的实现基于Twisted的inlineCallbacks机制,仅能对普通生成器函数做拦截处理:自动识别生成器yield出的Request对象,挂起函数直到请求响应返回后再恢复执行。当你给函数加上async关键字后,函数会变成原生协程,@inline_requests装饰器无法正确拦截协程内部yield的对象,协程会直接把yield的Request作为普通生成器值抛出,不会等待请求完成,就会出现逻辑提前向下执行的问题。

可选解决方案

方案1:保留scrapy-inline-requests依赖,桥接asyncio逻辑

不需要把回调声明为async函数,在配置了AsyncioSelectorReactor的前提下,使用Twisted提供的ensureDeferred工具把asyncio协程包装为Twisted Deferred对象,yield给@inline_requests识别等待即可,既保留原有的inline-requests串行写法,又能执行所有asyncio逻辑。

代码调整步骤

  1. 新增导入:
from twisted.internet.defer import ensureDeferred
  1. 移除handle方法的async关键字,所有asyncio逻辑用ensureDeferred包装后yield等待:
@inline_requests
def handle(self, response):
    code = response.meta["code"]
    cookiejar_ref = response.meta["cookiejar"]

    # 解析csrfToken逻辑保持不变
    soup = BeautifulSoup(response.text, "html.parser")
    relevant_script = [script.text for script in soup.find_all("script") if "csrfToken" in script.text]
    matched_group = re.search(r'"csrfToken":"(.+?)"', relevant_script[0]) if len(relevant_script) > 0 else None
    if matched_group is None:
        raise Exception("Failed to extract csrfToken")
    csrf_token = matched_group.group(1)

    # 等待asyncio延时,后续websocket等异步逻辑用同样方式包装即可
    yield ensureDeferred(asyncio.sleep(1))
    # 例:yield ensureDeferred(your_websocket_connect_logic())
   
    # 原有请求逻辑保持不变,inline_requests可正常等待响应
    api = "https://somesite.com/search"
    headers = { "x-csrf-token": csrf_token, 'Content-Type':'application/json' }
    payload = {"a": 1}
    response = yield Request(api, method='POST', headers=headers, meta={'cookiejar': cookiejar_ref}, body=json.dumps(payload))
    lots_url = json.loads(response.text)["redirect"]

    yield {
        "lots_url": lots_url,
    }

方案特点

  • 原有inline-requests写法改动量极小
  • 所有Scrapy原生特性(请求限流、中间件、日志统计、去重)完全保留
  • 无嵌套回调,代码可读性和原有逻辑一致

方案2:移除scrapy-inline-requests依赖,使用Scrapy原生async回调支持

Scrapy 2.0+版本原生支持async def回调,配合已经配置的AsyncioSelectorReactor可以直接用await语法写串行逻辑,完全不需要依赖第三方inline-requests库,不存在兼容性问题,是更稳定的长期方案。

代码调整示例

移除inline_requests相关导入,将回调改为async def,请求直接通过Scrapy引擎的download方法await即可:

# 可删除from inline_requests import inline_requests导入
class QuotesSpider(Spider):
    name = "scraper"

    custom_settings = {
        "TWISTED_REACTOR": "twisted.internet.asyncioreactor.AsyncioSelectorReactor",
        'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter',
    }

    def start_requests(self):
        codes= ["A", "B"]
        url = "https://somesite.com/"
        for i, code in enumerate(codes):
            yield Request(url=url, callback=self.handle, meta={'cookiejar': i, "code": code})

    async def handle(self, response):
        code = response.meta["code"]
        cookiejar_ref = response.meta["cookiejar"]

        # csrfToken解析逻辑保持不变
        soup = BeautifulSoup(response.text, "html.parser")
        relevant_script = [script.text for script in soup.find_all("script") if "csrfToken" in script.text]
        matched_group = re.search(r'"csrfToken":"(.+?)"', relevant_script[0]) if len(relevant_script) > 0 else None
        if matched_group is None:
            raise Exception("Failed to extract csrfToken")
        csrf_token = matched_group.group(1)

        # 原生asyncio逻辑直接await即可,无需包装
        await asyncio.sleep(1)
        # 后续websocket等异步逻辑直接await即可
       
        # 发起请求直接await下载结果
        api = "https://somesite.com/search"
        headers = { "x-csrf-token": csrf_token, 'Content-Type':'application/json' }
        payload = {"a": 1}
        search_req = Request(api, method='POST', headers=headers, meta={'cookiejar': cookiejar_ref}, body=json.dumps(payload))
        response = await self.crawler.engine.download(search_req, spider=self)
        lots_url = json.loads(response.text)["redirect"]

        # 返回Item直接return即可,也可以用yield
        return {
            "lots_url": lots_url,
        }

方案特点

  • 无第三方依赖,不存在装饰器和async语法的兼容问题
  • 原生支持所有asyncio生态库的调用,不需要额外包装转换
  • 所有Scrapy原生特性完全保留,请求会正常经过所有下载中间件、遵守并发限流配置、自动纳入日志统计
  • 代码为标准async/await写法,可读性更好

内容的提问来源于stack exchange,提问作者Rahul R Badenkal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 02:54:40