Scrapy 效率瓶颈

2019-05-05 14:53:07 +08:00
 caneman

现在在爬的一个站点,有一个起始链接,后续所有的链接都是通过上一链接返回的 response 来产生的, (可以理解为从返回的 response 里面提取到下一页的链接)

现在的问题是这样写好的爬虫,感觉是不是硬生生把并发搞成了单线程一样 我并发和线程数都调的很大,但是仍感觉速度很慢,大概每秒处理 2-5 个页面,一天也就只能抓 10-15W 的样子 感觉明显有问题

我想问一下怎么样我才能提高我的抓取效率呢?(单机的情况下)

这是我的一些配置 RETRY_ENABLED = 1 RETRY_TIMES = 2 DOWNLOAD_TIMEOUT = 15

DOWNLOAD_DELAY = 0 CONCURRENT_REQUESTS = 100 CONCURRENT_REQUESTS_PER_DOMAIN = 100 CONCURRENT_REQUESTS_PER_IP = 100

5077 次点击
所在节点    Python
27 条回复
InternetExplorer
2019-05-05 15:00:41 +08:00
要考虑对方服务器的性能的呀,网站服务器的性能可能还没你的爬虫机器高。。。。
caneman
2019-05-05 15:11:07 +08:00
@InternetExplorer 我试了调并发数并没有显著的影响到我的抓取速率,而且对方的站是绝对扛得住的(是个大站)。
这里我虽然写的很大,但是其实抓取频率并没有很高(所以才一直没改),而且抓取到的页面都是按顺序的,我觉得是不是我的抓取逻辑有问题,应该怎么样改善呢?
locoz
2019-05-05 15:27:27 +08:00
如果别人网站原本的翻页逻辑就是 [要根据上一页内容来得到下一页内容] 的,那你并发再高都没有用,跟 Scrapy 没有关系,如果要并发你只能是从分类之类的地方入手。(不过一般不都是这样爬么,直接计算页数爬的话很容易出现漏数据的情况)
locoz
2019-05-05 15:28:15 +08:00
@locoz #3 想了想这么说不太准确,“如果要并发” 换成“如果要提高效率”
caneman
2019-05-05 15:37:55 +08:00
@locoz 总共大概有 4000W 页面,如果我找到了这 4000W 页面的列表,我要写在 start_urls 里面才能实现高并发吗?
之前没有接触过 Scrapy-redis,目前的情况是,单机,带宽还可以,IP/Cookie 等所有反爬措施均已解决,可以理解为网站无反爬站措施,这样的话,我该怎么样实现日抓百万呢?

想到的一种可行的方案是,把所有的 url 写入 redis,然后所有的请求从 redis 里面去取 url, 但是单机的情况下,如何实现并发?(就是不是一个请求结束后再去 redis 取下一个,而是多个线程(并发数)同时连接 redis 去取 url,然后这些个线程同时进行抓取)不太清楚 scrapy-redis 有没有解决这个问题。。。
snal123
2019-05-05 15:42:07 +08:00
timeout_delay 调小 改成 10s 左右,默认 180s 很多时间浪费可能是代理质量很低造成的。
snal123
2019-05-05 15:44:07 +08:00
@snal123 说错了 看到你已经是 15 了 你没用 redis 的话我觉得还是这里的问题
tozp
2019-05-05 15:46:10 +08:00
首先爬虫不必过于追求效率;其次 Scrapy 执行效率是个问题,我现在都是用 Go 框架爬的;最后,Scrapy 你再怎么改也就那样。
dingyaguang117
2019-05-05 15:57:39 +08:00
LZ 你自己不是很清楚吗,下一页的 URL 是上一个的 response 里面读到的,这个肯定是串行啊。你得改变这种串行获取 url 的方式才行
caneman
2019-05-05 15:57:44 +08:00
@tozp 现在日抓 10-20W 级,有点跟不上需求,需求大概是日抓百万,但是不能分布式。。。不是不能用,是现在的问题是单机的性能远远的浪费了,无论是带宽还是性能,都远远的没有用到。
tozp
2019-05-05 16:00:21 +08:00
@caneman 和我之前差不多,用 Go 之后,每天差不多提升了 10 倍,2M-
caneman
2019-05-05 16:03:04 +08:00
@dingyaguang117 改变串行后呢,怎么提高效率,我总不能把 4000W 页面连接都写道 start_urls 里面吧?

