【Paper Tips】随记8-基于DOI号批量下载文献(示例:IEEE Xplore文献网站)

写paper时随心记录一些对自己有用的skills与tips。


一、待解决问题

1.1 问题描述

查询文献时,有众多文献想阅读,但一篇篇基于doi号去下载,费时费力。
因此,这篇文章基于IEEE Xplore搜索,撰写一段自动批量下载文献的程序。

1.2 解决方法

(1)安装Firefox浏览器驱动:geckodriver
(2)基于webdriver实现文献自动下载
(3)使用注意事项及待优化之处

二、方法详述

2.1 必要说明

(1)操作系统:ubuntu 22.04 LTS
(2)浏览器:Firefox 139.0.1 (64 位)
(3)python版本:3.11.11
(4)geckodriver版本:geckodriver 0.36.0
(5)selenium版本:4.24.0

2.2 应用步骤

2.2.1 安装Firefox浏览器驱动:geckodriver

geckodriver 是 Selenium 用于控制 Firefox 浏览器的驱动程序。它充当 Selenium 和 Firefox 浏览器之间的桥梁,允许 Selenium 发送命令到 Firefox 浏览器并接收响应。简单来说,geckodriver 是一个可执行文件,用于使 Selenium 能够自动化 Firefox 浏览器。

下载链接:https://github.com/mozilla/geckodriver/releases
然后解压并将可执行文件发送至对应目录:

tar -xvzf geckodriver-v0.36.0-linux64.tar.gz
sudo mv geckodriver /usr/local/bin/
geckodriver --version
geckodriver 0.36.0 (a3d508507022 2025-02-24 15:57 +0000)

The source code of this program is available from
testing/geckodriver in https://hg.mozilla.org/mozilla-central.

This program is subject to the terms of the Mozilla Public License 2.0.
You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.

实际运行过程中,存在报错:
在这里插入图片描述

解决方式:使用selenium调用firefox提示Profile Missing的问题解决

2.2.2 基于webdriver实现文献自动下载

以下是基于seleniumgeckodriver实现的在IEEE Xore上自动搜索doi批量下载文献的代码,功能概述如下:

(1)设置文献doi保存的txt,设置文献下载路径
(2)修改浏览器默认设置,访问IEEE Xplore官网,等待用户自行登录
(3)自动输入doi号开始搜索,有结果即寻找下载按钮进行pdf保存
(4)统计下载文献数量和未下载数量


import os
import time
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.firefox.options import Options
import shutil


def configure_firefox_driver(download_dir):

    firefox_options = Options()
    firefox_options.add_argument('-profile')
    firefox_options.add_argument(
        '/home/XXX/snap/firefox/common/.mozilla/firefox')

    firefox_options.set_preference("browser.download.folderList", 2)
    firefox_options.set_preference("browser.download.dir", download_dir)
    firefox_options.set_preference(
        "browser.helperApps.neverAsk.saveToDisk", "application/pdf")
    firefox_options.set_preference("pdfjs.disabled", True)

    geckodriver_path = "/usr/local/bin/geckodriver"
    service = Service(geckodriver_path)

    driver = webdriver.Firefox(service=service, options=firefox_options)
    return driver


