欢迎来到爱学习爱分享,在这里,你会找到许多有趣的技术 : )

Scrapy VS Golang 爬虫对比

开发者头条 5783℃

前言

之前写了一篇scrapy和golang爬虫性能对比,引起了很大的争议(就是被各位大佬喷的很惨的意思)。其中,很多人提了数据库读写的问题,看到大家的评论后不久我又测了一下,把写数据库的那部分代码注释掉后,速度瞬间就快了。当时由于没时间详细的测试,就把文章撤了下来,最近我抽时间吧代码整了一下,有了个新的测试,确实是数据的问题,各位可以看看。

上一篇文章的情况

爬虫的来历是因为团队需要一批公司名称的语料,先是用scrapy搭建了一个爬虫,后面又抱着一种试一试的心态用golang复现了一个同样的爬虫,对比了一下爬取速度,scrapy版本的和golang版本的最终差距很大,当时就很奇怪,做了个记录发到知乎上来了,当时的情况对比如下。

我详细的说明一下情况,线程池方面,scrapy底层用的twisted,默认是16个concurrent requests,至于这里是线程实现还是协程实现我就不计较了。golang是goroutine,是golang自己实现的一个东西。在爬取约650w完整数据集的时候,scrapy是采用默认设置,golang是自己实现了一个job/worker模型,设了个20个worker,相当于一个worker用一个goroutine,代码放在gayhub上。后来我又取了个较小的数据量,大约100页,即2.5w行数据,然后将scarpy和golang的worker数都设为16对比了一下,这就是这四列数据的来历。

另外,还需要说明一下爬取的目标,爬取目标是一个工商登记企业汇总的网站,这个网站基本没有反爬措施,而且网站的html也写的挺随心所欲的,不知道是自动生成的还是作者随便写写。最重要的一点是,可以通过URL翻页拼接的方式去遍历全网的数据,是难得的适合爬虫爬取的网站。当然,有一次大批量高速的爬取后第二天那台机器被该网站封ip了,估计是触发了反爬机制吧,但过段时间就解除了,基本上等于没有反爬机制。

上一篇文章的结论

1. golang的线程调度需要手动去写,不像scrapy是框架自身配置好了的,但是由于golang语法里就集成了gorountine和channel,如果是一个熟练工来说,写起来一点也不费事。

2. golang的异常处理比python要啰嗦很多,整个代码写下来浓浓的c风格。

3. golang已经有相当好用的爬虫库了,可以看看htmlquery,从请求网页到用xpath解析出结果也就是几句话的事情。

4. scrapy的接口风格每隔一段时间都会变一次,对于我这种不是专门做爬虫的,每次要用到

scrapy的时候都得重新看文档,这也是一个意想不到的耗时。

5. scrapy的使用是面向对象的设计,使用时需要去继承一个scrapy的类,然后往这个框架回调的方法里填自己想要做的事,嗯,这很面向对象…

6. scrapy也不是完全没有优势,断线重连,配置代理,爬取个数和时间的统计等等一些细节方面占优势,毕竟是个老牌的爬虫框架了。

总结一下就是,golang由于并发的语法设计加成,手动构建一个调度模型并不花时间。而scrapy

虽然把这些都集成好了,但由于其架构设计,在使用上并不是那么直观,其实也并没有省下码代码的时间。

更新后的情况

这几天我抽时间重新写了scrapy的数据库写入的pipline。由于网络等其他原因,100pages端到端的分行写入的时间这次是9min25s,在同样的条件下,分页写入只要9s,说明数据库写入才是瓶颈。这里说明一下分页写入和分行写入,分行写入指的是爬下来一页,然后将250条公司名称一行一行写入数据库,每写一行commit一次,分页写入指的自然就是250条一一起commit,为了方便对比,代码如下

...
class PyGetManyPipeline(object):

    def open_spider(self, spider):
        engine = create_engine(MYSQL_HANDER)
        self.session = sessionmaker(bind=engine)

    def process_item(self, item, spider):
        session = self.session()
        for data in item['data']:
            id, content, sub = int(data['id']), data['content'], data['sub']
            line = Company(id=id, content=content, sub=sub)
            session.add(line)
            # session.commit() # 9min25s for 100 pages
        session.commit() # 9s for 100 pages
        return item

可以看倒数第三行和倒数第二行,在for循环内commit时,耗时是9min25s,在循环外commit的耗时是9s。

另外,golang的代码没有改,仍然是分行写入,可能由于golang语言多线程的原因和sqlalchemy 事务的原因,仍然快不少,相信如果改成分页写入,再做下优化,应该不会比scrapy慢,但这里不是重点,就不做过多衍生了。代码也放在gayhub上了,大家可以把数据库改成自己的研究一下。

更新后的总结

首先是感谢大家给我的建议,讲道理,数据库的读写也是基本功了,我也不是不知道这一点,主要是当时写代码的时候没注意,而且也不是一次性写完的。开始跑的时候已经忘了数据库的问题,后来写完golang的版本后一看速度差别这么大,就想当然的以为是语言的问题。

但是,上一篇的结论中,python在性能这块是逃过一劫,但另外说到的开发效率上,python并不乐观了。至少,在8102年,“人生苦短,我用XX。”这句话已经不是python的专利,可以有别的选择。

致谢

@BuGoNee @十四君 @黄超平的 @许怀远 @混沌的鳄鱼

不完全统计,以上几位在上篇文章的评论区提到的数据库等问题比较切中要害,在此致谢。至于评论没法恢复,这里不详细说明了。还有其他人提到的各种建议(DA LIAN),就不一一解释了。

python杂七杂八的使用经验

转载请注明:爱学习爱分享 » Scrapy VS Golang 爬虫对比

喜欢 (4)or分享 (0)