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

4926 次点击
所在节点    Python
27 条回复
caneman
2019-05-06 10:34:07 +08:00
@renmu123 现在能得到所有的 url 了,我想着怎么能用 scrapy 高效抓取,scrapy 这么多年了 这样一个成熟的框架应该不至于解决不了这种问题。想先单机把 scrapy 性能发挥到极致,了解他的极限和瓶颈在哪里,然后再上分布式再接着进一步优化,计划的学习路线是这样的。
rocketman13
2019-05-06 11:45:04 +08:00
scrapy 的 CrawlSpider 类?
cxh116
2019-05-06 15:50:16 +08:00
数据是怎么保存的? 用的是同步还是异步调用.在 pipline 用同步阻塞方式去保存数据的话,会阻塞整个抓取调度的.

https://leehodgkinson.com/blog/scrapy-pipelines/
caneman
2019-05-06 17:54:27 +08:00
@cxh116 是采用的异步 MySQL 存储的,很多页面是空数据的,所以瓶颈不在存储这一块,下面是主要代码。


def start_requests(self):
url = 'https://www.xxxx.com/'
longitude, latitude = get_next_coordinate( self.start_longitude, self.start_latitude)
data = get_form(longitude, latitude)
proxy = 'http://' + get_proxy()
yield FormRequest(url, method='POST', formdata=data, callback=self.parse, dont_filter=True, meta={'proxy':proxy,'download_timeout':3,'longitude':data['longitude'], 'latitude':data['latitude']})

def parse(self, response):
info_list = json.loads(response.text)
if info_list['Count']:
for item in info_list['list']:
item_loader = QiyeItemloader(item=QiyeItem())
item_loader.add_value('hash', item['Key'])
item_loader.add_value('name', item['Name'])
item_loader.add_value('longitude', response.meta['longitude'])
item_loader.add_value('latitude', response.meta['latitude'])
qiye_item= item_loader.load_item()
yield qiye_item
longitude, latitude = get_next_coordinate(response.meta['longitude'], response.meta['latitude'])
next_data = get_form(longitude, latitude)
yield FormRequest(response.url, method='POST', formdata = next_data, callback=self.parse, dont_filter=True, meta={'proxy':response.meta['proxy'],'download_timeout':3,'longitude':next_data['longitude'], 'latitude':next_data['latitude']})

我想的一种解决方案是把所有 URL 放在 redis 里面,然后在 start_requests 里面 while True:yield Request()
这样的问题我不知道我这样一直写会不会时间长了我的电脑就崩了。
我如何控制这个被 yield 的 Request 的数量?比如,在队列里面一直有 100 个 Request,每少一个就添一个,始终保持 Start_url 里面有 100 个待爬 URL,这样的情况下,我调 CONCURRENT_REQUESTS 的值,是不是就能真正的控制并发数了?
caneman
2019-05-06 18:02:54 +08:00
cxh116
2019-05-06 20:14:19 +08:00
@caneman 你得确认瓶颈在什么地方?
假如网页通过代理访问,60 秒才返回一个页面.这样就算你 1000 个并发. 1000 / 60 = 16.6 .这样算每秒最多也就是 16 个而已.

假如网页解析比较费时,这个问题就更加不好解决.因为毕竟这种类似于阻塞的调用.


你可以登录 telnet 用 est() 查看一下状态,分析一下原因 https://docs.scrapy.org/en/latest/topics/telnetconsole.html
可以看一下 engine.scraper.slot.queue 的实现,这里应该可以取到你要的队列大小值.

你还可以尝试用你自己的 redis 这种方案,启用多个进程,看看有没有提升.
caneman
2019-05-06 20:41:48 +08:00
@cxh116 谢谢,代理好像不是瓶颈,不加代理提升的速率也非常有限(大概就是去除了代理延迟级别的速度提升) redis 的那种方案确实提高了速率,是我之前写法太蠢了,所有的下一个页面链接都得等我上一个页面请求完毕才能获取,生生的变成了同步。(可是书上和网上都是这样来写的啊,寻找下一页的链接然后 yield ),不知道是我的理解问题,还是这样写本身就存在这种问题,我再多尝试尝试改一改,谢谢啦🙏。

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

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

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

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

© 2021 V2EX