def search_and_download_doi(driver, doi, download_dir, title, notFound_path):
    print(">>======<<")
    print(f"开始搜索文献,标题:{title},DOI号:{doi}")
    print(">>======<<")
    try:
        search_input = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CLASS_NAME, "Typeahead-input"))
        )
        search_input.click()
        search_input.clear()

        print(f"输入DOI号: {doi}")
        search_input.send_keys(doi)

        search_button = WebDriverWait(driver, 10).until(
            EC.element_to_be_clickable((By.CLASS_NAME, "fa-search"))
        )
        search_button.click()

        time.sleep(1)

        try:
            result_span = driver.find_element(
                By.XPATH, "//span[contains(., 'Showing') and contains(., 'result')]")
            result_text = result_span.text
            if ("Showing" in result_text) & ("result" in result_text):
                print("找到搜索结果,开始寻找PDF下载按钮...")

                try:

                    pdf_link = WebDriverWait(driver, 20).until(
                        EC.element_to_be_clickable(
                            (By.XPATH, "//a[@aria-label='PDF']/i[@class='icon-size-md xpl-pdf-icon fas fa-file-pdf']"))
                    )

                    pdf_relative_url = pdf_link.find_element(
                        By.XPATH, "./..").get_attribute("href")
                    pdf_url = pdf_relative_url

                    print("找到PDF链接:", pdf_url)

                    # 下载前读取文件夹中文件数量
                    prev_files = set(os.listdir(download_dir))

                    # 访问PDF页面,并自动下载pdf
                    driver.get(pdf_url)

                    # 等待下载完成,这里是手动设置60s延时等待下载,如果网速较慢的情况下需增大延时
                    print("等待PDF下载完成...")
                    time.sleep(60)

                    # 下载后读取文件夹中文件数量
                    current_files = set(os.listdir(download_dir))

                    # 比较文件数量变化
                    if len(current_files) == len(prev_files) + 1:
                        print(f"{title} PDF下载成功")
                        return True
                    else:
                        print(f"{title} PDF下载失败,文件数量没有增加")
                        with open(notFound_path, "a") as f:
                            f.write(f"标题:{title},DOI号:{doi}\n")
                        return False

                    return True
                except:
                    print("未找到对应pdf下载按钮!")
                    with open(notFound_path, "a") as f:
                        f.write(f"标题:{title},DOI号:{doi}\n")
                    return False
            else:
                print("对应DOI无搜索结果!")
                with open(notFound_path, "a") as f:
                    f.write(f"标题:{title},DOI号:{doi}\n")
                return False
        except:
            print("未找到搜索结果!")
            with open(notFound_path, "a") as f:
                f.write(f"标题:{title},DOI号:{doi}\n")
            return False

    except Exception as e:
        print(f"处理DOI号{doi}时发生错误: {str(e)}")
        with open(notFound_path, "a") as f:
            f.write(f"标题:{title},DOI号:{doi}\n")
        return False


