超好用的Scihub文獻(xiàn)下載工具又雙叒叕更新了

2020-05-28補(bǔ)充:已用最新的scihub提取網(wǎng),目前項(xiàng)目可用,感謝@lisenjor的分享。
2020-06-25補(bǔ)充:增加關(guān)鍵詞搜索,批量下載論文功能。
2021-01-07補(bǔ)充:增加異步下載方式,加快下載速度;加強(qiáng)下載穩(wěn)定性,不再出現(xiàn)文件損壞的情況。
2021-04-08補(bǔ)充:由于sciencedirect增加了機(jī)器人檢驗(yàn),現(xiàn)在搜索下載功能需要先在HEADERS中填入Cookie才可爬取,詳見(jiàn)第4步。
2021-04-25補(bǔ)充:搜索下載增加百度學(xué)術(shù)、publons渠道。
2021-08-10補(bǔ)充:修復(fù)scihub頁(yè)面結(jié)構(gòu)變化導(dǎo)致無(wú)法下載的問(wèn)題,增加DOI下載函數(shù)。
本文完整源代碼可在 GitHub 找到:
https://github.com/Ckend/scihub-cn
1. 什么是Scihub
首先給大家介紹一下Sci-hub這個(gè)線上數(shù)據(jù)庫(kù),這個(gè)數(shù)據(jù)庫(kù)提供了約8千萬(wàn)篇科學(xué)學(xué)術(shù)論文和文章下載。由一名叫亞歷珊卓·艾爾巴金的研究生建立,她過(guò)去在哈佛大學(xué)從事研究時(shí)發(fā)現(xiàn)支付所需要的數(shù)百篇論文的費(fèi)用實(shí)在是太高了,因此就萌生了創(chuàng)建這個(gè)網(wǎng)站,讓更多人獲得知識(shí)的想法

后來(lái),這個(gè)網(wǎng)站越來(lái)越出名,逐漸地在更多地國(guó)家如印度、印度尼西亞、中國(guó)、俄羅斯等國(guó)家盛行,并成功地和一些組織合作,共同維護(hù)和運(yùn)營(yíng)這個(gè)網(wǎng)站。到了2017年的時(shí)候,網(wǎng)站上已有81600000篇學(xué)術(shù)論文,占到了所有學(xué)術(shù)論文的69%,基本滿足大部分論文的需求,而剩下的31%是研究者不想獲取的論文。
2. 為什么我們需要用Python工具下載
在起初,這個(gè)網(wǎng)站是所有人都能夠訪問(wèn)的,但是隨著其知名度的提升,越來(lái)越多的出版社盯上了他們,在2015年時(shí)被美國(guó)法院封禁后其在美國(guó)的服務(wù)器便無(wú)法被繼續(xù)訪問(wèn),因此從那個(gè)時(shí)候開(kāi)始,他們就跟出版社們打起了游擊戰(zhàn)
游擊戰(zhàn)的缺點(diǎn)就是導(dǎo)致scihub的地址需要經(jīng)常更換,所以我們沒(méi)辦法準(zhǔn)確地一直使用某一個(gè)地址訪問(wèn)這個(gè)數(shù)據(jù)庫(kù)。當(dāng)然也有一些別的方法可讓我們長(zhǎng)時(shí)間訪問(wèn)這個(gè)網(wǎng)站,比如說(shuō)修改DNS,修改hosts文件,不過(guò)這些方法不僅麻煩,而且也不是長(zhǎng)久之計(jì),還是存在失效的可能的。
3. 新姿勢(shì):用Python寫(xiě)好的API工具超方便下載論文
這是一個(gè)來(lái)自github的開(kāi)源非官方API工具,下載地址為:
https://github.com/zaytoun/scihub.py
但由于作者長(zhǎng)久不更新,原始的下載工具已經(jīng)無(wú)法使用,Python實(shí)用寶典修改了作者的源代碼,適配了中文環(huán)境的下載器,并添加了異步批量下載等方法:
https://github.com/Ckend/scihub-cn
歡迎給我一個(gè)Star,鼓勵(lì)我繼續(xù)維護(hù)這個(gè)倉(cāng)庫(kù)。如果你訪問(wèn)不了GitHub,請(qǐng)?jiān)?Python實(shí)用寶典 公眾號(hào)后臺(tái)回復(fù) scihub,下載最新可用代碼。
解壓下載的壓縮包后,使用CMD/Terminal進(jìn)入這個(gè)文件夾,輸入以下命令(默認(rèn)你已經(jīng)安裝好了Python)安裝依賴:
pip install -r requirements.txt然后我們就可以準(zhǔn)備開(kāi)始使用啦!
這個(gè)工具使用起來(lái)非常簡(jiǎn)單,有兩種方式,第一種方式你可以先在 Google 學(xué)術(shù)(搜索到論文的網(wǎng)址即可)或ieee上找到你需要的論文,復(fù)制論文網(wǎng)址如:
https://ieeexplore.ieee.org/document/26502

