你不得不知道的python超级文献批量搜索下载工具

文献搜索对于广大学子来说真的是个麻烦事,如果你的学校购买的论文下载权限不够多,或者不在校园内,那就很头痛了。幸好,我们有Python制作的这个论文搜索工具,简化了我们学习的复杂性。

2020-05-28 补充:已用最新的scihub提取网,目前项目可用,感谢@lisenjor的分享。另外分享一个可用下载地址:https://sci-hub.shop/

2020-06-25 补充:增加关键词搜索批量下载论文功能。

​2021-01-07 补充:增加异步下载方式,加快下载速度;加强下载稳定性,不再出现文件损坏的情况。

2021-04-08 补充:由于sciencedirect增加了机器人检验,现在搜索下载功能需要先在HEADERS中填入Cookie才可爬取。

2021-04-25 补充:搜索下载增加百度学术、publons渠道。

2021-08-10 补充:修复scihub页面结构变化导致无法下载的问题,增加DOI下载函数。

1.什么是Scihub?

首先给大家介绍一下sci-hub这个线上数据,这个数据提供了 81,600,000 篇科学学术论文和文章下载。起初由一名叫 亚历珊卓·艾尔巴金 的研究生建立,她过去在哈佛大学从事研究时发现支付所需要的数百篇论文的费用实在是太高了,因此就萌生了创建这个网站,让更多人获得知识的想法

后来,这个网站越来越出名,逐渐地在更多地国家如印度、印度尼西亚、中国、俄罗斯等国家盛行,并成功地和一些组织合作,共同维护和运营这个网站。到了2017年的时候,网站上已有81600000篇学术论文,占到了所有学术论文的69%,基本满足大部分论文的需求,而剩下的31%是研究者不想获取的论文。

2.为什么我们需要用Python工具下载

在起初,这个网站是所有人都能够访问的,但是随着其知名度的提升,越来越多的出版社盯上了他们,在2015年时被美国法院封禁后其在美国的服务器便无法被继续访问,因此从那个时候开始,他们就跟出版社们打起了游击战。

游击战的缺点就是导致scihub的地址需要经常更换,所以我们没办法准确地一直使用某一个地址访问这个数据。当然也有一些别的方法可让我们长时间访问这个网站,比如说修改DNS,修改hosts文件,不过这些方法不仅麻烦,而且也不是长久之计,还是存在失效的可能的。

3.新姿势:用Python写好的API工具超方便下载论文

这是一个来自github的开源非官方API工具,下载地址为:

https://github.com/zaytoun/scihub.py

但由于作者长久不更新,原始的下载工具已经无法使用,Python实用宝典修改了作者的源代码,适配了中文环境的下载器,并添加了异步批量下载等方法:

https://github.com/Ckend/scihub-cn

欢迎给我一个Star,鼓励我继续维护这个仓库。如果你访问不了GitHub,请在 Python实用宝典 公众号后台回复 scihub,下载最新可用代码。

解压后使用CMD/Terminal进入这个文件夹,输入以下命令(默认你已经安装好了Python)安装依赖:

pip install -r requirements.txt

然后我们就可以准备开始使用啦!

这个工具使用起来非常简单,你可以先在 Google 学术(搜索到论文的网址即可)或ieee上找到你需要的论文,复制论文网址如:

http://img3.imgtn.bdimg.com/it/u=664814095,2334584570&fm=11&gp=0.jpg

然后在scihub-cn文件夹里新建一个文件叫download.py, 输入以下代码:

from scihub import SciHub

sh = SciHub()

# 第一个参数输入论文的网站地址
# path: 文件保存路径
result = sh.download('https://ieeexplore.ieee.org/document/26502', path='paper.pdf')

进入该文件夹后在cmd/terminal中运行:

python download.py

你就会发现文件成功下载到你的当前目录啦,名字为paper.pdf,如果不行,多试几次就可以啦,还是不行的话,可以在下方留言区询问哦。

上述是第一种下载方式,第二种方式你可以通过在知网或者百度学术上搜索论文拿到DOI号进行下载,比如:

from scihub import SciHub
sh = SciHub()
result = sh.download('10.1016/j.compeleceng.2020.106640', path='paper2.pdf')

下载完成后就会在文件夹中出现该文献:

除了这种最简单的方式,我们还提供了 论文关键词搜索批量下载 及 论文关键词批量异步下载 两种高级的下载方法。

我们在下文将会详细地讲解这两种方法的使用,大家可以看项目内的  test.py 文件,你可以了解到​论文搜索批量下载的方法。

进一步的高级方法在download.py 中可以找到,它可以实现论文搜索批量异步下载​,大大加快下载速度。具体实现​请看后文。

4.论文关键词批量下载

支持使用搜索的形式批量下载论文,比如说搜索关键词 量化投资(quant):