def read_papers(file_path):
    papers = []
    with open(file_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()
        title = None
        doi = None
        for line in lines:
            line = line.strip()
            if line.startswith('Title:'):
                # 如果已经提取到标题和DOI,先将之前的信息添加到列表中
                if title and doi:
                    papers.append({'title': title, 'doi': doi})
                # 开始提取新的文献信息
                title = line.replace('Title:', '').strip()
                doi = None
            elif line.startswith('DOI:'):
                doi = line.replace('DOI:', '').strip()
        # 添加最后一组文献信息
        if title and doi:
            papers.append({'title': title, 'doi': doi})
    return papers


def count_lines_in_txt(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            line_count = 0
            for line in file:
                line_count += 1
            return line_count
    except FileNotFoundError:
        print(f"文件 {file_path} 未找到。")
        return -1
    except Exception as e:
        print(f"读取文件时发生错误:{e}")
        return -1


def main():
    file_path = "./selected_Paper_WOS.txt"  # 保存文献title、doi号的文档
    # 存放下载文献的路径,必须是绝对路径
    download_dir = "/home/XXX/code/1env_paper/02_get_FullPaper/paper(Selected)"
    notFound_path = "./not_found_Paper.txt"  # 保存没有搜索到的文献doi

    # 创建或清空名为 "not_found_Paper.txt" 的文件
    with open(notFound_path, "w", encoding="utf-8") as file:
        pass  # 使用 pass 语句创建空文件

    # 每次运行前删除旧的下载目录并创建新的
    if os.path.exists(download_dir):
        shutil.rmtree(download_dir)
        print(f"已删除旧的下载目录 {download_dir}")

    if not os.path.exists(download_dir):
        os.makedirs(download_dir)
        print(f"目录 {download_dir} 已创建")

    driver = configure_firefox_driver(download_dir)
    driver.get("https://ieeexplore.ieee.org/Xplore/home.jsp")
    input("用户请自行登录IEEE Xplore,已登录后输入 'y' 并按回车继续: ")

    num_selectedPapers = 0

    try:
        papers = read_papers(file_path)
        num_selectedPapers = len(papers)
        for paper in papers:
            title = paper['title']
            doi = paper['doi']
            search_and_download_doi(
                driver, doi, download_dir, title, notFound_path)
            # 下载完成后返回主页,准备下载下一篇文章
            driver.get("https://ieeexplore.ieee.org/Xplore/home.jsp")
            print("返回IEEE Xplore主页...")
            time.sleep(1)
    finally:
        driver.quit()
        print("浏览器已关闭")

    num_downloadPapers = len(set(os.listdir(download_dir)))
    num_noFoundPapers = count_lines_in_txt(notFound_path)
    print(f"+++总计预下载的文献有{num_selectedPapers}篇。+++")
    print(f">>>从IEEE成功下载文献总计{num_downloadPapers}篇!<<<")
    print(f"~~~未在IEEE找到文献总计{num_noFoundPapers}篇!~~~")


if __name__ == "__main__":
    main()


2.2.3 使用注意事项及待优化之处

(1)注意事项-1 :修改浏览默认设置

def configure_firefox_driver(download_dir):

    firefox_options = Options()
    firefox_options.add_argument('-profile')
    firefox_options.add_argument(
        '/home/XXX/snap/firefox/common/.mozilla/firefox')

    firefox_options.set_preference("browser.download.folderList", 2)
    firefox_options.set_preference("browser.download.dir", download_dir)
    firefox_options.set_preference(
        "browser.helperApps.neverAsk.saveToDisk", "application/pdf")
    firefox_options.set_preference("pdfjs.disabled", True)

    geckodriver_path = "/usr/local/bin/geckodriver"
    service = Service(geckodriver_path)

    driver = webdriver.Firefox(service=service, options=firefox_options)
    return driver

这个函数中1路径得自己确认:
firefox路径:‘/home/XXX/snap/firefox/common/.mozilla/firefox’
geckodriver路径:“/usr/local/bin/geckodriver”

  • firefox_options.set_preference("browser.download.folderList", 2):

    • 这行代码设置下载文件夹的列表。值为2表示使用自定义的下载路径。
  • firefox_options.set_preference("browser.download.dir", download_dir):

    • 这行代码设置下载文件的保存目录。download_dir是下载文件的保存路径。
  • firefox_options.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/pdf"):

    • 这行代码指定对于特定的MIME类型(这里是PDF文件,MIME类型为application/pdf),浏览器不会询问用户是否要下载,而是直接保存到磁盘。
  • firefox_options.set_preference("pdfjs.disabled", True):

    • 这行代码禁用Firefox的内置PDF查看器。这样,当访问PDF文件时,Firefox会直接下载PDF文件而不是在浏览器中显示。

(2)注意事项-2 :下载成功判断不准确

# 等待下载完成,这里是手动设置60s延时等待下载,如果网速较慢的情况下需增大延时
                    print("等待PDF下载完成...")
                    time.sleep(60)

由于是手动设置60s延时等待下载,如果网速较慢的情况下,会存在未下载完就开始搜索下一篇文献的情况,但不影响下载进程。加上代码中判断是否下载成功,是在延时后判断文件夹内是否有文件数量+1,这就导致了可能下载进程未完成,但已经判断下载失败,是一个可以优化的地方。

三、疑问

暂无。

四、总结

  • 上述代码只适用于firefox浏览器,若在其它浏览器使用,可下载对应驱动。
  • 对于其它出版社的文献刊登网站,可以采用同样的方法,不过需要重新去适配对应网页的html元素设置逻辑,例如定位搜索框的html元素、搜索结果文本的html元素、pdf下载按钮的html元素等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值