ieee文章
然后在scihub-cn文件夾里新建一個(gè)文件叫 my_test.py 輸入以下代碼:
from scihub import SciHub
sh = SciHub()
# 第一個(gè)參數(shù)輸入論文的網(wǎng)站地址
# path: 文件保存路徑
result = sh.download('https://ieeexplore.ieee.org/document/26502', path='paper.pdf')進(jìn)入該文件夾后在cmd/terminal中運(yùn)行:
python my_test.py你就會(huì)發(fā)現(xiàn)文件成功下載到你的當(dāng)前目錄啦,名字為paper.pdf
如果不行,有可能是網(wǎng)絡(luò)問(wèn)題,多試幾次。實(shí)在不行可以在下方留言區(qū)詢問(wèn)哦。
上述是第一種下載方式,第二種方式你可以通過(guò)在知網(wǎng)或者百度學(xué)術(shù)上搜索論文拿到DOI號(hào)進(jìn)行下載,比如:

將DOI號(hào)填入download函數(shù)中:
from scihub import SciHub
sh = SciHub()
result = sh.download('10.1016/j.compeleceng.2020.106640', path='paper2.pdf')下載完成后就會(huì)在文件夾中出現(xiàn)該文獻(xiàn):

除了這種最簡(jiǎn)單的方式,我們還提供了 論文關(guān)鍵詞搜索批量下載 及 論文關(guān)鍵詞批量異步下載 兩種高級(jí)的下載方法。
我們?cè)谙挛膶?huì)詳細(xì)地講解這兩種方法的使用,大家可以看項(xiàng)目?jī)?nèi)的 test.py 文件,你可以了解到論文搜索批量下載的方法。
進(jìn)一步的高級(jí)方法在download.py 中可以找到,它可以實(shí)現(xiàn)論文搜索批量異步下載,大大加快下載速度。具體實(shí)現(xiàn)請(qǐng)看后文。
4. 基于關(guān)鍵詞的論文批量下載
支持使用搜索的形式批量下載論文,比如說(shuō)搜索關(guān)鍵詞 量化投資(quant):
from scihub import SciHub
sh = SciHub()
# 搜索詞
keywords = "quant"
# 搜索該關(guān)鍵詞相關(guān)的論文,limit為篇數(shù)
result = sh.search(keywords, limit=10)
print(result)
for index, paper in enumerate(result.get("papers", [])):
# 批量下載這些論文
sh.download(paper["doi"], path=f"files/{keywords.replace(' ', '_')}_{index}.pdf")
2021-04-25 更新:
由于讀者們覺(jué)得Sciencedirect的搜索實(shí)在太難用了,加上Sciencedirect現(xiàn)在必須要使用Cookie才能正常下載,因此我新增了百度學(xué)術(shù)和publons這2個(gè)檢索渠道。
由于 Web of Science 有權(quán)限限制,很遺憾我們無(wú)法直接使用它來(lái)檢索,不過(guò)百度學(xué)術(shù)作為一個(gè)替代方案也是非常不錯(cuò)的。
現(xiàn)在默認(rèn)的 search 函數(shù)調(diào)用了百度學(xué)術(shù)的接口進(jìn)行搜索,大家不需要配置任何東西,只需要拉一下最新的代碼,使用上述例子中的代碼就可以正常搜索下載論文。
其他兩個(gè)渠道的使用方式如下:
sciencedirect渠道:
由于 sciencedirect 加強(qiáng)了他們的爬蟲(chóng)防護(hù)能力,增加了機(jī)器人校驗(yàn)機(jī)制,所以現(xiàn)在必須在HEADER中填入Cookie才能進(jìn)行爬取。
操作如下:
1.獲取Cookie

