RSS と Python を組み合わせて、新着文献を自動ダウンロード+メールで送付

研究者にとって、日々の新着論文チェックは非常に重要です。

実際にジャーナルのトップページに行き新着文献をチェックするのも良いとは思いますが、最近では多くの研究者が新着論文のチェックに RSS を活用していると思います。

Feedly などの RSSリーダー を登録しておくと各ジャーナルのページにいちいちアクセスせずに新着論文チェックができるので、非常に便利です。

しかし、RSS を使っていても不便な点はあります。

それは、所属する研究機関のネットワーク経由でないと論文の pdf をダウンロードできない点です!

自宅や出張先でも新着論文はチェックしたいものです。

そこで、今回の記事では RSS の情報をもとに自身の研究に関連する新着論文を自動的にダウンロードして、メールで送付してくれる python スクリプトを紹介します!

一応、記事としては、これまでの python でのスクレイピングの続きになります。

参考Webスクレイピング で文献検索を効率化!第1回s【Python】

参考論文 PDF のダウンロードを効率化!【Webスクレイピング 第2回】

使い方の説明

ファイルのダウンロード

まずは、一連のファイルを下記リンクからダウンロードして、path の通してあるディレクトリに移し、解凍しましょう。

以下のファイルが揃っていることを確認してください。

  • RSS_ACS.py
  • RSS_Wiley.py
  • RSS_RSC.py
  • log.txt
  • keyword.txt
  • Author.txt
  • RSS_download.sh

キーワードの設定

このプログラムでは、keyword.txt 内にあるキーワードが論文タイトルに含まれている新着論文と、Author.txt 内にある著者名が含まれている新着論文を自動的にダウンロードするよう設定してあります。

そこで、keyword.txtAuthor.txt の中身を自分用に編集してください。キーワードを “,” で区切って入力してください。

ディレクトリの設定

三つの python ファイル内にはダウンロード用のファイル path が書いてあります。これをご自身のお使いの環境に書き換えてください。

Chrome Driver の設定

ACS など多くの雑誌は、プログラムがウェブブラウザを介さずに直接 pdf をダウンロードすることを禁止しています。

そのため、今回の python スクリプトでは、一度 Google Chrome を起動して自動的に pdf をダウンロードするように設定してあります。

python から Google chrome を使用するには、chrome driver を設定しなくてはいけません。

こちらのページからご自身のお使いの Google chrome の version に一致した chrome driver をダウンロードし、path の通ったディレクトリに移動してください。

時折 Google Chrome が自動アップデートされてしまい、chromeDriver のバージョンが一致しなくなる場合があります。ですので、数週間に一回はchromeDriver のバージョンを確認するようにしてみて下さい!

実行

sh RSS_download.sh

と入力するとプログラムが実行されます。

python スクリプトの説明

上記の説明の通り実行すれば新着論文をダウンロードすることはできます。ここからは、もう少し詳しいスクリプトの動作について知りたい人向けに書きます。

今回のプログラムには 3 つの python スクリプトが含まれていますが、これらは第一引数に雑誌名を取ります。

例えば、
python RSS_ACS.py JACS
として実行すると JACSRSS ページに行き、新着論文のチェックとダウンロードを開始します。

しかし、雑誌ごとにいちいち以下のようにコマンドを打つのは非常に面倒です。

python RSS_ACS.py JACS
python RSS_ACS.py OL
python RSS_ACS.py JCTC

そこで、RSS_download.sh にまとめて実行するようにします。

RSS_download.sh 内には

python RSS_ACS.py JACS
python RSS_ACS.py OL
python RSS_ACS.py JCTC
...
python RSS_Wiley.py Angew
python RSS_Wiley.py JCC
...
python RSS_RSC.py OBC
python RSS_RSC.py NPR

と記述されています。

あとは、これを研究室に置きっ放しにしてあるパソコンで cron を利用して定期的に実行するだけで、自動的に論文をダウンロードしてメールで送付してくれるようになります。

cron は、あるプログラムを特定の時間に(定期的に)実行してくれる便利な仕組みです。

crontab -e で編集します。

例えば、毎朝 6:00 にプログラムを実行したいのであれば

00 6 * * * source ~/.bashrc; sh /Users/hogehoge/paper_RSS.sh

と設定すれば OK です。
(上記の hogehoge のところに適切な path を書き込んでください。)

ReadCube も連携させよう!

メールで論文 pdf を送信するのが利用規約に抵触しそうで怖いという方は、Readcube を利用した方が良いと思います。

参考論文ってメールで送っても良いの?【著作権】

Chem-Station さんのこちらの記事にもあるように Readcube「Add a Watch Folder」機能を使うことで、ダウンロードした論文を自動的に ReadCube に追加することが可能です。

これで、同期しているタブレットなどでも論文を読めるようになるのではないでしょうか?

でも、ReadcubeLinux サポート外らしいので、そうすると研究室に常設してある Mac または Windows を使うことになりそうです。。。

