用Python批量获取PubMed文献并整理成Excel
准备投一篇生信方法学文章,Introduction 里需要交代领域背景。在 PubMed 搜了一圈,相关文献大概两三百篇,想系统过一遍,但手动复制粘贴到 Excel 实在太折磨了。
于是写了个小脚本,输入关键词直接导出 Excel,包含 PMID、标题、摘要、作者、期刊、年份、DOI,字段整齐,可以直接筛选排序。代码不到 80 行,分享出来。
环境准备
pip install biopython pandas openpyxl
biopython 封装了 PubMed 官方 API,pandas 负责写 Excel。
另外建议去 NCBI 申请一个 API Key,免费的。有 Key 下载速度快 3 倍多,没 Key 也能跑,就是慢一点。
完整代码
保存成 .py 文件,改好配置区参数直接跑:
"""
PubMed 文献批量下载 → Excel
用法:改好配置区,直接 python 运行
"""
from Bio import Entrez
import pandas as pd
import time
# ============ 改成你自己的 ============
Entrez.email = "your.email@example.com"
Entrez.api_key = "your_api_key_here" # 没有 Key 删掉这行,能跑但慢
QUERY = "single cell RNA-seq tumor microenvironment"
MAX_RESULTS = 200
OUTPUT = "pubmed_results.xlsx"
# =======================================
def search_pubmed(query, max_results):
"""搜索 PubMed,拿到 PMID 列表"""
handle = Entrez.esearch(
db="pubmed",
term=query,
retmax=max_results,
sort="relevance"
)
record = Entrez.read(handle)
handle.close()
return record["IdList"]
def parse_article(article):
"""从 PubMed 返回的 XML 里提取信息"""
medline = article['MedlineCitation']
data = medline['Article']
# 摘要:有的分段(列表),有的整段(字符串),有的干脆没有
abstract = ""
if 'Abstract' in data:
parts = data['Abstract'].get('AbstractText', [])
if isinstance(parts, list):
abstract = " ".join(str(p) for p in parts)
else:
abstract = str(parts)
# 作者
authors = []
for a in data.get('AuthorList', []):
last = a.get('LastName', '')
first = a.get('ForeName', a.get('Initials', ''))
if last:
authors.append(f"{last} {first}".strip())
# 年份:有的文献没有 Year,只有 MedlineDate
pub_date = data['Journal']['JournalIssue'].get('PubDate', {})
year = pub_date.get('Year', pub_date.get('MedlineDate', '')[:4])
# DOI:藏在 ArticleIdList 里,得遍历找
doi = ""
for item in article.get('PubmedData', {}).get('ArticleIdList', []):
if item.attributes.get('IdType') == 'doi':
doi = str(item)
break
return {
'PMID': str(medline['PMID']),
'Title': data.get('ArticleTitle', ''),
'Abstract': abstract,
'Authors': ', '.join(authors),
'Journal': data['Journal']['Title'],
'Year': year,
'DOI': doi
}
def main():
pmids = search_pubmed(QUERY, MAX_RESULTS)
if not pmids:
print("没搜到文献,换个关键词试试?")
return
print(f"搜到 {len(pmids)} 篇,开始下载...")
articles = []
for i in range(0, len(pmids), 200):
batch = pmids[i:i+200]
print(f" 第 {i+1}-{i+len(batch)} 篇...")
handle = Entrez.efetch(
db="pubmed",
id=",".join(batch),
rettype="xml",
retmode="xml"
)
records = Entrez.read(handle)
handle.close()
for article in records.get('PubmedArticle', []):
articles.append(parse_article(article))
time.sleep(0.15)
df = pd.DataFrame(articles)
df.to_excel(OUTPUT, index=False, engine='openpyxl')
print(f"搞定,{len(articles)} 篇已保存到 {OUTPUT}")
if __name__ == "__main__":
main()
几个容易翻车的地方
PubMed 返回的 XML 结构挺乱的,说几个写 parse_article 时遇到的坑:
摘要字段不固定。 大部分摘要是分段列表(Background、Methods...),但有些就是一段字符串。只按列表处理会报错,代码里做了类型判断。
老文献没有摘要。 90 年代以前的很多文献只有标题,直接取 ['Abstract'] 会 KeyError,用 .get() 比较安全。
DOI 不一定存在。 早期文献压根没有 DOI,得从 ArticleIdList 里遍历找,找不到就留空。
年份字段位置不统一。 大部分有 Year,但有些只有 MedlineDate(比如 "1995 Mar"),得截前四位。
批次别太大。 试过 500 篇一批,偶尔超时。改成 200 篇之后稳多了。
搜索词写法
PubMed 搜索语法可以直接写在 QUERY 里:
# 基础搜索
QUERY = "CRISPR cancer therapy"
# 限定年份
QUERY = "single cell AND 2024:2026[PDAT]"
# 排除综述
QUERY = "(diabetes AND insulin) NOT review[Publication Type]"
# 限定期刊
QUERY = "immunotherapy AND Nature[Journal]"
# 组合使用
QUERY = "lung cancer immunotherapy AND 2025:2026[PDAT] AND Clinical Trial[Publication Type]"
我一般先在 PubMed 网页上试关键词,确认结果数量和相关性没问题,再贴到脚本里跑。
参考: NCBI E-utilities 官方文档 [1]
[1] https://www.ncbi.nlm.nih.gov/books/NBK25497/