如何让Scrapy生成请求时立即执行,而非等待全部请求创建完成
问题
我有一个Scrapy爬虫,会从私有Github下载包含约100万播放列表ID的文件,希望借助Chartmetric(音乐数据分析工具)对这些ID进行搜索。相关代码如下:
def start_requests(self): self.logger.info("starting requests") yield scrapy.Request('https://raw.githubusercontent.com/XXXX/XXXX/main/unique_playlist_ids.csv.gz', method = 'GET', callback = self.do_requests, headers = {'Authorization': 'token XXX'})
在do_requests函数中,我会解压数据集并循环所有行来创建请求:
def do_requests(self,response): data = BytesIO(response.body) with gzip.open(data, 'rt') as f: reader = csv.reader(f) playlists = [row[0] for row in reader] for n,playlist_id in enumerate(playlists): search_data = {"q": "https://open.spotify.com/playlist/"+playlist_id, "type": "playlists", "limit": "1"} yield scrapy.Request(url = "https://api.chartmetric.com/api/search?" + urlencode(search_data), method='GET', callback=self.parse_playlist, meta = {"spotify_playlist_id": playlist_id}, priority = -n, dont_filter=True)
遇到的问题:Scrapy会先循环完100万行创建所有请求,之后才开始执行(并调用self.parse_playlist解析),但使用本地CSV文件时不会出现此问题。另外,有一个自定义中间件会在爬虫启动时创建并轮换3个Token以调用Chartmetric API,但这应该不影响当前问题。尝试过设置priority = -n或priority = n,均无效。需要让Scrapy在生成请求时立即执行,而非等待全部请求调度完成。
解决方案
问题根源在于你一次性将100万条数据全部加载到内存列表playlists中,必须等整个列表生成完毕,Scrapy才能开始调度请求。本地文件读取更快、内存占用压力小,所以产生了“立即执行”的错觉,但逻辑本质一致。
核心修复:边读取边生成请求
不要一次性把所有行存入列表,直接遍历csv.reader的迭代器,读取一行就生成一个请求,这样Scrapy可以在生成请求的同时立即调度执行:
def do_requests(self, response): data = BytesIO(response.body) with gzip.open(data, 'rt') as f: reader = csv.reader(f) # 直接遍历reader迭代器,无需先转成列表 for n, row in enumerate(reader): playlist_id = row[0] search_data = {"q": f"https://open.spotify.com/playlist/{playlist_id}", "type": "playlists", "limit": "1"} yield scrapy.Request( url=f"https://api.chartmetric.com/api/search?{urlencode(search_data)}", method='GET', callback=self.parse_playlist, meta={"spotify_playlist_id": playlist_id}, priority=-n, dont_filter=True )
可选优化:调整并发设置
即使边生成边yield,Scrapy默认并发数可能限制请求的立即执行,可在settings.py中根据目标API的限流规则调整:
# 全局并发请求数 CONCURRENT_REQUESTS = 32 # 针对Chartmetric域名的并发数 CONCURRENT_REQUESTS_PER_DOMAIN = 16
排查项:确认中间件无阻塞
临时禁用自定义Token轮换中间件,测试是否是中间件导致请求延迟调度。如果禁用后恢复正常,再检查中间件逻辑是否存在阻塞行为。
内容的提问来源于stack exchange,提问作者thematthiaz
相关产品推荐
相关产品推荐