それか、Dropbox を使うという方法もありますが。。。

より詳しいスクリプトの説明

ここから下は、より詳しいスクリプトの説明です。

ACS 系の雑誌をダウンロードするスクリプトを例に説明します。まず最初に対応している雑誌の一覧があります。もし、自分の興味のあるものが無い人は、追加して下さい。

#! /usr/local/bin/python3

#ACS 系の以下の雑誌に対応
#JACS:      Journal of the American Chemical Society
#JCTC:      Journal of Chemical Theory and Computation
#JPCA:      The Journal of Physical Chemistry A
#JPCB:      The Journal of Physical Chemistry B
#JOC:       The Journal of Organic Chemistry
#Omega:     ACS Omega
#ChemBio:   ACS Chemical Biology
#Catalysis: ACS Catalysis
#ChemRev:   Chemical Reviews
#OL:        Organic Letters
#SynBio:    ACS Synthetic Biology

import re
import os
import sys
import time
import glob
import datetime
import smtplib
import feedparser
import mimetypes
from os.path import basename
from selenium import webdriver
from email import encoders
from email.utils import formatdate
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart

args = sys.argv
argc = len(args)

ここから下は、 chrome の設定などです。通常ブラウザには pdf ビューワーがついていますが、それを OFF にすることで pdf をダウンロードできるようにしています。

#PDF download 用の設定
download_dir = "/Users/hogehoge/RSS" #pdf のダウンロード先

#chrome の設定
options = webdriver.ChromeOptions()
options.add_experimental_option("prefs", {
                                "download.default_directory": download_dir,
                                "download.prompt_for_download": False,
                                "download.directory_upgrade": True,
                                "plugins.plugins_disabled": ["Chrome PDF Viewer"],
                                "plugins.always_open_pdf_externally": True
                                })
options.add_argument("--disable-extensions")
options.add_argument("--disable-print-preview")

#RSS の設定とダウンロード
driver = webdriver.Chrome(options=options)
d = feedparser.parse('https://feeds.feedburner.com/acs/jacsat')

#キーワードや著者名の追加
if args[1] == 'author':
    with open('./Author.txt', mode='a') as f:
        f.write(args[2] + ", ")
        sys.exit()
if args[1] == 'key':
    with open('./keyword.txt', mode='a') as f:
        f.write(args[2] + ", ")
        sys.exit()
#雑誌名の指定
if args[1] == 'JACS':
    d = feedparser.parse('https://feeds.feedburner.com/acs/jacsat')
if args[1] == 'JCTC':
    d = feedparser.parse('https://feeds.feedburner.com/acs/jctcce')
if args[1] == 'JPCA':
    d = feedparser.parse('https://feeds.feedburner.com/acs/jpcafh')
if args[1] == 'JPCB':
    d = feedparser.parse('https://feeds.feedburner.com/acs/jpcbfk')
if args[1] == 'JOC':
    d = feedparser.parse('https://feeds.feedburner.com/acs/joceah')
if args[1] == 'Omega':
    d = feedparser.parse('https://feeds.feedburner.com/acs/acsodf')
if args[1] == 'ChemBio':
    d = feedparser.parse('https://feeds.feedburner.com/acs/acbcct')
if args[1] == 'Catalysis':
    d = feedparser.parse('https://feeds.feedburner.com/acs/accacs')
if args[1] == 'ChemRev':
    d = feedparser.parse('https://feeds.feedburner.com/acs/chreay')
if args[1] == 'OL':
    d = feedparser.parse('https://feeds.feedburner.com/acs/joceah')
if args[1] == 'SynBio':
    d = feedparser.parse('https://feeds.feedburner.com/acs/asbcd6')

ここで、Author.txtKeyword.txt log.txt の中身を読み込んでいます。その後、RSS のページに行き pdfURL や論文のタイトル、著者名などを読み込んでいます。

一度読み込んだ新着論文の ID は、log.txt というファイルに保存されるため、同じ論文を何度もダウンロードする、ということは起きません。

#キーワードや著者名の読み込み
Authors=[]
Keywords=[]
logs=[]
Key_num=0
with open('./Author.txt') as fa:
    s = fa.read()
    for i in s.split(", "):
        Authors.append(i)
with open('./keyword.txt') as fk:
    s = fk.read()
    for i in s.split(", "):
        Keywords.append(i)
        Key_num +=1
with open('./log.txt') as fl:
    s = fl.read()
    for i in s.split("\n"):
        logs.append(i)

#検索キーワードの設定
#著者名は、頭文字だけ大文字
keyword_dir = "./keyword.txt"
Author_dir = "./Author.txt"

JACS_title=[]
JACS_author=[]
filenames=[]
num=0

