Pubmed検索でヒットした論文をダウンロードする(Python)

「退屈なことはPythonにやらせよう」主義なので、Pubmedで検索した際にヒットした論文をDLし、論文のタイトルをファイル名にして保存するスクリプトを書いた(なお、現在一部の雑誌にしか対応していない)。 手順としては次の通り。

1. EntrezのAPIを叩いて、該当する論文のPMIDのリストを得る
2. Seleniumを用いてブラウザを立ち上げる
3. 論文のフルテキストのページに移動
4. ページのソースからPDFを含むリンクを抽出しそれをDLする
5. 4でDLしたPDFの名前を論文タイトルに変更する

以下、詳しく見ていこう。

PMIDのリストを得る

今回はBiopythonを用いてNCBIのEntrezデータベースにアクセスしてみることにしよう(詳細はこちら)。

pip install biopython

Entrezのesearch関数で検索を行う。retmaxは検索結果の上位何件を取得するかの数字で、デフォルトは20だが最大10000まで指定可能。

from Bio import Entrez
from Bio.Entrez import efetch, read
Entrez.email = "メールアドレス"
handle = Entrez.esearch(db="pubmed", term='検索ワード', mindate="1997/01/28", maxdate="2017/09/17", retmax=5)
record = Entrez.read(handle)
IdList = record['IdList']

これによりダウンロードしたい論文のPMIDのリストがIdListに格納される。

Seleniumでブラウザを立ち上げる

Selenium(ブラウザのオートメーションツール)を使ってfirefoxを動かしてみる。

Seleniumのインストール
pip install selenium
ドライバの入手

geckodriverをダウンロードした上で、さらにパスを通しておく。

ブラウザの起動

PDFをDLする際にいちいちダイアログボックスなどが出ていては困るので、保存場所を指定した上でワンクリックでDLできるようにFirefoxProfileを設定しておく。

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
fp = webdriver.FirefoxProfile()
fp.set_preference("browser.download.folderList", 2)
fp.set_preference("browser.download.manager.showWhenStarting", False)
fp.set_preference("browser.download.dir", "PDFの保存場所")
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/pdf")
fp.set_preference("pdfjs.disabled", True)

browser = webdriver.Firefox(firefox_profile=fp)

各ファイルに対しての処理

最後の方にある、ダウンロードしたPDFを論文のtitleでrenameする処理に関しては、こちらを参考にさせて頂いた。

def format_filename(str): #ファイル名に使用不可能な文字を半角スペースに置き換える
    str = str + '.pdf'
    unusable_character = ['/', ':', '?', '*', '"', '<', '>', '|']
    for UC in unusable_character:
        if UC in str:
            str = str.replace(UC, ' ')
    return(str)

def return_filename(Id, f): #論文の題名を、APIを叩いて得られるxmlをパースすることにより得る
    handle = efetch(db='pubmed', id=Id, retmode='xml')
    xml_data = read(handle)
    title = xml_data['PubmedArticle'][0]['MedlineCitation']['Article']['ArticleTitle']
    filename = format_filename(title)
    return(filename)

for Id in IdList:
    url = 'http://eutils.ncbi.nlm.nih.gov/entrez/eutils/elink.fcgi?dbfrom=pubmed&id=' + Id + '&retmode=ref&cmd=prlinks' #これで論文のフルテキストのページにリダイレクトされる
    browser.get(url)
    Object = browser.find_element_by_link_text('PDF') #リンクテキストが'PDF'であるリンクを取得する
    Object.click()
    
    #以下、一番最近DLしたPDFを論文の題名でrenameする処理
    save_to_directory = 'PDFの保存場所'
    os.chdir(save_to_directory)
    files = filter(os.path.isfile, os.listdir(save_to_directory))
    files = [os.path.join(save_to_directory, f) for f in files] #それぞれのファイルにパスを通す
    files.sort(key=lambda x: os.path.getmtime(x))
    newest_file = files[-1]
    filename = return_filename(Id, f)
    os.rename(newest_file, filename)

注意

万が一多くのリクエストを送る場合は、time.sleep()を活用するなどしてアクセスが短時間に集中しないような配慮が求められる。