起因:
这项工作的源起是因为我的一个老同事在疫情期间找到我,说以前的对于KEGG Mapper(https://www.genome.jp/kegg/tool/map_pathway2.html)的爬虫脚本已经无法使用了。我检查了下,发现是KEGG Mapper的页面做了微调,原来的脚本用的是简单的正则进行匹配,无法再适应现在的KEGG Mapper页面。我当时只给了他一个权宜的解决办法。年后我跟着学崔庆才的网课又重学了一遍爬虫,于是我就花了一个周末又把这个脚本又重写了一遍。
脚本爬取目标:
KEGG Mapper简单来说是KEGG的一个软件,它基于KEGG,将基因批量注释到KEGG通路,并获得彩色的通路图。(详细的介绍请参考其他资料)
这个脚本的目标就是通过上传如下文件,选择所属物种的拉丁文三字码后,返回搜索结果,爬取搜索结果里的每个pathway连接里的图。
实现思路:
原来的脚本的实现方法为读取所需上传的文件和相关参数,直接上传到KEGG Mapper的API上,用正则表达式去匹配返回结果的一个个条目,再进入条目中的连接,用正则表达式匹配图片所在连接,依次下载保存。
这个脚本的问题在于:1. 使用正则表达式来匹配网页源码的节点。网页一旦有微调就要重写正则表达式。2. 由于KEGG服务器在国外,下载时经常会有网络阻塞,我们请求网页之后需要等待页面响应并返回结果,如果没返回结果,程序会一直等着。由于是顺次下载每个条目里的链接,每个链接等待的时间累积下来会很可观。
我现在使用Python3.7的aiohttp和selenium重写这个脚本。成功解决了以上两个问题。selenium利用WebDriver驱动浏览器执行特定的动作,如点击、下拉等操作,同时还可以获取浏览器当前呈现的页面源代码,做到可见即可爬。而aiohttp将返回的搜索结果里的链接进行异步爬取。即对每个网页提交request后,不用等response,而去提交下一个网页的request。这样理论上就大大节约了等待时间。
另外,由于 aiohttp 可以支持非常大的并发,比如上万、十万、百万都是能做到的,但这么大的并发量,目标网站是很可能在短时间内无法响应的,而且很可能瞬时间将目标网站爬挂掉。所以我用aiohttp的semaphore控制了下并发的数量。最后,我用之前我自建的代理池(主要是来自于免费代理)给这个脚本加上了代理,防止被网站封IP。
基本配置:
上传文件和修改参数:
上传文件和修改参数主要是通过selenium来实现的。上传文件主要通过XPATH路径找到input标签来实现。修改参数主要通过selenium执行JavaScript执行语句来实现。
异步爬取下载图片:
这里主要是通过get_content这个函数来实现文件的异步下载。为了控制异步下载的并发数,我这里用semaphore的数目来控制。
添加代理池:
这里的代理池我是写在另一个项目上。代理池的主要实现方法是通过爬虫抓取免费代理IP,存储在本地Redis库,并通过对每个代理IP不断提交请求,从而对库里的所有代理IP进行打分,将打分过低的IP会剔除出库。然后从库里随机取一个可用的代理IP放在flask构建的web API上。最后通过get_proxy这个函数从网页上获取随机的代理IP。
执行异步爬取:
走过的一些弯路和个人思考:
之前我有想过用scrapy框架去实现这个爬虫,但是我花了很长时间都没有找到scrapy框架upload文件的实例。未来学习完scrapy框架的话我可能会用scrapy框架把这个脚本再重写一遍。
本来想连网页也一起爬取下来,但由于单个pathway网页的结构也改变了,所以也就没有爬取。
这里我提交请求是timeout写的是None,也就是无限等待时间,但事实上,设置为10min比较好,有些图片就是很难爬取,所以等待时间会很长,这样异步也就失去了意义。
我构建的代理池由于是通过免费代理构建的,所以不稳定,不是很建议用这样的代理IP来抓取网络阻塞比较严重的资源。
完整代码连接:https://github.com/yiyinzhang/scrapeKEGGMapper