单线程、多线程和协程的爬虫性能对比
作者:小小明
豆瓣深圳影讯爬虫
大家好,我是小小明。今天我要给大家分享的是如何爬取豆瓣上深圳近期即将上映的电影影讯,并分别用普通的单线程、多线程和协程来爬取,从而对比单线程、多线程和协程在网络爬虫中的性能。
具体要爬的网址是:https://movie.douban.com/cinema/later/shenzhen/
除了要爬入口页以外还需爬取每个电影的详情页,具体要爬取的结构信息如下:
爬取测试
下面我演示使用 xpath 解析数据。
入口页数据读取:
结果:
检查一下所需数据的 xpath:
可以看到每个电影信息都位于 id 为showing-soon
下面的 div 里面,再分别分析内部的电影名称、url 和想看人数所处的位置,于是可以写出如下代码:
结果:
然后再选择一个详情页的 url 进行测试,我选择了熊出没·狂野大陆这部电影,因为文本数据相对最复杂,也最具备代表性:
结果:
分析详情页结构:
文本信息都在这个位置中,下面我们直接提取这个 div 下面的所有文本节点:
结果:
为了阅读方便,拼接一下:
结果:
接下来就简单了:
结果:
可以看到成功的切割出了每一项。
下面根据上面的测试基础,我们完善整体的爬虫代码:
单线程爬虫
结果:
爬到的文件:
整体耗时:
42.5 秒。
多线程爬虫
单线程的爬取耗时还是挺长的,下面看看使用多线程的爬取效率:
结果:
耗时 8 秒。
由于每个子页面都是单独的线程爬取,每个线程几乎都是同时在工作,所以最终耗时仅取决于爬取最慢的子页面。
异步协程爬虫
由于我在 jupyter 中运行,为了使协程能够直接在 jupyter 中直接运行,所以我在代码中增加了下面两行代码,在普通编辑器里面可以去掉:
这个问题是因为 jupyter 所依赖的高版本 Tornado 存在 bug,将 Tornado 退回到低版本也可以解决这个问题。
下面我使用协程来完成这个需求的爬取:
结果:
耗时仅 7 秒,相对比多线程更快一点。
由于 request 库不支持协程,所以我使用了支持协程的 aiohttp 进行页面抓取。当然实际爬取的耗时还取绝于当时的网络,但整体来说,协程爬取会比多线程爬虫稍微快一些。
asyncio 知识点补充
asyncio.run(main())表示拿到 event loop,运行输入的 coro(main()函数),直到结束后关闭这个 event loop。
需要注意,asyncio.run() 在 Python3.7 以上版本才有,对于 python3.7 一下版本可以使用以下语句替换:
asyncio.create_task(coro)
,表示对输入的协程 coro 创建一个任务,安排它的执行,并返回此任务对象。这个函数也只能在 Python 3.7 以上版本使用,对于 3.7 以下版本可以用asyncio.ensure_future(coro)
替代。
回顾
今天我向你演示了,单线程爬虫、多线程爬虫和协程爬虫。可以看到一般情况下协程爬虫速度最快,多线程爬虫略慢一点,单线程爬虫则必须上一个页面爬取完成才能继续爬取。
但协程爬虫相对来说并不是那么好编写,数据抓取无法使用 request 库,只能使用 aiohttp。所以在实际编写爬虫时,我们一般都会使用多线程爬虫来提速,但必须注意的是网站都有 ip 访问频率限制,爬的过快可能会被封 ip,所以一般我们在提速的同时都会加上代理 ip 来并行爬取数据。
彩蛋:xpath+pandas 解析表格并提取 url
我们在深圳影讯的底部能够看到一个> 查看全部即将上映的影片的按钮,点进去能够看到一张完整近期上映电影的列表,发现这个列表是个 table 标签的数据:
那就简单了,解析 table 我们可能压根就不需要用 xpath,直接用 pandas 即可,但片名中包含的 url 地址还需解析,所以我采用 xpath+pandas 来解析这个网页,看看我的代码吧:
结果:
这样就能到了主页面的完整数据,再简单的处理一下即可。
结语
感谢各位读者的鉴赏,有什么想法和收获欢迎留言评论噢!
版权声明: 本文为 InfoQ 作者【小小明】的原创文章。
原文链接:【http://xie.infoq.cn/article/2bba3dea47eef735111105966】。文章转载请联系作者。
评论