for entry in d.entries:
	#if "Energy" in entry.title:
    #print(entry.authors)
    name_str = str(entry.authors).replace('[{\'name\':','').replace('*','').replace('†','').replace('§','').replace('?','').replace('#','').replace('‡','').replace('\' ','').replace('}]','').replace('and','')
    for auth in Authors:
        if auth in name_str:
            pdf_URL = "https://pubs.acs.org/doi/pdf/" + entry.id[18:]
            if entry.id in logs:
                continue
            driver.get(pdf_URL)
            num += 1
            JACS_title.append(entry.title)
            JACS_author.append(auth)
            name_split=entry.id.split("/")
            name=name_split[-1]
            filenames.append(name + ".pdf")
            logs.append(entry.id)
            with open('./log.txt', mode='a') as f:
                f.write(entry.id + "\n")
            time.sleep(1)
    for Key in Keywords:
        if Key in entry.title:
            pdf_URL = "https://pubs.acs.org/doi/pdf/" + entry.id[18:]
            if entry.id in logs:
                continue
            driver.get(pdf_URL)
            num += 1
            JACS_title.append(entry.title)
            name_str = str(entry.authors).replace('[{\'name\':','').replace('*','').replace('†','').replace('§','').replace('?','').replace('#','').replace('‡','').replace('\' ','').replace('}]','').replace('and','')
            JACS_author.append(name_str)
            name_split=entry.id.split("/")
            name=name_split[-1]
            filenames.append(name + ".pdf")
            with open('./log.txt', mode='a') as f:
                f.write(entry.id + "\n")
            time.sleep(1)

ここから下は、メールの送信設定です。今回は、Gmail を利用して送信する設定にしてあります。

FROM_ADDRESS には 送信元の gmail アドレス、MY_PASSWORD には Gmail にログインするためのパスワード、TO_ADDRESS には送信先のアドレス(自分がメインで使用しているメールアドレスなど)を記述して下さい。

#メールの設定
FROM_ADDRESS = 'hogehoge@gmail.com'
MY_PASSWORD = 'hogehoge'
TO_ADDRESS = 'hogehoge@yahoo.co.jp'
SUBJECT = args[1] + ' の新着記事が ' + str(num) + ' 件あります'
body = 'pythonでメール送信'

def attachment(filename):
    fd = open(filename, 'rb')
    mimetype, mimeencoding = mimetypes.guess_type(filename)
    if mimeencoding or (mimetype is None):
        mimetype = 'application/octet-stream'
    maintype, subtype = mimetype.split('/')
    if maintype == 'text':
        retval = MIMEText(fd.read(), _subtype=subtype)
    else:
        retval = MIMEBase(maintype, subtype)
        retval.set_payload(fd.read())
        encoders.encode_base64(retval)
    retval.add_header('Content-Disposition', 'attachment', filename=filename)
    fd.close()
    return retval

def create_message(from_addr, to_addr, subject, message, files):
    msg = MIMEMultipart()
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Date'] = formatdate()
    body = MIMEText(message, _subtype='plain')
    msg.attach(body)
    for filename in files:
        msg.attach(attachment(filename))
    return msg

def send(from_addr, to_addrs, msg):
    smtpobj = smtplib.SMTP('smtp.gmail.com', 587)
    smtpobj.ehlo()
    smtpobj.starttls()
    smtpobj.ehlo()
    smtpobj.login(FROM_ADDRESS, MY_PASSWORD)
    smtpobj.sendmail(from_addr, to_addrs, msg.as_string())
    smtpobj.close()

time.sleep(1)

for i in range(num):
    print(str(i) + ":\t" + JACS_title[i] + "\n\t" + JACS_author[i])

#メールの本文の編集
body = args[1] + " の新着文献が " + str(num) + " 件あります\n\n"
for i in range(num):
    body += str(i) + ":\t" + JACS_title[i] + "\n\t" + JACS_author[i] + "\n"
body += "\npythonでメール送信\n"

to_addr = TO_ADDRESS
subject = SUBJECT
print("body\n\t" + body)

for i in range(num):
    while os.path.exists(filenames[i]) == False:
        time.sleep(2)   # pdf のダウンロードに時間がかかる場合があるため

#メール送信
if num != 0:
    msg = create_message(FROM_ADDRESS, to_addr, subject, body, filenames)
    send(FROM_ADDRESS, to_addr, msg)

time.sleep(1)
driver.quit()

もしもメールでの通知機能はいらないという方は、最後の driver.quit() と以下のループだけ残して、メール実装部分はコメントアウトされるのが良いと思います。

for i in range(num):
    while os.path.exists(filenames[i]) == False:
        time.sleep(2)   # pdf のダウンロードに時間がかかる場合があるため

まとめ

今回の記事では、新着文献のチェック、ダウンロードという部分を自動化するスクリプトを紹介しました。

しかもメールで送付する機能も実装されているため、自宅や学外にいる時でも新着文献を読むことが可能です。

このように日々行う退屈なルーティーンワーク作業は全部 python にやらせて、もっとクリエイティブな作業に時間を割くようにしましょう!

参考文献

  1. Pythonからファイル添付したメール送信
管理人: