Scrapy/Python:如何等待yield图片下载完成后再执行生成PDF代码
问题原因
Scrapy 的 Item Pipeline 是异步执行的,你在 parse_chapter 中 yield 包含图片链接的 item 之后,代码不会阻塞等待图片管道完成下载,会直接执行后续的 self.create_pdf(),所以会出现图片还未全部下载完成就开始生成 PDF 的问题。
推荐解决方案:在图片管道中监听下载完成事件
Scrapy 内置的 ImagesPipeline 提供了 item_completed 方法,会在当前 item 关联的所有图片下载完成后自动调用,你可以在这里触发 PDF 生成逻辑,是成本最低的实现方式。
步骤1:调整 parse_chapter 返回内容
为了区分不同章节的图片,你需要在返回的 item 中加入章节的唯一标识(比如章节URL、章节名、自定义章节ID),同时如果一个章节有多张图,要把提取方法从 get() 改为 getall():
def parse_chapter(self, response): logging.debug("parse_chapter") # 提取章节唯一标识,示例用URL后缀当ID,你可以替换为自己提取的章节名 chapter_id = response.url.split("/")[-1] # 单张图保留get()也可,多张图必须用getall() image_urls = response.xpath('/html/body/div[1]/div[3]/div/div[2]/div[2]/a/img/@src').getall() yield { 'image_urls' : image_urls, 'chapter_id': chapter_id }
步骤2:重写图片管道的 item_completed 方法
在你已修改的 pipelines.py 中,调整自定义的图片管道代码:
from scrapy.pipelines.images import ImagesPipeline from scrapy.exceptions import DropItem class CustomImagePipeline(ImagesPipeline): def item_completed(self, results, item, info): # 过滤下载失败的图片,提取有效下载路径 image_paths = [x['path'] for ok, x in results if ok] if not image_paths: raise DropItem("当前章节无有效下载的图片,跳过处理") # 把下载完成的图片路径存入item item['image_paths'] = image_paths # 调用爬虫中定义的create_pdf方法,传入章节标识和图片路径 info.spider.create_pdf(item['chapter_id'], item['image_paths']) return item
步骤3:调整 create_pdf 方法适配新逻辑
def create_pdf(self, chapter_id, image_paths): # 此处image_paths就是当前章节所有已完成下载的图片路径,直接生成PDF即可 # 生成PDF完成后再删除对应图片即可 ...
其他可选方案
如果不想修改管道,也可以通过 scrapy.signals.item_completed 信号监听所有item的处理状态,统计对应章节的图片下载完成度,全部下载完成后再触发PDF生成,不过实现复杂度比修改管道高,更推荐上述方案。
内容的提问来源于stack exchange,提问作者Romain
相关产品推荐
相关产品推荐

