关于 openalex-database
openalex-database 是通过 OpenAlex REST API 查询学术记录的技能,适用于跨学科文献检索、引用分析、作者查询和元数据补充。
主要功能
- 01按条件搜索作品并分页
- 02通过 DOI 或 OpenAlex ID 查询作品
- 03查找作者及 ORCID 记录
- 04获取引用网络
- 05分析概念、机构和出版物
适用场景
- 01构建系统性文献综述语料
- 02绘制引用与合著网络
- 03补充 DOI 列表的论文元数据
- 04按主题或国家追踪研究产出
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill openalex-database安装后在 Claude Code 中输入/openalex-database确认已加载。
详细介绍
OpenAlex 学术数据库
概述
OpenAlex 是一个免费、开放访问的索引,收录了 2.5 亿多篇学术著作、9,000 多万名作者、11 万多种期刊和 1 万多家机构。它接替了 Microsoft Academic Graph,并提供丰富的元数据:摘要、开放获取 URL、引用次数、参考文献、作者消歧 ID(ORCID)和概念标签。REST API 每天最多可免费使用 100,000 次,无需身份验证;加入礼貌访问池(通过 email 参数)可获得优先处理。
适用场景
- 搜索所有学术领域(不只是生物医学),构建系统性文献综述语料库
- 检索引文网络,用于文献计量分析、共被引聚类或参考文献图遍历
- 使用 ORCID/OpenAlex 作者 ID 跨机构消歧作者身份
- 为一组 DOI 查找开放获取全文 URL,以构建可下载的论文语料库
- 按年份、机构、国家或研究概念分析发表趋势
- 通过 DOI 或标题丰富论文列表的元数据(引用次数、摘要、发表载体)
- PubMed 收录的生物医学文献请使用
pubmed-database;bioRxiv 预印本请使用biorxiv-database
前置条件
- Python 包:
requests、pandas - 数据要求:DOI、OpenAlex Work ID(W…)、作者姓名、ORCID ID 或搜索词
- 环境:互联网连接;无需 API 密钥
- 速率限制:匿名访问为 10 req/s;添加
[email protected]查询参数即可加入礼貌访问池(优先级更高,限制相同)
pip install requests pandas
快速开始
import requests
BASE = "https://api.openalex.org"
# Search for works on CRISPR
r = requests.get(f"{BASE}/works",
params={"search": "CRISPR gene editing",
"filter": "publication_year:2023",
"per_page": 5,
"mailto": "[email protected]"})
r.raise_for_status()
data = r.json()
print(f"Total results: {data['meta']['count']}")
for work in data["results"][:3]:
print(f" {work['title'][:80]} ({work['publication_year']}) cites={work['cited_by_count']}")
核心 API
查询 1:搜索著作
按标题/摘要关键词和筛选条件搜索著作。
import requests, pandas as pd
BASE = "https://api.openalex.org"
def search_works(query, filters=None, per_page=25, mailto="[email protected]"):
params = {"search": query, "per_page": per_page, "mailto": mailto}
if filters:
params["filter"] = ",".join(f"{k}:{v}" for k, v in filters.items())
r = requests.get(f"{BASE}/works", params=params)
r.raise_for_status()
return r.json()
# Search with filters
data = search_works("single-cell RNA sequencing",
filters={"publication_year": "2020-2024",
"open_access.is_oa": "true"},
per_page=10)
print(f"Open-access scRNA-seq papers 2020-2024: {data['meta']['count']}")
rows = []
for w in data["results"]:
rows.append({
"title": w["title"],
"year": w["publication_year"],
"citations": w["cited_by_count"],
"doi": w.get("doi"),
"oa_url": w.get("open_access", {}).get("oa_url"),
})
df = pd.DataFrame(rows)
print(df[["title", "year", "citations"]].head())
# Paginate through all results
def paginate_works(query, filters=None, max_results=200, mailto="[email protected]"):
"""Retrieve up to max_results works, paginating automatically."""
all_results = []
cursor = "*"
while len(all_results) < max_results:
params = {"search": query, "per_page": 200,
"cursor": cursor, "mailto": mailto}
if filters:
params["filter"] = ",".join(f"{k}:{v}" for k, v in filters.items())
r = requests.get(f"{BASE}/works", params=params)
data = r.json()
all_results.extend(data["results"])
cursor = data["meta"].get("next_cursor")
if not cursor:
break
return all_results[:max_results]
papers = paginate_works("transformer protein structure", max_results=100)
print(f"Retrieved {len(papers)} papers")
查询 2:按 DOI 或 OpenAlex ID 查找
通过 DOI 或 OpenAlex ID 检索单篇著作。
import requests
BASE = "https://api.openalex.org"
# By DOI
doi = "10.1038/s41592-019-0458-z" # Scanpy paper
r = requests.get(f"{BASE}/works/https://doi.org/{doi}",
params={"mailto": "[email protected]"})
r.raise_for_status()
work = r.json()
print(f"Title : {work['title']}")
print(f"Year : {work['publication_year']}")
print(f"Citations: {work['cited_by_count']}")
print(f"Journal : {work.get('primary_location', {}).get('source', {}).get('display_name')}")
abstract = work.get("abstract_inverted_index")
if abstract:
# Reconstruct abstract from inverted index
words = {pos: word for word, positions in abstract.items() for pos in positions}
text = " ".join(words[i] for i in sorted(words))
print(f"Abstract (first 200): {text[:200]}")
查询 3:作者搜索和 ORCID 查找
查找作者记录、解析 ORCID 标识符并检索发表作品列表。
import requests, pandas as pd
BASE = "https://api.openalex.org"
# Search for an author
r = requests.get(f"{BASE}/authors",
params={"search": "Jennifer Doudna",
"per_page": 5,
"mailto": "[email protected]"})
authors = r.json()["results"]
for a in authors[:3]:
print(f"Author: {a['display_name']}")
print(f" OpenAlex ID : {a['id']}")
print(f" ORCID : {a.get('orcid', 'n/a')}")
# 2024+: singular `last_known_institution` was replaced by plural list `last_known_institutions[0]`
insts = a.get("last_known_institutions") or []
print(f" Institution : {insts[0]['display_name'] if insts else 'n/a'}")
print(f" Works count : {a['works_count']}")
print(f" h-index : {a['summary_stats'].get('h_index', 'n/a')}")
print()
# Get all papers by an author (by ORCID)
orcid = "0000-0001-9161-999X" # Jennifer A. Doudna (correct ORCID; the 8742-3594 variant 404s)
r = requests.get(f"{BASE}/works",
params={"filter": f"author.orcid:{orcid}",
"sort": "cited_by_count:desc",
"per_page": 10,
"mailto": "[email protected]"})
papers = r.json()["results"]
for p in papers[:5]:
print(f" [{p['publication_year']}] {p['title'][:70]} (cites: {p['cited_by_count']})")
查询 4:检索引文网络
获取论文的参考文献和施引作品。
import requests, pandas as pd
BASE = "https://api.openalex.org"
work_id = "W2018426904" # CRISPR paper
# Get what this paper references
r = requests.get(f"{BASE}/works/{work_id}",
params={"select": "referenced_works,cited_by_count,title",
"mailto": "[email protected]"})
work = r.json()
ref_ids = work.get("referenced_works", [])
print(f"'{work['title']}' cites {len(ref_ids)} papers")
print(f"Total citations: {work['cited_by_count']}")
# Fetch metadata for references (batch)
if ref_ids:
ids_str = "|".join(id.split("/")[-1] for id in ref_ids[:10])
r2 = requests.get(f"{BASE}/works",
params={"filter": f"openalex_id:{ids_str}",
"per_page": 10,
"mailto": "[email protected]"})
refs = r2.json()["results"]
for ref in refs[:5]:
print(f" [{ref['publication_year']}] {ref['title'][:70]}")
查询 5:概念/主题筛选与趋势分析
按研究概念筛选并分析发表趋势。
import requests, pandas as pd
BASE = "https://api.openalex.org"
# Get concept ID for "Machine Learning". OpenAlex concept search is brittle for
# multi-word phrases ("machine learning biology" returns 0); use the single core term.
r = requests.get(f"{BASE}/concepts",
params={"search": "machine learning",
"per_page": 3,
"mailto": "[email protected]"})
concepts = r.json()["results"]
for c in concepts[:3]:
print(f"Concept: {c['display_name']} (ID: {c['id']}, level: {c['level']})")
# Count papers per year for a concept
concept_id = "C154945302" # Machine learning (OpenAlex ID)
r2 = requests.get(f"{BASE}/works",
params={"filter": f"concepts.id:{concept_id},publication_year:2015-2024",
"group_by": "publication_year",
"per_page": 200,
"mailto": "[email protected]"})
groups = r2.json()["group_by"]
df = pd.DataFrame(groups).rename(columns={"key": "year", "count": "papers"})
df = df.sort_values("year")
print(df.tail(5).to_string(index=False))
查询 6:机构和发表载体查询
检索特定机构、期刊或会议发表的论文。
import requests, pandas as pd
BASE = "https://api.openalex.org"
# Papers from a specific journal in the last year
r = requests.get(f"{BASE}/works",
params={
"filter": "primary_location.source.issn:0028-0836,publication_year:2023",
"per_page": 10,
"sort": "cited_by_count:desc",
"mailto": "[email protected]"
})
data = r.json()
print(f"Nature papers 2023: {data['meta']['count']}")
for w in data["results"][:5]:
print(f" [{w['cited_by_count']} cites] {w['title'][:70]}")
核心概念
倒排索引摘要
由于版权限制,OpenAlex 将摘要存储为倒排索引(词 → 位置列表),而非纯文本。可通过以下方式重建:" ".join(words[i] for i in sorted({pos: w for w, ps in inv.items() for pos in ps}))。
基于游标的分页
OpenAlex 使用基于游标的分页(cursor 参数),而非偏移量分页。请以 cursor="*" 开始,并使用每次响应中的 next_cursor。每页最多返回 200 条结果;游标分页最多支持 10,000 条结果。
常见工作流
工作流 1:系统性文献检索
目标:下载所有符合主题查询的论文及其元数据,用于系统性综述。
import requests, time, pandas as pd
BASE = "https://api.openalex.org"
MAILTO = "[email protected]"
def systematic_search(query, year_from, year_to, max_results=500):
"""Paginate through results and return a DataFrame."""
all_results = []
cursor = "*"
filters = f"publication_year:{year_from}-{year_to}"
while len(all_results) < max_results:
r = requests.get(f"{BASE}/works",
params={"search": query, "filter": filters,
"per_page": 200, "cursor": cursor,
"mailto": MAILTO,
"select": "id,doi,title,publication_year,cited_by_count,open_access"})
r.raise_for_status()
data = r.json()
all_results.extend(data["results"])
cursor = data["meta"].get("next_cursor")
if not cursor:
break
time.sleep(0.1)
rows = []
for w in all_results[:max_results]:
rows.append({
"openalex_id": w["id"],
"doi": w.get("doi"),
"title": w.get("title"),
"year": w.get("publication_year"),
"citations": w.get("cited_by_count"),
"is_oa": w.get("open_access", {}).get("is_oa"),
"oa_url": w.get("open_access", {}).get("oa_url"),
})
return pd.DataFrame(rows)
# Example: papers on drug repurposing 2019-2024
df = systematic_search("drug repurposing machine learning", 2019, 2024, max_results=200)
df.to_csv("drug_repurposing_literature.csv", index=False)
print(f"Retrieved {len(df)} papers")
print(df[["title", "year", "citations", "is_oa"]].head(5).to_string(index=False))
工作流 2:作者合作网络
目标:梳理某位研究者的合著者,分析其合作网络。
import requests, time, pandas as pd
from collections import defaultdict
BASE = "https://api.openalex.org"
MAILTO = "[email protected]"
def get_author_works(orcid, max_papers=50):
r = requests.get(f"{BASE}/works",
params={"filter": f"author.orcid:{orcid}",
"sort": "cited_by_count:desc",
"per_page": min(max_papers, 200),
"mailto": MAILTO})
r.raise_for_status()
return r.json()["results"]
def extract_collaborators(works):
collab_count = defaultdict(int)
for work in works:
for authorship in work.get("authorships", []):
author = authorship.get("author", {})
name = author.get("display_name")
if name:
collab_count[name] += 1
return collab_count
# Map collaborators for a researcher
orcid = "0000-0001-9161-999X" # Jennifer A. Doudna
works = get_author_works(orcid, max_papers=50)
collabs = extract_collaborators(works)
top_collabs = sorted(collabs.items(), key=lambda x: -x[1])
df = pd.DataFrame(top_collabs, columns=["collaborator", "papers_together"])
df = df[df["collaborator"] != "Jennifer A. Doudna"] # exclude self
print("Top collaborators:")
print(df.head(10).to_string(index=False))
df.to_csv("collaboration_network.csv", index=False)
关键参数
| 参数 | 模块 | 默认值 | 范围 / 选项 | 效果 |
|---|---|---|---|---|
search | 全部 | — | 文本字符串 | 在标题和摘要中进行全文搜索 |
filter | 全部 | — | field:value,field:value | 结构化筛选(AND 逻辑) |
per_page | 全部 | 25 | 1–200 | 每页结果数 |
cursor | 分页 | "*" | 游标字符串 | 分页游标 |
sort | 著作 | relevance | cited_by_count:desc、publication_year:desc | 结果排序 |
select | 全部 | 所有字段 | 逗号分隔的字段名称 | 限制响应字段(速度更快) |
group_by | 著作 | — | 字段名称 | 按字段汇总计数 |
mailto | 全部 | — | 电子邮件地址 | 访问礼貌访问池(优先处理) |
最佳实践
-
始终包含
mailto:在所有请求中添加[email protected],即可加入礼貌请求池并获得优先处理,且不会受到速率限制。 -
大规模分页时使用
select:遍历数千条结果时,仅指定所需字段(select=id,doi,title,cited_by_count),以减小响应体并加快解析速度。 -
使用游标分页,而非偏移量分页:OpenAlex 不支持超过 10,000 条结果的偏移量分页。进行深度遍历时,请使用基于游标的分页(
cursor参数)。 -
从倒排索引重建摘要:并非所有作品都有摘要;重建前请检查
abstract_inverted_index is not None,以避免 KeyError。 -
按作品 ID 缓存:OpenAlex Work ID(W…)是稳定标识符。缓存检索到的作品元数据,避免在同一项目中重复获取。
常见用法
用法:批量查询 DOI 对应的元数据
适用场景:为一组 DOI 补充引用次数、开放获取 URL 和摘要。
import requests, pandas as pd, time
BASE = "https://api.openalex.org"
dois = [
"10.1038/s41592-019-0458-z",
"10.1186/s13059-021-02519-4",
"10.1038/s41587-019-0071-9",
]
rows = []
for doi in dois:
r = requests.get(f"{BASE}/works/https://doi.org/{doi}",
params={"select": "title,publication_year,cited_by_count,open_access",
"mailto": "[email protected]"})
if r.ok:
w = r.json()
rows.append({
"doi": doi, "title": w.get("title"),
"year": w.get("publication_year"),
"citations": w.get("cited_by_count"),
"is_oa": w.get("open_access", {}).get("is_oa"),
})
time.sleep(0.1)
df = pd.DataFrame(rows)
print(df.to_string(index=False))
用法:按国家统计论文数量
适用场景:对某个主题的研究产出进行地理分析。
import requests, pandas as pd
r = requests.get(
"https://api.openalex.org/works",
params={"search": "CRISPR therapeutics",
"filter": "publication_year:2023",
"group_by": "authorships.institutions.country_code",
"per_page": 200,
"mailto": "[email protected]"}
)
df = pd.DataFrame(r.json()["group_by"]).rename(columns={"key": "country", "count": "papers"})
print(df.sort_values("papers", ascending=False).head(10).to_string(index=False))
用法:查找某个领域中引用次数最多的论文
适用场景:确定某个主题的代表性论文,以供背景阅读。
import requests, pandas as pd
r = requests.get(
"https://api.openalex.org/works",
params={"search": "protein language model",
"sort": "cited_by_count:desc",
"per_page": 10,
"mailto": "[email protected]"}
)
for w in r.json()["results"]:
print(f"[{w['cited_by_count']:5d} cites] ({w['publication_year']}) {w['title'][:70]}")
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
HTTP 429 Too Many Requests | 超出速率限制 | 在请求之间添加 time.sleep(0.15);使用礼貌请求池(mailto) |
abstract_inverted_index 为空 | 没有可用摘要 | 重建前检查是否为 None;并非所有作品都有摘要 |
| 游标分页返回重复结果 | 游标已过期 | 使用 cursor="*" 重新开始分页 |
| DOI 查询返回 404 | OpenAlex 中未收录该 DOI | 改用标题搜索;OpenAlex 收录了 250M+ 条记录,但并未涵盖 100% 的文献 |
| 筛选器返回 0 条结果 | 字段名称错误或筛选语法有误 | 检查筛选语法:field:value,中间不能有空格;在 API 文档中确认字段名称 |
cited_by_count 已过时 | 引用次数会定期更新 | 引用次数会定期刷新,但可能延迟数天;适合用于分析趋势,不适合用作精确数据 |
相关技能
pubmed-database— 使用 MeSH 受控词表的生物医学文献;更适用于临床和生命科学领域biorxiv-database— 尚未被 OpenAlex 收录的生物医学预印本scientific-brainstorming— 将文献作为输入的假设生成工作流literature-review— 使用 OpenAlex 设计系统性文献综述的指南
参考资料
- OpenAlex 文档 — 完整的 API 参考和数据模型
- OpenAlex API 端点 — 交互式 API 浏览器
- OpenAlex 论文(Priem 等人,2022) — OpenAlex 数据系统说明
- OpenAlex 实体类型 — Works、Authors、Sources、Institutions、Concepts 文档