Python爬虫框架Scrapy下载图片到4万张左右总是会卡住,如何解决?
已经从本地直接获取图片 url,大概有 20w 个左右,在下载到 4w 左右 scrapy 总是卡住,开始以为是内存泄漏,后来检测内存和 cpu 使用率,在卡住的时候并没有发现异常,下载部分代码如下:
def parse(self, response):
//get local list_img_urls
item = DownloaderItem()
item['image_urls'] = list_img_urls
yield item
有人遇到过这种情况吗?望不吝相助
Python爬虫框架Scrapy下载图片到4万张左右总是会卡住,如何解决?
scrapy 的设计是 spider 是去爬取 HTML 页面的,然后转换成 Item, 然后通过 pipeline 去下载 item 关联的照片。 我每天下载 150 万张照片无压力。
这个问题我遇到过,Scrapy的ImagesPipeline在处理大量图片时确实容易卡住。核心原因是默认配置下图片下载是同步的,大量IO操作会阻塞整个爬虫。
关键是要启用异步下载并调整并发设置。在settings.py里加上这些配置:
# 启用自定义的媒体管道
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline': 1,
}
# 图片存储路径
IMAGES_STORE = './images'
# 异步下载的关键配置
CONCURRENT_REQUESTS = 32 # 总并发数
CONCURRENT_ITEMS = 100 # 每个请求处理的最大item数
IMAGES_EXPIRES = 90 # 图片过期天数
# 调整Twisted反应器线程池大小
REACTOR_THREADPOOL_MAXSIZE = 20
# 下载延迟和超时设置
DOWNLOAD_DELAY = 0.25
DOWNLOAD_TIMEOUT = 30
# 自动限速
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
如果还卡,建议用自定义的ImagesPipeline继承类,加入错误重试和日志:
import scrapy
from scrapy.pipelines.images import ImagesPipeline
from scrapy.exceptions import DropItem
class CustomImagesPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
for image_url in item['image_urls']:
yield scrapy.Request(image_url, meta={'item': item})
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 contains no images")
item['image_paths'] = image_paths
return item
记得把ITEM_PIPELINES里的路径改成你自己的CustomImagesPipeline。
另外检查下是不是磁盘IO瓶颈,可以试试把图片存到不同目录分散写入压力。还有可能是内存问题,4万张图片的URL和元数据如果全放在内存里也会卡,适当调低CONCURRENT_ITEMS值。
简单说就是调高并发加异步处理。
去重的部分在内存里,太多就会缓存到磁盘,那就慢了
图片下载应该是 ImagesPipeline ?
DOWNLOAD_TIMEOUT 和 CONCURRENT_REQUESTS 怎么设置的,
是不是无响应的链接超过并发数限制了?
抓到两个集图的
我是在空目录下重新下载的,img_url 里一般也不会有重复的
是的,是用的继承 ImagesPipeline 的方法
DOWNLOAD_TIMEOUT = 30,CONCURRENT_REQUESTS 这个用的默认的,应该是 8 个吧
刚才是我看错了,CONCURRENT_REQUESTS 默认的是 16 个,我尝试了把 RETRY_ENABLED = False,并且缩短了 download_timeout,但是并没有明显改善,但是我发现虽然 top 监控的内存占用率并没有提高,但是在卡住的那段时间里,输入命令等会变得很卡,我在找是不是和服务器性能哪里有关的原因导致的
查看连接数,netstat -an
scrapy 为了去重,会把 url 缓存在内存中,超过一定数量,就只能写磁盘了,那时候 io 超高就卡死了,这是我遇到过的情况。
我对 netstat 命令不大熟悉,可以给个提示应该怎么查看 scrapy 的异常连接吗?
哦,这种情况,我以前没有遇到过,请教下这种情况是怎么去 debug 呢?
看 io,看内存占用
仔细检查进程资源占用情况
top: 看 cpu,内存
iotop,iostat:看磁盘 io 情况
nmon:看系统网络资源情况
nload:看网卡流量情况
ss,netstat:看网络连接
加我们的群问效率高些,一群工程师组建的面向初学者的
Python Linux 学习群,qq 群号:278529278,
Php Linux 学习群,qq 群号:476648701,
非商业性质,拒绝广告,只接收真正想学这方面技术的朋友,交流学习,申请请说明来自 v2ex
好的,谢谢,我去检查下 io
谢谢
,
感谢各位提供帮助,问题应该还是出在内存上,由于我是可以直接拿到那几十万的 url 的,这些请求会进入到在 scrapy 的请求队列中,占用内存,直至最后服务器内存不够 down 掉;
我在网上查了下资料,scrapy 的调度器来控制队列,但是似乎不 hack 源码没有办法控制调度器对队列的操作;
最后我的解决方法是在数据库就把数据切片,切成 2w 套循环,就不会出现内存不够的问题了;
希望后来的大神有更优雅的方法可以指教。我会持续关注这个问题
可以使用 scrapy_redis 来完成,所有的去重都是通过 redis 来完成的,还能够做到状态保持,如果需要可以实现分布式,redis 的内容类似下面,
redis > keys
1) "**:dupefilter"
2) "***:items"
3) "***:requests"
或者可以迭代的读取数据库的内容,下一次请求之前从内存中删除上一次的请求地址,类似 https://www.ibm.com/developerworks/cn/xml/x-hiperfparse/
这个方法我自己没有试过,但是应该是可行的