2.使用sciencedirect搜索時(shí),需要用 search_by_science_direct 函數(shù),并將cookie作為參數(shù)之一傳入:
from scihub import SciHub
sh = SciHub()
# 搜索詞
keywords = "quant"
# 搜索該關(guān)鍵詞相關(guān)的論文,limit為篇數(shù)
result = sh.search_by_science_direct(keywords, cookie="你的cookie", limit=10)
print(result)
for index, paper in enumerate(result.get("papers", [])):
# 批量下載這些論文
sh.download(paper["doi"], path=f"files/{keywords.replace(' ', '_')}_{index}.pdf")這樣大概率就能順利通過(guò)sciencedirect搜索并下載文獻(xiàn)了。
publons渠道:
其實(shí)有了百度學(xué)術(shù)的默認(rèn)渠道,大部分文獻(xiàn)我們都能覆蓋到了。但是考慮到publons的特殊性,這里還是給大家一個(gè)通過(guò)publons渠道搜索下載的選項(xiàng)。
使用publons渠道搜索下載其實(shí)很簡(jiǎn)單,你只需要更改搜索的函數(shù)名即可,不需要配置Cookie:
from scihub import SciHub
sh = SciHub()
# 搜索詞
keywords = "quant"
# 搜索該關(guān)鍵詞相關(guān)的論文,limit為篇數(shù)
result = sh.search_by_publons(keywords, limit=10)
print(result)
for index, paper in enumerate(result.get("papers", [])):
# 批量下載這些論文
sh.download(paper["doi"], path=f"files/{keywords.replace(' ', '_')}_{index}.pdf")5. 異步下載優(yōu)化,增加超時(shí)控制
這個(gè)開(kāi)源代碼庫(kù)已經(jīng)運(yùn)行了幾個(gè)月,經(jīng)常有同學(xué)反饋搜索論文后下載論文的速度過(guò)慢、下載的文件損壞的問(wèn)題,這幾天剛好有時(shí)間一起解決了。
下載速度過(guò)慢是因?yàn)橹暗陌姹臼褂昧舜械姆绞饺カ@取數(shù)據(jù)和保存文件,事實(shí)上對(duì)于這種IO密集型的操作,最高效的方式是用 asyncio 異步的形式去進(jìn)行文件的下載。
而下載的文件損壞則是因?yàn)橄螺d時(shí)間過(guò)長(zhǎng),觸發(fā)了超時(shí)限制,導(dǎo)致文件傳輸過(guò)程直接被腰斬了。
因此,我們將在原有代碼的基礎(chǔ)上添加兩個(gè)方法:1.異步請(qǐng)求下載鏈接,2.異步保存文件。
此外增加一個(gè)錯(cuò)誤提示:如果下載超時(shí)了,提示用戶下載超時(shí)并不保存損壞的文件,用戶可自行選擇調(diào)高超時(shí)限制。
首先,新增異步獲取scihub直鏈的方法,改為異步獲取相關(guān)論文的scihub直鏈:
# 部分代碼
async def async_get_direct_url(self, identifier):
"""
異步獲取scihub直鏈
"""
async with aiohttp.ClientSession() as sess:
async with sess.get(self.base_url + identifier) as res:
logger.info(f"獲取 {self.base_url + identifier} 中...")
# await 等待任務(wù)完成
html = await res.text(encoding='utf-8')
s = self._get_soup(html)
frame = s.find('iframe') or s.find('embed')
if frame:
return frame.get('src') if not frame.get('src').startswith('//') \
else 'http:' + frame.get('src')
else:
logger.error("Error: 可能是 Scihub 上沒(méi)有收錄該文章, 請(qǐng)直接訪問(wèn)上述頁(yè)面看是否正常。")
return html這樣,在搜索論文后,調(diào)用該接口就能獲取所有需要下載的scihub直鏈,速度很快:
def search(keywords: str, limit: int):
"""
搜索相關(guān)論文并下載
Args:
keywords (str): 關(guān)鍵詞
limit (int): 篇數(shù)
"""
sh = SciHub()
result = sh.search(keywords, limit=limit)
print(result)
loop = asyncio.get_event_loop()
# 獲取所有需要下載的scihub直鏈
tasks = [sh.async_get_direct_url(paper["doi"]) for paper in result.get("papers", [])]
all_direct_urls = loop.run_until_complete(asyncio.gather(*tasks))
print(all_direct_urls)
獲取直鏈后,需要下載論文,同樣也是IO密集型操作,增加2個(gè)異步函數(shù):
async def job(self, session, url, destination='', path=None):
"""
異步下載文件
"""
file_name = url.split("/")[-1].split("#")[0]
logger.info(f"正在讀取并寫(xiě)入 {file_name} 中...")
# 異步讀取內(nèi)容
try:
url_handler = await session.get(url)
content = await url_handler.read()
except:
logger.error("獲取源文件超時(shí),請(qǐng)檢查網(wǎng)絡(luò)環(huán)境或增加超時(shí)時(shí)限")
return str(url)
with open(os.path.join(destination, path + file_name), 'wb') as f:
# 寫(xiě)入至文件
f.write(content)
return str(url)
async def async_download(self, loop, urls, destination='', path=None):
"""
觸發(fā)異步下載任務(wù)
如果你要增加超時(shí)時(shí)間,請(qǐng)修改 total=300
"""
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=300)) as session:
# 建立會(huì)話session
tasks = [loop.create_task(self.job(session, url, destination, path)) for url in urls]
# 建立所有任務(wù)
finished, unfinished = await asyncio.wait(tasks)
# 觸發(fā)await,等待任務(wù)完成
[r.result() for r in finished]最后,在search函數(shù)中補(bǔ)充下載操作:
import asyncio
from scihub import SciHub
def search(keywords: str, limit: int):
"""
搜索相關(guān)論文并下載
Args:
keywords (str): 關(guān)鍵詞
limit (int): 篇數(shù)
"""
sh = SciHub()
result = sh.search(keywords, limit=limit)
print(result)
loop = asyncio.get_event_loop()
# 獲取所有需要下載的scihub直鏈
tasks = [sh.async_get_direct_url(paper["doi"]) for paper in result.get("papers", [])]
all_direct_urls = loop.run_until_complete(asyncio.gather(*tasks))
print(all_direct_urls)
# 下載所有論文
loop.run_until_complete(sh.async_download(loop, all_direct_urls, path=f"files/"))
loop.close()
if __name__ == '__main__':
search("quant", 5)一個(gè)完整的下載過(guò)程就OK了:

