回调写成 async def 之后,Scrapy 怎么接收它们的返回值、会不会并行、能不能 await Twisted 的 Deferred、能不能直接用 aiohttp?这篇把八种回调写法和几个常见的坑一次测完。
def,return 一个 dict item=[1];cb2=-;spider_exceptions=0 def,return 列表 [dict, Request] item=[2];cb2=被调用;spider_exceptions=0 def 生成器,yield item=[3];cb2=-;spider_exceptions=0 async def,return 一个 dict item=[4];cb2=-;spider_exceptions=0 async def,return 列表 item=[5, 6];cb2=-;spider_exceptions=0 async def,await 之后 return item=[7];cb2=-;spider_exceptions=0 async def 异步生成器,yield item=[8, 9];cb2=-;spider_exceptions=0 async def,return None item=[];cb2=-;spider_exceptions=0
回调可以是普通函数、生成器、协程或异步生成器;可以 return 一个对象、一个列表,也可以逐个 yield。引擎只区分每个产出物是 Request 还是 item。
136 ms cb 开始
136 ms pipeline {'k': 'first'}
1138 ms pipeline {'k': 'second'}普通 Spider 里异步生成器边 yield 边处理;CrawlSpider 的规则回调会先收集完整个异步生成器。
async def + await asyncio.sleep(0.3) 总耗时 0.33 s def + time.sleep(0.3) 总耗时 6.25 s
async 回调在同一个事件循环里并发;同步的阻塞调用会把所有回调串行化。
直接 await Deferred → RuntimeError: Task got bad yield: <Deferred at 0x…> await maybe_deferred_to_future(d) → deferred-result await deferred_to_future(d) → deferred-result
asyncio reactor 下要用 maybe_deferred_to_future / deferred_to_future 才能 await Twisted 的 Deferred。
服务器收到 40 个请求(20 + 20 个 aiohttp 请求);item_scraped_count=20;总耗时 0.03 s
可以直接在回调里用 aiohttp 等基于 asyncio 的库,但它们的请求不经过 Scrapy 的下载器中间件、去重和限速。
500 响应(HttpError)→ async errback 产出的 item:[{'from_errback': 500}];stats spider_exceptions=0
async 回调抛异常:spider_exceptions/ValueError=1;日志 ['[scrapy.core.scraper] ERROR: Spider error processing <GET http://127.0.0.1:9960/x> (referer: None)']async 回调抛的异常和同步回调一样计入 spider_exceptions;async errback 同样可以 yield item。