Scrapy异步改造报错:'async_generator' object is not iterable
解决Scrapy异步改造后出现的TypeError: 'async_generator' object is not iterable问题
问题根源
Scrapy的start_requests方法要求返回可迭代对象(如生成器、列表),但你用async def结合yield将其定义成了异步生成器,Scrapy框架无法直接迭代异步生成器,因此抛出TypeError。同时parse方法直接使用await异步调用也不符合Scrapy的回调机制要求。
解决方案
- 用Twisted的
inlineCallbacks和ensureDeferred工具,将异步逻辑包装成Scrapy可识别的Deferred对象,避免使用异步生成器 - 改造
start_requests为普通方法,通过异步工具获取URL后生成请求 - 改造
parse方法,将异步存储操作包装成Deferred,适配Scrapy的回调流程
修改后的完整代码
from scrapy import Spider, Request from scrapy.linkextractors import LinkExtractor from twisted.internet.defer import inlineCallbacks, ensureDeferred import settings from your_module import AsyncMySQLConnection, URLManager # 替换为你的实际模块路径 class WebSpider(Spider): name = 'webspider' allowed_domains = allowed_domains custom_settings = { 'RETRY_TIMES': 8, 'RETRY_DELAY': 5, 'RETRY_HTTP_CODES': [500, 502, 503, 504, 400, 403, 404, 408], } @staticmethod async def get_start_urls(): async with AsyncMySQLConnection() as mysql_connection: urls = await mysql_connection.select_urls(settings.DATABASE_NAME, settings.TABLE_NAME) return urls @inlineCallbacks def start_requests(self): # 将异步获取URL的任务包装成Deferred,等待结果返回 urls = yield ensureDeferred(self.get_start_urls()) # 生成请求,返回可迭代的Request对象 for url in urls: yield Request(url=url, callback=self.parse) @inlineCallbacks def parse(self, response): links = LinkExtractor(allowed_domains).extract_links(response) all_urls = [link.url for link in links] # 将异步存储操作包装成Deferred,交给Twisted事件循环处理 yield ensureDeferred(URLManager().save_new_url_in_redis(all_urls)) print(all_urls) if __name__ == '__main__': from scrapy import cmdline cmdline.execute("scrapy crawl webspider".split())
关键修改点说明
- 移除
start_requests和parse的async修饰符,改用@inlineCallbacks装饰器,让Twisted处理异步逻辑 - 用
ensureDeferred包装异步函数(get_start_urls、save_new_url_in_redis),将其转换为Twisted的Deferred对象,通过yield等待异步任务完成 start_requests最终返回的是由Twisted管理的Deferred,其结果是可迭代的Request列表,符合Scrapy的要求
内容的提问来源于stack exchange,提问作者Zari Mohammadi
相关产品推荐
相关产品推荐