比以前的方式舒服太多太多了... 如果你要增加超時(shí)時(shí)間,請(qǐng)修改async_download函數(shù)中的 total=300,把這個(gè)請(qǐng)求總時(shí)間調(diào)高即可。
最新代碼前往GitHub上下載:
https://github.com/Ckend/scihub-cn
或者從Python實(shí)用寶典公眾號(hào)后臺(tái)回復(fù) scihub 下載。
最近有同學(xué)希望直接通過(guò)DOI號(hào)下載文獻(xiàn),因此補(bǔ)充了這部分內(nèi)容。
import asyncio
from scihub import SciHub
def fetch_by_doi(dois: list, path: str):
"""
根據(jù) doi 獲取文檔
Args:
dois: 文獻(xiàn)DOI號(hào)列表
path: 存儲(chǔ)文件夾
"""
sh = SciHub()
loop = asyncio.get_event_loop()
# 獲取所有需要下載的scihub直鏈
tasks = [sh.async_get_direct_url(doi) for doi in dois]
all_direct_urls = loop.run_until_complete(asyncio.gather(*tasks))
print(all_direct_urls)
# 下載所有論文
loop.run_until_complete(sh.async_download(loop, all_direct_urls, path=path))
loop.close()
if __name__ == '__main__':
fetch_by_doi(["10.1088/1751-8113/42/50/504005"], f"files/")這個(gè)API的源代碼其實(shí)非常好讀懂
7.1、找到sci-hub目前可用的域名
首先它會(huì)在這個(gè)網(wǎng)址里找到sci-hub當(dāng)前可用的域名,用于下載論文:
https://whereisscihub.now.sh/

可惜的是,作者常年不維護(hù),該地址已經(jīng)失效了,我們就是在這里修改了該域名,使得項(xiàng)目得以重新正常運(yùn)作:

7.2、對(duì)用戶輸入的論文地址進(jìn)行解析,找到相應(yīng)論文
1. 如果用戶輸入的鏈接不是直接能下載的,則使用sci-hub進(jìn)行下載
2. 如果scihub的網(wǎng)址無(wú)法使用則切換另一個(gè)網(wǎng)址使用,除非所有網(wǎng)址都無(wú)法使用。

3.值得注意的是,如果用戶輸入的是論文的關(guān)鍵詞,我們將調(diào)用sciencedirect的接口,拿到論文地址,再使用scihub進(jìn)行論文的下載。
7.3、下載
1. 拿到論文后,它保存到data變量中
2. 然后將data變量存儲(chǔ)為文件即可

此外,代碼用到了一個(gè)retry裝飾器,這個(gè)裝飾器可以用來(lái)進(jìn)行錯(cuò)誤重試,作者設(shè)定了重試次數(shù)為10次,每次重試最大等待時(shí)間不超過(guò)1秒。
希望大家能妥善使用好此工具,不要批量下載,否則一旦網(wǎng)站被封,學(xué)生黨們又要哭了。
我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請(qǐng)持續(xù)關(guān)注Python實(shí)用寶典。
有任何問(wèn)題,可以在公眾號(hào)后臺(tái)回復(fù):加群,回答相應(yīng)紅字驗(yàn)證信息,進(jìn)入互助群詢問(wèn)。
原創(chuàng)不易,希望你能在下面點(diǎn)個(gè)贊和在看支持我繼續(xù)創(chuàng)作,謝謝!
點(diǎn)擊下方閱讀原文可獲得更好的閱讀體驗(yàn)
Python實(shí)用寶典 (pythondict.com)
不只是一個(gè)寶典
歡迎關(guān)注公眾號(hào):Python實(shí)用寶典