其实我现在是有点不太明白 scrapy 是实现并发的原理,网上也没有找到很好的解释文档。按我的理解,它是通过 start_urls 来实现并发的,任何在 parse 里面写的 yield 都会存在上面的串行问题。

我能想到的是把 scrapy 和 redis 对接(单机对接),然后多个线程同时去取 url,然后同时去抓,关键是我不知道 scrapy 支不支持这种操作,也不知道能不能实现或者有没有现成的解决方案,以免重复造轮子或者根本就此路不通。。。

不过好像上面这种想法又回到了 scrapy 是如何实现并发的问题上了。。。。
dingyaguang117
2019-05-05 16:14:06 +08:00
@caneman 当然要保证 队列里有足够的 url 够下载器消费啦,你可以 按照某些固定的规则放进去,保证足够的数量就行了
你现在是每次队列里只有 1 个,你 100 个并发下载啥?
caneman
2019-05-05 16:18:45 +08:00
@dingyaguang117 谢谢,我觉得问题在这儿,但是这个规则怎么建立没想好,4000W 级别,还要涉及到失效错误链接的处理,请问 scrapy-redis 是不是能解决我的问题?
AlloVince
2019-05-05 17:02:54 +08:00
Scrapy 底层是 Twisted,Twisted 通过事件循环+线程池来实现异步 IO 的效果,LZ 所说的“并发数”,在 Scrapy 中是 CONCURRENT_REQUESTS, 其实只是传给 Twisted 的 Deferred 对象数量。由于 Twisted 只适用于单机环境,如果要增大 LZ 所说的“并发”数,可以调大 CONCURRENT_REQUESTS, 但显然“并发”数不可能无限增大,因为 Twisted 本身也存在限制

一方面 Twisted 本身有 Queue 和线程池,在 Scrapy 中可以通过设置 Twisted 的 REACTOR_THREADPOOL_MAXSIZE 增大线程池线程数。

另外 Twisted 主线程是单线程的,主线程达到瓶颈的话,再扩大线程池也没有意义。

因此你可以认为单机环境下 Scrapy 的瓶颈 == Twisted 主线程处理上限。
AlloVince
2019-05-05 17:14:51 +08:00
关于 4000W url 如何调用 scrapy 爬取的问题,简单说可以将已知的 url 构建为`Request`, 然后`Spider.parse_start_url()` 中 `yield Request` 即可,所有待处理的 Request 会存入 Scheduler,Scheduler 的数据都存在内存,可以提前评估一下内存是否够存放所有的 url。

scrapy-redis 实现的是将 Scheduler 的数据从内存改为 Redis, 一方面 redis 在进程崩溃后数据不会丢失,另一方面可以突破单机的限制,理论上有足够多的机器的话,再多的 URL 也可以同时请求。此时的瓶颈在 url -> Scheduler 生产者的生产速度
caneman
2019-05-05 17:26:03 +08:00
@AlloVince 非常感谢!您的这一番讲解能让我少走很多弯路,再次感谢!
renmu123
2019-05-05 22:22:32 +08:00
先把所有的 URL 爬到手,一些能看出规律的就手动构造,然后就多多进程 /多线程 /异步,随便玩
zhijiansha
2019-05-05 23:23:26 +08:00
@caneman #10 其实,上分布式,可以把 worker 都放在一台机子上的
caneman
2019-05-06 10:30:29 +08:00
@zhijiansha 这个思路挺好的,谢谢啊

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/561121

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX