Linux 集群上文献信息的爬虫和下载

写在开头:

几年的 科研工作 搬砖经历,不得不和文献打交道,不论是自己读还是做成文档给别人讲,一般都会需要获取文献某些信息如题目,杂志名称,影响因子,一作,通讯以及下载文献原文等,而常规(以往的自己)的做法是,搜索 - 复制 - 粘贴 - 下载(以及 for 循环这个过程),其实很烦很枯燥很无聊很费时间(那你以前是怎么忍下来的…),应该把这部分时间节省下来做其他重要的事情,比如睡觉和发呆 +.+

最近因为工作的刚需以及加上同事的鞭策,我写了一个程序,只需要输入 DOI 就可以做到上边提到的事情,两个脚本:

  1. advanced_paper_informational.py, 用来在 Linux 环境下提取文献信息;
  2. scihub.py,用来下载文献;

1. advanced_paper_informational.py

这个脚本需要两个输入文件:

  1. IF_2019.txt 影响因子文件,每年都会更新,以后下载更新即可;
  2. J_Medline.txt,NCBI 的杂志缩写和全称对应文件;

一些软件和相应的库:

  1. python3 以及相应的库包括 selenium; bs4;
  2. PhantomJS,在 linux 用来模拟浏览器,我本来想用 chromedriver 来着,结果没有在集群上安装成功;

这些东西安装成功以后需要添加至环境变量。

成功以后你可以用单独的 DOI 或者 DOI 的文件(换行符分割)批量查询。

这个脚本的原理简述如下:根据文献的 DOI(唯一标识),使用脚本模拟浏览器去 PubMed 检索,根据检索结果提取信息即可,下边是代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
from selenium import webdriver
from bs4 import BeautifulSoup
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
import time,os,re

# set pars
import argparse,re,os,math,glob
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description='''
-------------------
Simple Introduction:
Crawl the information of paper... You should provide the DOI or a file for DOI (line breaks).
Example: python3 advanced_paper_informational.py -l 10.1016/S0140-6736(19)32319-0
python3 advanced_paper_informational.py -f doi_file
To be continued.
------------------'''
)
parser.add_argument('-l','--onedoi', nargs='?', help = "DOI information.")
parser.add_argument('-f','--filelist',nargs='?', help = "the DOI information list file.")
parser.add_argument("-v", "--version",action='version', version='%(prog)s 1.0')
args = parser.parse_args()
var = args.onedoi
files = args.filelist

## define the user(linux) and abbreviatio for print
import re
Dict = {'zhangSan':'ZS', 'LiSi':'LS'}
import os
user_full = os.popen('whoami').readlines()[0].strip()
if user_full in Dict:
user = Dict[user_full]
else:
user = user_full

# doi information
doi_list = list()
if var:
doi_list.append('DOI: ' + var)
if files:
with open(files, 'r') as IN:
Input = IN.readlines()
doi_list = ['DOI: ' + x.strip() for x in Input]

# out file
out = open('paper_information.xls', 'w')

## scraping
print(user_full + ' is crawling... \nThe warning information appeared later does not matter. \nIt may need some time, please wait patiently:)\nIf there is no output for a long long time, you should stop it and try to run again.\n')

browser = webdriver.PhantomJS() # 因为集群环境我只能用 PhantomJS。
url = 'https://pubmed.ncbi.nlm.nih.gov/'

# 杂志名称全称
j_name = dict()
with open('files/J_Medline.txt', 'r') as IN:
for line in IN:
line = line.strip('\n')
if line.startswith('JournalTitle'):
if re.search(' \(.*\)', line):
match = re.search('JournalTitle: (.*) \(.*\)', line)
else:
match = re.search('JournalTitle: (.*)', line) # ncbi 是缩写,然后影响因子是全称,所以得找到这个信息
#发现有带(London, England)这种信息的。。。。
full = match.group(1)
if line.startswith('MedAbbr'):
match = re.search('MedAbbr: (.*)', line)
abbr = match.group(1)
if line.startswith('NlmId'):
j_name[abbr] = full

# 杂志 IF
import pandas as pd
dt = pd.read_table('files/IF_2019.txt', index_col = 1)
IF_dict = dt['Journal Impact Factor'].to_dict()

for x in doi_list:
DOI = x
browser.get(url)
print("\nThe pubmed url is opening correctly.\n")
time.sleep(3)
try:
browser.find_element_by_xpath('//*[@name="term"]').send_keys(x)
time.sleep(2)
except NoSuchElementException:
print('AO, something wrong...')

browser.find_element_by_xpath('//*[@class="search-btn"]').click()
time.sleep(2)
print("\nThe page for paper " + x + " is opiening correctly.\n")
soup = BeautifulSoup(browser.page_source, "html.parser")

# journal name abbr
journal = soup.find(id = "full-view-journal-trigger").get_text().strip()