from scihub import SciHub

sh = SciHub()

# 搜索词
keywords = "quant"

# 搜索该关键词相关的论文,limit为篇数
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 更新:

由于读者们觉得Sciencedirect的搜索实在太难用了,加上Sciencedirect现在必须要使用Cookie才能正常下载,因此我新增了百度学术和publons这2个检索渠道。

由于 Web of Science 有权限限制,很遗憾我们无法直接使用它来检索,不过百度学术作为一个替代方案也是非常不错的。

现在默认的 search 函数调用了百度学术的接口进行搜索,大家不需要配置任何东西,只需要拉一下最新的代码,使用上述例子中的代码就可以正常搜索下载论文。

其他两个渠道的使用方式如下:

sciencedirect渠道:

由于 sciencedirect 加强了他们的爬虫防护能力,增加了机器人校验机制,所以现在必须在HEADER中填入Cookie才能进行爬取。

操作如下:

1.获取Cookie

2.使用sciencedirect搜索时,需要用search_by_science_direct函数,并将cookie作为参数之一传入:

from scihub import SciHub

sh = SciHub()

# 搜索词
keywords = "quant"

# 搜索该关键词相关的论文,limit为篇数
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")

这样就能顺利通过sciencedirect搜索论文并下载了。

publons渠道:

其实有了百度学术的默认渠道,大部分文献我们都能覆盖到了。但是考虑到publons​的特殊性,这里还是给大家一个通过publons渠道搜索下载的选项。

使用publons渠道搜索下载其实很简单,你只需要更改搜索的函数名即可,不需要配置Cookie:

from scihub import SciHub

sh = SciHub()

# 搜索词
keywords = "quant"

# 搜索该关键词相关的论文,limit为篇数
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.异步批量下载优化,增加超时控制

这份代码已经运行了几个月,经常有同学反馈搜索论文后批量下载论文的速度过慢、下载的文件损坏的问题,这几天刚好有时间一起解决了。

下载速度过慢是因为之前的版本使用了串行的方式去获取数据和保存文件,事实上对于这种IO密集型的操作,最高效的方式是用 asyncio 异步的形式去进行文件的下载。

而下载的文件损坏则是因为下载时间过长,触发了超时限制,导致文件传输过程直接被腰斩了。

因此,我们将在原有代码的基础上添加两个方法:1.异步请求下载链接,2.异步保存文件。

此外增加一个错误提示:如果下载超时了,提示用户下载超时并不保存损坏的文件,用户可自行选择调高超时限制。

首先,新增异步获取scihub直链的方法,改为异步获取相关论文的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 等待任务完成
                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 上没有收录该文章, 请直接访问上述页面看是否正常。")
                    return html

这样,在搜索论文后,调用该接口就能获取所有需要下载的scihub直链,速度很快:

def search(keywords: str, limit: int):
    """
    搜索相关论文并下载

    Args:
        keywords (str): 关键词
        limit (int): 篇数
    """

    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个异步函数:

async def job(self, session, url, destination='', path=None):
    """
    异步下载文件
    """
    if not url:
        return
    file_name = url.split("/")[-1].split("#")[0]
    logger.info(f"正在读取并写入 {file_name} 中...")
    # 异步读取内容
    try:
        url_handler = await session.get(url)
        content = await url_handler.read()
    except Exception as e:
        logger.error(f"获取源文件出错: {e},大概率是下载超时,请检查")
        return str(url)
    with open(os.path.join(destination, path + file_name), 'wb') as f:
        # 写入至文件
        f.write(content)
    return str(url)

async def async_download(self, loop, urls, destination='', path=None):
    """
    触发异步下载任务
    如果你要增加超时时间,请修改 total=300
    """
    async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=300)) as session:
        # 建立会话session
        tasks = [loop.create_task(self.job(session, url, destination, path)) for url in urls]
        # 建立所有任务
        finished, unfinished = await asyncio.wait(tasks)
        # 触发await,等待任务完成
        [r.result() for r in finished]

这样,最后,在search函数中补充下载操作:

import asyncio
from scihub import SciHub


def search(keywords: str, limit: int):
    """
    搜索相关论文并下载

    Args:
        keywords (str): 关键词
        limit (int): 篇数
    """

    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", 10)

一个完整的下载过程就OK了:

比以前的方式舒服太多太多了… 如果你要增加超时时间,请修改async_download函数中的 total=300,把这个请求总时间调高即可。

最新代码前往GitHub上下载:

https://github.com/Ckend/scihub-cn

或者从Python实用宝典公众号后台回复 scihub 下载。

6.根据DOI号下载文献

最近有同学希望直接通过DOI号下载文献,因此补充了这部分内容。

import asyncio
from scihub import SciHub


