academic-citation-manager について
BibTeXを基準形式として学術引用を管理するスキルです。DOIなどからのメタデータ取得、形式変換、重複処理、検証に対応し、LaTeXやMarkdownの執筆ワークフローに利用できます。
主な機能
- 01DOIなどから引用メタデータを取得
- 02複数の引用形式を相互変換
- 03重複エントリーの検出と統合
- 04CrossRefとOpenAlexによる検証
- 05書式付き参考文献の生成
活用シーン
- 01複数の情報源の文献を整理
- 02投稿先に応じた引用形式の切り替え
- 03DOI・年・重複エントリーの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wentorai/ research-plugins --skill academic-citation-managerインストール後、Claude Code で/academic-citation-managerを入力して読み込まれたか確認します。
詳細
学術文献引用マネージャー
BibTeX、APA 第7版、MLA 第9版、シカゴ、バンクーバー、IEEE など、複数の形式で学術文献の引用を管理できます。DOI からの自動取得、形式間の変換、重複の排除、検証に対応します。このスキルは、文献情報の取得から原稿の最終的な書式設定まで、引用のライフサイクル全体を扱います。
概要
引用の管理は、学術論文の執筆において継続的な負担となっています。研究者は、データベース、PDF、同僚、ウェブページなど複数のソースから文献を集め、異なる形式で保存し、投稿先の各ジャーナルが指定するスタイルで出力する必要があります。著者名の誤記、年の間違い、無効な DOI、書式の不統一といった引用の誤りは、編集部での却下や査読者からの指摘のよくある原因です。
このスキルは、GUI の文献管理ツールが提供する範囲を超えた、包括的な文献引用の管理ワークフローを提供します。数秒で DOI から完全なメタデータを取得し、任意の引用形式間で変換し、重複項目を検出して統合し、CrossRef および OpenAlex のデータベースと照合して項目を検証し、主要な引用スタイルに対応した書誌を適切な書式で生成できます。
テキストベースでスクリプト化できるため、LaTeX のワークフロー、Markdown の執筆パイプライン、自動文書生成との連携に最適です。すべての引用データは標準的な BibTeX 形式で正本として保存し、原稿の要件に応じて必要な形式へ変換できます。
引用情報の取得
DOI から
import requests
def get_bibtex_from_doi(doi):
"""Retrieve BibTeX entry from a DOI via CrossRef."""
url = f"https://doi.org/{doi}"
headers = {"Accept": "application/x-bibtex"}
response = requests.get(url, headers=headers, allow_redirects=True)
if response.status_code == 200:
return response.text
return None
# Example
bibtex = get_bibtex_from_doi("10.1038/s41586-021-03819-2")
print(bibtex)
# @article{Jumper_2021,
# title={Highly accurate protein structure prediction with AlphaFold},
# author={Jumper, John and Evans, Richard and ...},
# journal={Nature},
# volume={596},
# pages={583--589},
# year={2021},
# publisher={Springer}
# }
OpenAlex から
def get_citation_from_openalex(work_id):
"""Retrieve citation data from OpenAlex API."""
url = f"https://api.openalex.org/works/{work_id}"
headers = {"User-Agent": "ResearchPlugins/1.0 (https://wentor.ai)"}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
return format_as_bibtex(data)
return None
def format_as_bibtex(oa_data):
"""Convert OpenAlex data to BibTeX."""
authorships = oa_data.get("authorships", [])
author_str = " and ".join(a["author"]["display_name"] for a in authorships)
first_author = authorships[0]["author"]["display_name"].split()[-1] if authorships else "Unknown"
year = str(oa_data.get("publication_year", ""))
key = f"{first_author}_{year}"
venue = oa_data.get("primary_location", {}) or {}
journal = (venue.get("source") or {}).get("display_name", "")
return f"""@article{{{key},
title={{{oa_data.get('title', '')}}},
author={{{author_str}}},
year={{{year}}},
journal={{{journal}}},
doi={{{oa_data.get('doi', '')}}}
}}"""
arXiv ID から
def get_bibtex_from_arxiv(arxiv_id):
"""Retrieve BibTeX from arXiv."""
import feedparser
url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}"
feed = feedparser.parse(url)
if feed.entries:
entry = feed.entries[0]
authors = " and ".join(a["name"] for a in entry.authors)
first_author = entry.authors[0]["name"].split()[-1]
year = entry.published[:4]
return f"""@article{{{first_author}_{year},
title={{{entry.title.replace(chr(10), ' ')}}},
author={{{authors}}},
year={{{year}}},
journal={{arXiv preprint arXiv:{arxiv_id}}},
url={{https://arxiv.org/abs/{arxiv_id}}}
}}"""
return None
形式の変換
BibTeX から APA 第7版へ
def bibtex_to_apa7(entry):
"""Convert a parsed BibTeX entry to APA 7th edition format."""
authors = format_apa_authors(entry["author"])
year = entry.get("year", "n.d.")
title = entry["title"]
journal = entry.get("journal", "")
volume = entry.get("volume", "")
issue = entry.get("number", "")
pages = entry.get("pages", "")
doi = entry.get("doi", "")
# Article format
citation = f"{authors} ({year}). {title}. "
if journal:
citation += f"*{journal}*"
if volume:
citation += f", *{volume}*"
if issue:
citation += f"({issue})"
if pages:
citation += f", {pages}"
citation += "."
if doi:
citation += f" https://doi.org/{doi}"
return citation
def format_apa_authors(author_string):
"""Format author names in APA style: Last, F. M."""
authors = [a.strip() for a in author_string.split(" and ")]
formatted = []
for author in authors:
parts = author.split(", ") if ", " in author else author.rsplit(" ", 1)[::-1]
if len(parts) >= 2:
last = parts[0]
firsts = parts[1].split()
initials = " ".join(f"{f[0]}." for f in firsts)
formatted.append(f"{last}, {initials}")
else:
formatted.append(parts[0])
if len(formatted) == 1:
return formatted[0]
elif len(formatted) == 2:
return f"{formatted[0]}, & {formatted[1]}"
elif len(formatted) <= 20:
return ", ".join(formatted[:-1]) + f", & {formatted[-1]}"
else:
return ", ".join(formatted[:19]) + f", ... {formatted[-1]}"
形式の例
同じ文献を異なるスタイルで表記した例です。
BibTeX:
@article{Jumper_2021,
title={Highly accurate protein structure prediction with AlphaFold},
author={Jumper, John and Evans, Richard and Pritzel, Alexander},
journal={Nature},
volume={596},
pages={583--589},
year={2021},
doi={10.1038/s41586-021-03819-2}
}
APA 第7版: Jumper, J., Evans, R., & Pritzel, A. (2021). Highly accurate protein structure prediction with AlphaFold. Nature, 596, 583-589. https://doi.org/10.1038/s41586-021-03819-2
MLA 第9版: Jumper, John, Richard Evans, and Alexander Pritzel. "Highly Accurate Protein Structure Prediction with AlphaFold." Nature, vol. 596, 2021, pp. 583-89.
シカゴ(著者・年方式): Jumper, John, Richard Evans, and Alexander Pritzel. 2021. "Highly Accurate Protein Structure Prediction with AlphaFold." Nature 596: 583-89.
バンクーバー: Jumper J, Evans R, Pritzel A. Highly accurate protein structure prediction with AlphaFold. Nature. 2021;596:583-9.
IEEE: J. Jumper, R. Evans, and A. Pritzel, "Highly accurate protein structure prediction with AlphaFold," Nature, vol. 596, pp. 583-589, 2021.
重複の排除
重複項目の検出
from difflib import SequenceMatcher
def find_duplicates(bib_entries, threshold=0.85):
"""Find duplicate BibTeX entries by title similarity."""
duplicates = []
titles = [(key, normalize_title(entry["title"]))
for key, entry in bib_entries.items()]
for i in range(len(titles)):
for j in range(i + 1, len(titles)):
similarity = SequenceMatcher(
None, titles[i][1], titles[j][1]
).ratio()
if similarity >= threshold:
duplicates.append({
"entry_a": titles[i][0],
"entry_b": titles[j][0],
"similarity": similarity
})
return duplicates
def normalize_title(title):
"""Normalize title for comparison."""
import re
title = title.lower()
title = re.sub(r'[{}\\]', '', title) # Remove LaTeX formatting
title = re.sub(r'[^a-z0-9\s]', '', title) # Remove punctuation
title = ' '.join(title.split()) # Normalize whitespace
return title
def merge_duplicates(entry_a, entry_b):
"""Merge two duplicate entries, preferring the more complete one."""
merged = {}
all_fields = set(list(entry_a.keys()) + list(entry_b.keys()))
for field in all_fields:
val_a = entry_a.get(field, "")
val_b = entry_b.get(field, "")
# Prefer the longer (more complete) value
merged[field] = val_a if len(str(val_a)) >= len(str(val_b)) else val_b
return merged
検証
CrossRef による検証
def validate_citation(doi):
"""Validate a citation against CrossRef metadata."""
url = f"https://api.crossref.org/works/{doi}"
response = requests.get(url)
if response.status_code != 200:
return {"valid": False, "error": "DOI not found in CrossRef"}
data = response.json()["message"]
return {
"valid": True,
"title": data.get("title", [None])[0],
"authors": [f"{a.get('family', '')}, {a.get('given', '')}"
for a in data.get("author", [])],
"year": data.get("published-print", {}).get("date-parts", [[None]])[0][0],
"journal": data.get("container-title", [None])[0],
"type": data.get("type", "unknown")
}
よくある引用の誤り
| 誤り | 検出方法 | 修正方法 |
|---|---|---|
| DOI がない | doi フィールドが空かどうかを確認します | タイトルから CrossRef を検索します |
| 年が違う | CrossRef と照合します | CrossRef の年を使用します |
| 著者名の表記揺れ | ORCID とファジーマッチングします | ORCID の名前に統一します |
| 重複項目 | タイトルの類似度が 85% を超えています | 1つの項目に統合します |
| 無効な URL | HTTP HEAD リクエストが 4xx/5xx を返します | URL を更新するか削除します |
| 項目が不完全 | スタイルに必要なフィールドがありません | DOI から取得します |
執筆ツールとの連携
LaTeX
% In document preamble
\usepackage[backend=biber,style=apa]{biblatex}
\addbibresource{references.bib}
% In text
\textcite{Jumper_2021} showed that...
As demonstrated by previous work \parencite{Jumper_2021}...
% At end of document
\printbibliography
Pandoc Markdown
Previous work [@Jumper_2021] showed that...
## References
pandoc paper.md --citeproc --bibliography=references.bib \
--csl=apa.csl -o paper.pdf
参考資料
- CrossRef API:https://api.crossref.org
- OpenAlex API:https://api.openalex.org
- APA 第7版マニュアル:https://apastyle.apa.org/products/publication-manual-7th-edition
- BibTeX ドキュメント:http://www.bibtex.org
- CSL スタイルリポジトリ:https://github.com/citation-style-language/styles