# title
title = soup.find(class_ = "heading-title").get_text().strip()

# IF
for x in IF_dict:
match = re.search('^' + j_name[journal] + '$', x, flags=re.IGNORECASE) # 有的名字包含其他杂志的全称...
if match:
IF = IF_dict[x]
journal_name = j_name[journal]
else:
match = re.search('^' + j_name[journal].replace('.','') + '$', x, flags=re.IGNORECASE) # 有的杂志匹配出来的全称多了个点:Nature reviews. Immunology
if match:
journal_name = j_name[journal].replace('.','')
IF = IF_dict[x]


# 发表时间
if soup.find(class_ = "secondary-date"):
p_time = soup.find(class_ = "secondary-date").get_text().strip().strip('Epub ').strip('.')
else:
p_time = soup.find(class_ = "cit").get_text().split(";")[0]

# PMID
PMID = soup.find(class_ = "current-id").get_text()


#原文链接
doi_info = soup.find(class_ = "identifier doi")
http = doi_info.find(class_ = "id-link")['href'] # 增加这一步是因为偶尔会出现 NCBI 的链接

# 一作和通讯
authors = soup.find(class_ = "authors-list").get_text().strip().replace(u'\xa0', '').replace(u'\xa0', '').replace(' ', '')
author_list = re.sub('\n\w*', '', authors).split(',')
first_author = author_list[0]
corresponding_author = author_list[-1]

# 第一单位
affiliations = soup.find(class_ = "affiliations").get_text().strip()
affiliations = re.sub('[ ]+', ' ', affiliations)
affiliations_list = re.sub('[\n]{2,}', '', affiliations).split('\n')
first_affiliation = affiliations_list[1].lstrip(' 0123456789')

line = '\t'.join([user, title, journal_name, p_time.replace('.', ''), PMID, DOI, http, IF, first_author, corresponding_author, first_affiliation])
print(line, file = out)
print('\n' + line + '\n')

out.close()
print("\nDone!, the output is paper_information.xls.\n")

2. scihub.py

写完上边这个脚本以后,我就在想:都到这一步了,为啥不临门一脚把文献下载也给搞定了呢? 于是 Google 检索了下,发现 Github 上竟然有现成的,本着不重复造轮子的偷懒精神,我就拿过来稍微改动了下,因为不是我写的,就不好意思贴代码了,改动的地方有两个:

  1. 代码中一个用来爬取可用的 sci-hub 的网址失效了,我替换了一个新的;
  2. 以及去爬这个新的网址的时候,有时候会打不开,我增加了失败后重复爬取的功能,免得需要重复运行;

这个脚本需要软件和相应的库:

  1. Python3 以及 requests, retrying, pysocks, bs4;

这个脚本还有利用谷歌学术查询文献的功能,不过我暂时还没用到,只用到了下载的功能,目前支持每次下载一个文献,等我后边有时间再修改一下。

下载的原理简述: 以 DOI 为例,程序得到一个 DOI 后,去寻找目前所有可用 sci-hub 网址,然后利用 sci-hub 网址 + DOI 得到相应的原文网页,如果是判断格式为 PDF 的话就直接保存。

Paper_information.sh

这个脚本用来整合的,因为不想单独运行两个脚本。

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/bash

# doi
python3 advanced_paper_informational.py -l "10.1016/S0140-6736(19)32319-0"

# doi file, a list of doi, line break
#python3 advanced_paper_informational.py -f doilist.txt

# dowload the pdf, only support one doi in current version +.+, TBD
python3 scihub.py -d "10.1016/S0140-6736(19)32319-0"

参考链接

  1. Bioinformatics/README.md at master · Nonewood/Bioinformatics · GitHub
  2. scihub.py/scihub.py at master · zaytoun/scihub.py · GitHub
  3. Linux下Python3环境安装selenium跟phantomjs_徐代龙的技术专栏-CSDN博客_linuxpythonphantomjs
  4. python的retrying库处理尝试多次请求 - 简书
  5. 【Python3.6爬虫学习记录】(七)使用Selenium+ChromeDriver爬取知乎某问题的回答_子耶-CSDN博客_chromedriver爬知乎
  6. python - Selenium testing without browser - Stack Overflow
  7. 实战(二)轻松使用requests库和beautifulsoup爬链接 - 掘金

写在最后

  1. 写这个程序写得很开心 🥳;
  2. 脚本还很粗糙,使用过程中可能会遇到问题,后边会在 Giuhub 修改;
  3. 我只在 Linux 上做了测试,是可行的,其他的不一定,我有时间的话,会把非 Linux 的倒腾一下,然后放出来,不过得看时间;
  4. 本来还想尝试可视化,比如弄个网站或者小程序什么的,结果发现还是挺复杂的,暂时放弃,后边看情况吧;
(✪ω✪)