不完美的紧急需求(上篇)
昨天从志强老哥那里接到了一个小的爬虫需求。
说是小需求,是还是有点虚的,然后,在我面试完,吃完饭,锻炼完,摸鱼完之后,我终于打开了宇宙第一IDE vscode,新建文件夹了。
在做了在做了.jpg
需求要点有两个:
1.根据wos上的检索结果下载paper的本体(pdf),并按照文献的顺序进行命名。
2.按照一定的要求,获取paper的信息,特别是摘要,然后合并成一个word的报告。
首先,需求明确,第一个需求
“wos的检索结果”长什么样
利用什么东西下载文献
怎么下载,怎么批量下载
异常处理,下载不下来,访问不到等怎么处理
第二个需求:
paper的哪些信息
从哪里获取paper的这些信息
报告的模板是什么样的
怎么生成结果信息,又怎么从网页信息做成报告文档?
首先,先看最原始的数据,wos的检索结果,这里给出我删掉了筛选条件后的结果表:
number | PMID | Pubmed | title | authors | Journal | year | DOI |
---|---|---|---|---|---|---|---|
字段太长了就不展示,但是目前,从里面我们知道了一件事。
这里面没有我们最想要的下载链接,由于访问权限问题,wos能否导出下载连接我也无法确认,但是,至少这里面还有一个东西能实现这件事。
DOI号,理论上,通过这个编号,我们就一定能找到这篇文章,但是显然,杂志社千千万,我们不可能还要去各个杂志社的网站去下载pdf,那显然太复杂了。
所以我准备利用sci-hub,或者pubmed,两相权衡,我决定用sci-hub
这里就能看出sci-hub设计的强大了:
如果一个doi在sci-hub里没有,会直接返回无响应的状态码,这样我们就能够非常方便地用try…catch来控制在无文献时候的处理;
它没有多余的动态跳转,所有能下载的文献链接格式统一,相比之下,虽然pubmed访问文献的链接是统一的,但是下载仍然采用了动态生成的pdf链接,这对于批量下载极为不利,除非我们实现准备了杂志社的下载页面对照表(貌似这可以做啊)
它不需要什么特殊的访问权限,只要能访问互联网,而且不要大批量并发去抓取文献(类似ddos),基本都能够从上面下载文献,由此可见,这才是真正的remove barriers….相比之下NCBI这些都弱爆了…
虽然但是,我也发现,访问统一的NCBI-Pubmed页面,居然有我们需要的Abstract字段….
那么第一个需求的基本流程应该就厘清楚了:
使用sci-hub作为下载服务器-利用doi获得完整的下载链接-下载pdf。
首先目前可用的sci-hub地址需要自行查找,我这边貌似用的是sci-hub.st;
其二,需要观察sci-hub下载链接是真么样的,一个典型的例子:
https://sci-hub.yncjkj.com/10.1007/s00204-016-1824-6#
这里能很简单看出组成:
sci-hub-url+doi+”#”
这就非常合理了。我们能够用字符串的拼接很容易地将所有doi编号组合成下载地址;
其三,怎么访问这个地址,能够拿到得到web访问的返回,也就是pdf?那简单,在拼接好了完整的下载地址之后,肯定是requests.get即可.
这里介绍一个简单的小工具postman,可以很容易地看到返回的是什么。
那么我们把上面那个链接用get方法送过去,看看返回什么。
首先,最重要的,status code,由于上面的链接是已经确认sci-hub中存在的paper,所以status=200,这也是通用的成功get的返回状态。
其次,我们得到了一个看上去像是文本文档的东西,其实就是html页面了,这个页面如果由浏览器来渲染,那就是我们常见的网页,但这里我们不关心页面,我们只关心信息。
那么问题来了,为什么不是直接返回一个pdf文件?如果上面那个是下载链接,应该直接返回一个pdf文件不是么,但为什么仍然是一个html页面?因此,上面那个链接,只是能够访问到包含了真实下载链接在哪的一个网页,我们得自己找到真正的下载链接在哪里。
根据pastman返回的网页,按照最常见的想法,我们最后下载的东西是pdf,那么无论是html标签,或者是下载链接,里面肯定是带了pdf三个词的,所以直接全局搜索,搜索得到了两个:
1 | <div id="buttons"> |
这两个长得都挺像下载链接,那么直接用postman再get一下就行了,最后发现第二个是真正的下载链接,只需要对它进行get方法,就能直接收到pdf文件,那么基本锁定了我们的目标,那么我们只需要根据它的div或者字段拿到就行,注意看这里这个字段是包裹在什么html标签中,往外看一层,它的标签是:
1 | <div id="article"> |
那么很清楚的,这里是包在id为article的css层(或者干脆点就叫DIV元素)里面,里面又有一个类型为type,文本内容为src,id为pdf的css嵌入对象,我们就是要拿到这个嵌入对象,既然说了这是css嵌入层,那么我们就可以用css嵌入层选取,然后构建下载链接的对象,再用get进行请求就能下载到pdf的二进制文件,然后开一个文件写进去就行,那么综上,整个代码应该差不多是这个样子:
1 | import requests |
这个代码非常简单,也非常脆弱,比如下载期间的断线等异常情况没有处理,爬虫没有做随机时间设置,容易被封ip,对下载不了的文献也只是简单处理了一下,但没有记录失败原因,因为有可能并不是因为没有文章而失败,也可能是因为网络波动导致失败;
但是,至少这个代码能成功运行下载到这些文献。只需要提供一个doi的文件即可。
在这个脚本中,额外生成了一个error.txt,里面会记录哪些paper,此外,为了下一步进行命名,我们将下载失败的文章也创建了一个txt文件。(当然其实从后面的脚本来看,也可以不需要创建新的txt,可能只是为了好看。)
拿到paper之后,重命名就容易多了,就按照匹配规则,直接批量给某个文件夹的文件命名罢了。
1 | import os |
这里因为是特殊需要,要命名成论文的顺序.pdf/txt,这里可以通过替换namesheet.xlsx这个文件中的键值对实现任意的命名。当然,目前的脚本有改善的余地,比如既然命名规则是固定的,那么就不需要额外创造这个namesheet文件,直接用上面输出pdf时候的doi组合成名字,然后直接用字典存键值对就行,甚至也可以在上面输出pdf的时候加上循环,完成从下载到命名一步操作。
ok,到这里为止,第一个需求基本解决了,第二个需求稍微复杂一点,但实现思路类似,留在下一篇博客来讲吧。