在使用 Scrapy 下载图片时,你可能会发现 file_path
方法被多次调用。这篇博客将解释为什么会发生这种情况,并提供一个排查方法,帮助你理解和解决这个问题。
问题描述
你在下载图片时,发现 file_path
方法被调用了三次。以下是简化后的代码示例:
import scrapy
from scrapy.pipelines.images import ImagesPipeline
import traceback
def sanitize_filename(filename):
import re
sanitized = re.sub(r'[<>:"/\\|?*]', '_', filename)
return sanitized
class BiantuDownPicSavePipeline(ImagesPipeline):
def get_media_requests(self, item, info):
min_url = item['min_url']
print('1. 发送请求去下载图片, min_url:', min_url)
return scrapy.Request(url=min_url, meta={'item': item})
def file_path(self, request, response=None, info=None, *, item=None):
print('2. 图片的存储路径')
item = request.meta['item']
# 打印调用堆栈信息
print("file_path called")
traceback.print_stack()
filename = item['title_min']
filename = sanitize_filename(filename)
return filename
def item_completed(self, results, item, info):
print(f'3. 对Item进行更新, result: {results}')
if results:
ok, res = results[0]
if ok:
item['min_path'] = res["path"]
return item
排查方法
第一步:添加调试信息
在 file_path
方法中,我们添加了 traceback.print_stack()
来打印调用堆栈信息,以便我们能看到 file_path
方法每次被调用的具体位置。
第二步:运行代码并查看调试输出
当你运行这段代码时,你会看到类似以下的输出:
1. 发送请求去下载图片, min_url: https://pic.netbian.com/uploads/allimg/240509/010156-1715187716fada.jpg
2. 图片的存储路径
file_path called
File "path/to/your_script.py", line XX, in <module>
main()
...
3. 对Item进行更新, result: [(True, {'url': 'https://pic.netbian.com/uploads/allimg/240509/010156-1715187716fada.jpg', 'path': 'sanitized_filename.jpg', 'checksum': 'checksum_value', 'status': 'uptodate'})]
每次 file_path
被调用时,堆栈信息会显示其调用路径。
为什么 file_path
会被多次调用?
原因一:Scrapy 重试机制
Scrapy 有一个内置的重试机制,当第一次下载失败时,Scrapy 会自动重试几次(默认最多重试 2 次),总共尝试 3 次。如果图片下载在第一次或第二次失败,Scrapy 会再次调用 file_path
方法。
原因二:并发请求
如果在爬虫设置中启用了并发请求,并且对同一图片 URL 发送了多个并发请求,每个请求都会调用 file_path
方法来生成文件存储路径。
原因三:Scrapy 多次处理
Scrapy 在处理一个 item 时,可能会多次调用 file_path
方法来确认文件路径。特别是在处理管道(pipeline)和下载媒体(media)文件时。
解决方法
减少重试次数
你可以在 settings.py
中设置 RETRY_TIMES
为 0 或 1,以减少重试次数。
RETRY_TIMES = 1
增加下载延迟
如果是因为并发请求导致的,可以增加下载延迟或减少并发请求数。
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 1
检查缓存机制
如果是因为 Scrapy 内部缓存机制导致的多次调用,可以检查 Scrapy 的缓存设置,确保没有不必要的缓存。
HTTPCACHE_ENABLED = False
总结
file_path
方法被多次调用的原因可能是 Scrapy 的重试机制、并发请求处理以及内部多次处理 item 的机制。通过添加详细的调试信息并分析调用堆栈,可以准确找出多次调用的具体原因,并采取相应的措施进行优化。