無料で論文検索を自動化するために使うAPIやライブラリは何がいいの？

# 導入

欲しいテーマの論文を探してまとめたり要約したりする作業は大変ですよね。。ということで論文の取得と要約をしてくれるように、自動化する機会があったのでまとめさせてください。

先に結論だけお伝えすると、以下の3つを要件に合うものとして論文検索に活用しています。

- arXiv
- PubMed
- scholarly

今回は次の要件に合うようなAPIやライブラリを探して、論文の要約を自動でできるようになるところまで実装して紹介します。無料で使えても数回使っただけで試用期間が終わってしまうものもあったので、実用性のある回数使えるものに絞りたいと思います。

- **無料で使えること**
- できるかぎりたくさん実行できること

# 無料で使える論文検索APIとライブラリの一覧

調べた限り無料で使える論文検索のAPIとライブラリがこちらになります。今回は時間の都合もあり、使えそうなものでも結果を検証しきれなかったものは消去しています。

- arXiv
- PubMed
- Semantic Scholar
- Scopus
- scholarAI
- scholarly
- ~~IEEE Xplore~~
- ~~SpringerLink~~
- ~~CrossRef~~
- ~~Directory of Open Access Journals (DOAJ)~~

## 要件に合致する(しそうな)API

上記の一覧のうち要件に合致する(しそうな)APIのリンク、概要、アクセス制限をまとめます。同じく時間の都合で結果を検証しきれなかったものは消去しています。

- arXiv
    - https://arxiv.org/
    - 物理学、数学、計算機科学、統計学など、幅広い分野の論文が収録されており、特に理論的な研究や技術的な進展に強みがある
    - 3秒あたり1リクエストまで無料
- PubMed
    - https://www.ncbi.nlm.nih.gov/home/develop/api/
    - 医学、看護、薬学、生命科学に関連する論文が豊富に収録されており、特に臨床研究や基礎研究に強みがある
    - アカウント登録無しでの実行だと秒間3アクセス、アカウント登録ありだと秒間10アクセスが無料
- scholarly
    - https://github.com/scholarly-python-package/scholarly
    - googleScholarの検索結果を返してくれるライブラリです。requirements.txtにseleniumやbeautifulsoup4が記載されているのでスクレイピング結果を返してくれているのだと思います。
    - ライブラリのため無料
- ~~IEEE Xplore~~
    - https://developer.ieee.org/docs/read/Home
    - 電気工学、コンピュータサイエンス、電子工学に強みがある
    - 1秒あたり10回、1日あたり200回まで無料
- ~~SpringerLink~~
    - https://dev.springernature.com/docs/api-endpoints/open-access/
    - 科学、技術、医学、人文社会科学などの広範な分野にわたる学術文献にアクセスできる
    - 月500リクエストまで無料

## 他のAPIの除外理由

- Semantic Scholar
    - APIキーの発行が一時停止中
- Scopus
    - キーワードに対して期待している論文タイトルと関連の低いタイトルを取得している
        - machine learningというキーワードに対してLGBTQの論文が返ってきたり…
    - 取得できた論文URLに本文が載っていない時がある
- scholarAI
    - 無料実行回数が5回まで

# 実行スクリプト

要件に合致するAPIとライブラリのうちarXiv, PubMed, scholarlyを通して論文のタイトルとURL、要約、著者を取得します。今回はライブラリが豊富なpythonを使っていきます。

arXiv

```python
import arxiv

def get_paper_info_from_arXiv(keyword):
    client = arxiv.Client()

    # keywordをqueryに変換、要約を対象にすべてOR検索とする
    query = ' OR '.join([f'abs:{kw}' for kw in keyword])

    # 検索実行、5件を取得、ソートは投稿日時順
    search = arxiv.Search(
        query = query,
        max_results = 5,
        sort_by=arxiv.SortCriterion.SubmittedDate
    )

    results = client.results(search)
    papers = []
    # 返却する配列の生成
    for result in list(results):
        papers.append({
            'title': result.title,
            'authors': [author.name for author in result.authors],
            'summary': result.summary,
            'url': result.entry_id
        })
    return papers

keyword = ['wellbeing', 'cancer']
print(get_paper_info_from_arXiv(keyword))
```

PubMed

```python
import requests
import time
from lxml import etree

def get_paper_info(keywords, retstart, retmax):
    def format_keywords(keywords):
        return ' OR '.join(f'({keyword})' for keyword in keywords)

    term = format_keywords(keywords)
    print(term)
    
    # PMIDを取得
    URL = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&retmode=json'
    option = '&retstart='+str(retstart)+'&retmax='+str(retmax)+'&term='+term+'&sort=relevance'
    query = URL + option
    response = requests.get(query)
    response_json = response.json()
    pmids = response_json['esearchresult']['idlist']

    # 論文情報を取得
    URL_summary = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&retmode=json&id='
    queries_summary = [URL_summary + pmid for pmid in pmids]
    responses_summary = {}
    for query in queries_summary:
        response = requests.get(query)
        res_json = response.json()['result']
        responses_summary.update(res_json)
        time.sleep(0.5)

    # アブストラクトを取得
    URL_fetch = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&retmode=xml&id='
    queries_fetch = [URL_fetch + pmid for pmid in pmids]
    responses_abst = {}
    for query in queries_fetch:
        response = requests.get(query)
        root = etree.fromstring(response.content)
        pmid = root.find('.//PMID').text
        abst = root.findall('.//AbstractText')
        if abst is None:
            abst_text = ''
        else:
            abst_text = ''.join(root.xpath('//Abstract//*/text()'))
        responses_abst[pmid] = abst_text
        time.sleep(0.5)

    # 返却する配列の生成
    papers = []
    for pmid in pmids:
        paper = {
            'title': responses_summary[pmid]['title'],
            'url': f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/",
            'summary': responses_abst[pmid],
            'authors': [responses_summary[pmid]['sortfirstauthor']],
        }
        papers.append(paper)

    return papers
    
# 複数のキーワードをOR条件で検索
keyword = ['wellbeing', 'cancer']
print(get_paper_info(keyword, 0, 5))
```

scholarly

```python
from scholarly import scholarly

def get_paper_info_from_scholarly(keyword):
    search_query = scholarly.search_pubs(keyword)
    
    papers = []
    for i in range(5):  # 最初の5件を取得
        paper = next(search_query)
        print(paper)
        papers.append({
            'title': paper['bib']['title'],
            'summary': paper['bib'].get('abstract', 'No summary available'),
            'url': paper['eprint_url'] if 'eprint_url' in paper else 'No URL available',
            'authors': paper['bib']['author']
        })
    return papers

keyword = '"wellbeing" OR "cancer"'
papers = get_paper_info_from_scholarly(keyword)
for paper in papers:
    print(f"Title: {paper['title']}")
    print(f"Summary: {paper['summary']}")
    print(f"URL: {paper['url']}")
    print(f"Authors: {paper['authors']}")
    print()
```

# まとめ

pubmedとarXivは専門領域に特化した論文を取得するため、広く情報を取ってきたいときはgoogleScolarの情報を返してくれるscholarlyのライブラリを使うのが良いかもしれません。

今回の趣旨とは違うので余談にはなるのですが、APIを使わない論文検索であればconsensusというサービスを使って検索すると関連度の高い情報が返ってくると感じました。

https://consensus.app/search/

# 参考記事

https://qiita.com/JoCOOL/items/d1b727a39b458c947d5f