def fetch_by_doi(dois: list, path: str):
    """
    根据 doi 获取文档
    Args:
        dois: 文献DOI号列表
        path: 存储文件夹
    """

    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/")

默认存储到files文件夹中,你也可以根据自己的需求对代码进行修改。

7.工作原理

这个API的源代码其实非常好读懂。

一、找到sci-hub目前可用的域名

首先它会在这个网址里找到sci-hub当前可用的域名,用于下载论文:

https://whereisscihub.now.sh/

可惜的是,作者常年不维护,该地址已经失效了,我们就是在这里修改了该域名,使得项目得以重新正常运作:

二、对用户输入的论文地址进行解析,找到相应论文

1. 如果用户输入的链接不是直接能下载的,则使用sci-hub进行下载

2. 如果scihub的网址无法使用则切换另一个网址使用,除非所有网址都无法使用。

3.如果用户输入的是关键词,将调用sciencedirect的接口,拿到论文地址,再使用scihub进行论文的下载。

三、下载

1. 拿到论文后,它保存到data变量中

2. 然后将data变量存储为文件即可

此外,代码用到了一个retry装饰器,这个装饰器可以用来进行错误重试,作者设定了重试次数为10次,每次重试最大等待时间不超过1秒。

我们的文章到此就结束啦,如果你希望我们今天的文章,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦,有任何问题都可以在下方留言区留言,我们都会耐心解答的!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

《你不得不知道的python超级文献批量搜索下载工具》上的79个想法

  1. 为啥批量下载没有反应呢?就是把download.py换成下面的没用啊
    from scihub import SciHub

    sh = SciHub()

    # 获取在谷歌学术上 ‘bittorrent’ 关键词的5篇文章
    results = sh.search(‘bittorrent’, 5)

    # 下载论文,有需要的话会调用scihub
    for paper in results[‘papers’]:
    sh.download(paper[‘url’])

    1. WIN+R,输入cmd进入cmd,然后cd到你的文件夹目录,输入这条命令,看看报什么错。

  2. File “E:\python\scihub\scihub\scihub.py”, line 43, in __init__
    self.base_url = self.available_base_url_list[0] + ‘/’
    IndexError: list index out of range
    报错了

  3. 可是我尝试把源代码中的网址换成这个,甚至是直接把几个可用网址用列表列出来,不报错了,但是下载没反应

    1. 是的,源代码中包含了提取可用链接的部分,可能是提取出来的链接不正确,你可以在相关下载代码前DEBUG一下或者print下载链接,看看正不正确。

  4. 安装依赖pip install -r requirements.txt 的时候报错,
    ERROR:Could not find aversion that satisfies the requirement beautifulsoup 4(from -r requirement. txt (line 1)

  5. 谢谢及时的回复,之前由于是在手机上输入,多打了一个空格,具体出现的错误如下,ERROR: Could not find a version that satisfies the requirement beautifulsoup4 (from -r requirements.txt (line 1)) (from versions: none)
    ERROR: No matching distribution found for beautifulsoup4 (from -r requirements.txt (line 1))。这样该周末解决呢?

    1. 先试一下升级pip:
      pip install –upgrade pip
      再试试:
      pip install beautifulsoup4
      如果不行可以按照bs4:
      pip install bs4

    2. 先试一下升级pip:
      pip install –upgrade pip
      再试试:
      pip install beautifulsoup4
      如果不行可以安装bs4:
      pip install bs4

  6. 请问Errno2咋办,其实我是有keywords-download.py的,是python要和这个文件夹在同一个大文件夹里吗?

  7. 我也想知道,怎么换接口了?不用sciencedirect,另外我下载限制为100篇,为什么只出来40篇左右了?

    1. 换接口需要改代码里的链接和相应的元素参数,可以把你希望用的网站发给我,我来提供选项

  8. 请问运行python出现下面信息提示,怎么办?请各位大神指教。
    INFO: Sci-Hub: Failed to fetch pdf with identifier science/article/pii/S0308814620325504 (resolved url None) due to request exception.

  9. 一直都显示是:{‘papers’: [], ‘err’: ‘Failed to complete search with query klebsiella pneumoniae (connection error)’},
    无论怎么更换keywords都是这个结果,但是我自己网络用起来是好的

    1. 要在与scihub.py文件同级的文件夹下新建download.py. 如果你是下载的代码,会发现已经有这个文件了,直接运行即可。(https://github.com/Ckend/scihub-cn)

  10. rror running ‘scihub’: Cannot run program “C:\Users\郭丰磊\PycharmProjects\pythonProject\venv\Scripts\python.exe” (in directory “C:\Users\xxx\PycharmProjects\pythonProject\scihub-cn-master\scihub”): CreateProcess error=2, 系统找不到指定的文件 一直显示这样的

  11. 有两个问题:1。为什么我运行这串代码的时候总是提示说找不到scihub这个模块,我是按照代码进行的啊?
    2.为什么我无法加群啊,加了微信,也没有任何反应啊?

    1. 1.终端要进入download.py的文件夹运行。
      2.你的微信多少,我加一下,最近加的人比较多,没有按照要求的回答都没加。

  12. 您好,在cmd中输入pip那句时报错:
    ERROR: Could not find a version that satisfies the requirement retrying (from -r requirements.txt (line 3)) (from versions: none)
    ERROR: No matching distribution found for retrying (from -r requirements.txt (line 3))

  13. 您好,刚才的问题换了台电脑已解决(感谢!),现在运行download.py后报错如下:
    aiohttp.client_exceptions.ServerDisconnectedError: Server disconnected

  14. 您好!我在使用中遇到以下问题:
    Traceback (most recent call last):
    File “D:\LearningStuff\scihub-cn-master\scihub\scihub.py”, line 257, in job
    file_name = url.split(“/”)[-1].split(“#”)[0]
    AttributeError: ‘NoneType’ object has no attribute ‘split’
    ERROR:asyncio:Task exception was never retrieved
    future: <Task finished name='Task-566' coro= exception=AttributeError(“‘NoneType’ object has no attribute ‘split'”)>
    请问如何解决?

    1. 获取直链的时候没有获取成功,检查一下该关键词是否真的能从sciencedirect上搜索到论文,然后试试手动上scihub上看看相关论文地址是否可以下载。

  15. 我使用cmd输入运行命令提示错误如下:

    ModuleNotFoundError: No module named ‘aiohttp’

    如果在Visual Studio Code终端运行提示如下错误:
    python.exe: can’t open file ‘d:\PythonPractise\scihub-cn’: [Errno 2] No such file or directory
    向大神求救!

  16. sh.fetch 命令可以正常运行,但sh.download和sh.search显示以下错误:
    AttributeError: ‘SciHub’ object has no attribute ‘download’
    请问如何解决

  17. 第一个运行”python download.py”时,会报错“Traceback (most recent call last):

    ModuleNotFoundError: No module named ‘aiohttp’”

  18. 你好,我在使用中遇到如下问题:
    Traceback (most recent call last):
    File “download.py”, line 30, in
    search(“quant”, 5)
    File “download.py”, line 25, in search
    loop.run_until_complete(sh.async_download(loop, all_direct_urls, path=f”files/”))
    File “/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py”, line 468, in run_until_complete
    return future.result()
    File “/Users/han/PycharmProjects/scihub-cn-master/scihub/scihub.py”, line 279, in async_download
    finished, unfinished = await asyncio.wait(tasks)
    File “/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/tasks.py”, line 304, in wait
    raise ValueError(‘Set of coroutines/Futures is empty.’)
    ValueError: Set of coroutines/Futures is empty.

  19. 大佬您好!我也想加一些接口,除了sciencedirect的,希望能有pubs.acs.org,onlinelibrary.wiley.com, pubmed.ncbi.nlm.nih.gov, 这些接口,麻烦大佬赐教,多谢!

  20. INFO:Sci-Hub:Failed to fetch pdf with identifier 10.1016/j.compeleceng.2020.106640 (resolved url None) due to request exception.zh
    这个如何解决?

  21. 昨晚下载成功了几十篇了,今早又不行了。

    发生异常: FileNotFoundError
    [Errno 2] No such file or directory: ‘files/10.1016@j.jcv.2020.104524.pdf’

  22. 不能直接用web of science,就用数组迭代的方式通过DOI号批量下载文献:
    from scihub import SciHub
    sh = SciHub()

    from doilist import DOIs

    index = len(DOIs)

    for doi in DOIs:
    result = sh.download(doi, path= str(index) + “.pdf”)
    index -= 1

    但是经常出现下面问题:
    发生异常: TypeError
    argument of type ‘NoneType’ is not iterable
    File “C:\Downloads\py\scihub\scihub.py”, line 125, in download
    if not ‘err’ in data:
    File “C:\Downloads\py\scihub\DOI_download.py”, line 9, in
    result = sh.download(doi, path= str(index) + “.pdf”)

  23. 大牛,两个问题
    1、请问 是最多只能下载 80篇吗?
    limit300完全 不管作用

    2、请问大牛能更新一下吗?让只下载近10年的文献

  24. 测试的时候提示
    Traceback (most recent call last):
    File “E:\scihub-cn\scihub\my_test.py”, line 2, in
    sh = SciHub()
    TypeError: __init__() missing 1 required positional argument: ‘proxy’
    应该怎么解决啊

    1. 现在可以通过命令行下载:https://github.com/Ckend/scihub-cn
      pip install scihub-cn
      下载只需要输入命令
      $scihub-cn -w machine_learning

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注