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

Scrapy自定义FilesPipeline下载嵌套PDF遇回调失效问题求助

解决Scrapy自定义FilesPipeline下载嵌套PDF的问题

你的问题核心在于对FilesPipeline的工作机制理解有误,导致自定义回调函数没有被执行,最终下载了包裹页而非目标PDF。我来帮你拆解问题并给出解决方案:

问题分析

FilesPipeline的get_media_requests方法设计初衷是直接返回要下载的媒体文件请求,它会接管这些请求的整个处理流程,不会触发你指定的callback方法。所以你的get_pdfurl完全没有被执行,自然也就无法解析iframe中的PDF链接,最终下载的还是原始的包裹页。另外你的代码里还有个小语法错误:Python3中print response.url应该写成print(response.url)。

解决方案

方法一:用inline_requests在Pipeline内异步解析包裹页

Scrapy提供了inline_requests装饰器,允许你在生成器方法中发起异步请求,等待响应后再继续处理。这样就能在get_media_requests里先请求包裹页,解析出真实PDF链接后再发起下载请求:

import scrapy
from scrapy.pipelines.files import FilesPipeline
from scrapy.utils.decorators import inline_requests
import logging

class PdfPipeline(FilesPipeline):
    @inline_requests
    def get_media_requests(self, item, spider):
        # 先请求PDF包裹页
        response = yield scrapy.Request(item['pdfLink'])
        logging.info(f"成功请求包裹页:{response.url}")
        
        # 解析iframe中的PDF链接
        pdf_relative_url = response.css('iframe::attr(src)').get()
        if not pdf_relative_url:
            logging.warning(f"未找到PDF iframe链接:{response.url}")
            return
        
        # 处理相对路径,拼接成绝对URL
        pdf_absolute_url = response.urljoin(pdf_relative_url)
        logging.info(f"获取到真实PDF链接:{pdf_absolute_url}")
        
        # 返回PDF下载请求,交给FilesPipeline处理
        yield scrapy.Request(pdf_absolute_url)

方法二:在Spider中提前解析包裹页(更简洁)

如果不需要在Pipeline中处理,也可以把包裹页的解析逻辑放到Spider里,直接将真实PDF链接传入FilesPipeline默认监听的file_urls字段:

# 你的Spider代码示例
def parse(self, response):
    item = MyCustomItem()
    # 先获取包裹页URL
    pdf_wrap_url = response.css('a.pdf-btn::attr(href)').get()
    
    # 发起请求解析包裹页,携带当前item
    yield scrapy.Request(
        pdf_wrap_url,
        callback=self.parse_pdf_wrap_page,
        meta={'item': item}
    )

def parse_pdf_wrap_page(self, response):
    item = response.meta['item']
    # 提取iframe中的PDF链接并转成绝对路径
    pdf_url = response.urljoin(response.css('iframe::attr(src)').get())
    # 赋值给FilesPipeline默认识别的字段
    item['file_urls'] = [pdf_url]
    yield item

然后在settings.py中配置基础的FilesPipeline即可:

ITEM_PIPELINES = {
    'scrapy.pipelines.files.FilesPipeline': 1,
    # 如果用自定义的PdfPipeline,替换成你的路径:'your_project.pipelines.PdfPipeline': 1
}
FILES_STORE = './downloaded_pdfs'  # 设置PDF保存目录

关键注意事项

  1. 绝对路径处理:iframe的src很可能是相对路径,一定要用response.urljoin()拼接成绝对URL,否则Scrapy无法正确请求。
  2. 日志配置:确保settings.py中LOG_LEVEL = 'INFO',这样才能看到你打印的日志信息。
  3. 自定义字段(可选):如果你的item不想用默认的file_urls和files字段,可以在自定义Pipeline中重写:
    class PdfPipeline(FilesPipeline):
        file_urls_field = 'your_custom_url_field'  # 替换成你item中的包裹页URL字段名
        files_field = 'your_custom_files_field'    # 替换成你想存储文件信息的字段名
    

内容的提问来源于stack exchange,提问作者vera

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:53:58