联系我们(点击👈蓝色字关注我们)
想做系统综述/Meta分析,ClinicalTrials.gov上有成千上万条试验注册信息,一条一条手动下载?PubMed文献检索后,一个引用一个引用地复制粘贴到参考文献管理器?这不仅是时间成本,更会产生遗漏和录入错误。
Python爬虫可以自动化获取公开的医学数据。本文不是教你"黑"网站,而是教你如何合法合规地利用公开API和网络爬虫技术,批量获取ClinicalTrials.gov试验信息和PubMed文献数据,用pandas整理为结构化表格。附5个经审核合规的数据源和完整Python代码。
合规前提:三条红线
1. 检查robots.txt:每个网站都有robots.txt(如clinicaltrials.gov/robots.txt),告知爬虫哪些页面禁止访问2. 仅抓取公开数据:不绕过登录验证,不爬取付费墙后的内容3. 遵守使用条款:PubMed E-utilities API明确要求不超过3次/秒的请求频率,ClinicalTrials.gov API同样有限速4. 尊重版权:PubMed摘要可下载,但全文PDF属于出版商版权,不可批量下载5. 不商用:爬取的数据仅用于学术研究,不得商业售卖
一、5个合规数据源速览
| | | | |
| | | | |
| | | 3次/秒(无API key)10次/秒(有API key) | |
| | | | |
| | | | |
| | | | |
二、requests+BeautifulSoup 基础爬虫框架
大多数医学数据网站都提供API(优先使用),但有些老旧网站只有HTML页面。以下为基础HTML爬虫模板:
# ===== 标准爬虫框架 =====import requests from bs4 import BeautifulSoup import time import pandas as pd # 设置headers,模拟正常浏览器访问 headers = { 'User-Agent': 'Mozilla/5.0 ...', 'From': 'your.email@institution.edu.cn' } # 使用session维持连接 session = requests.Session() session.headers.update(headers) # 获取页面deffetch_page(url, delay=1): time.sleep(delay) # 遵守礼貌性延迟 resp = session.get(url, timeout=30) resp.raise_for_status() # 检查HTTP错误return BeautifulSoup(resp.text, 'html.parser') page = fetch_page('https://example.com') # 然后用page.find() / page.find_all()提取内容三、案例1:ClinicalTrials.gov API批量获取试验信息
ClinicalTrials.gov提供了RESTful API,这是获取临床试验数据最规范的方式。无需爬HTML页面。
import requests import pandas as pd import time # API基础URL (v2) BASE_URL = "https://clinicaltrials.gov/api/v2/studies"# 查询参数:NSCLC + 免疫治疗 + Phase 2/3 + 活跃 params = { "query.cond": "Non-Small Cell Lung Cancer", "query.intr": "pembrolizumab OR nivolumab", "filter.overallStatus": "ACTIVE_NOT_RECRUITING,RECRUITING", "filter.phase": "PHASE2,PHASE3", "pageSize": 100, "format": "json" } # 分页获取所有结果 all_studies = [] next_token = NonewhileTrue: if next_token: params["pageToken"] = next_token resp = requests.get(BASE_URL, params=params) data = resp.json() studies = data.get("studies", []) all_studies.extend(studies) next_token = data.get("nextPageToken") ifnot next_token: break time.sleep(0.3) # 限速print(f"共获取 {len(all_studies)} 条试验记录")# 提取关键字段 records = [] for s in all_studies: protocol = s["protocolSection"] ident = protocol["identificationModule"] status = protocol["statusModule"] design = protocol.get("designModule", {}) records.append({ "NCT_ID": ident["nctId"], "Title": ident["briefTitle"], "Status": status["overallStatus"], "Phase": ", ".join( design.get("phases", []) ), "Enrollment": design.get( "enrollmentInfo", {} ).get("count"), "StartDate": status.get( "startDateStruct", {} ).get("date") }) df = pd.DataFrame(records) df.to_csv("clinical_trials_nsclc.csv", index=False) print(f"已保存 {len(df)} 条记录")四、案例2:PubMed E-utilities API批量检索文献
PubMed提供了一套强大的API(E-utilities),可以程序化搜索文献、获取摘要、批量导出。这是做系统综述和Meta分析时的有利工具。
import requests from xml.etree import ElementTree as ET import time NCBI_API = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"# 步骤1: ESearch - 搜索文献 search_params = { "db": "pubmed", "term": '(UHRF1 AND "T-ALL")', "retmax": 100, "retmode": "xml", "usehistory": "y", # 使用缓存"api_key": "YOUR_API_KEY"# 提升速率 } resp = requests.get(f"{NCBI_API}/esearch.fcgi", params=search_params) root = ET.fromstring(resp.text) id_list = [e.text for e in root.findall(".//Id")] web_env = root.find(".//WebEnv").text query_key = root.find(".//QueryKey").text print(f"找到 {len(id_list)} 篇文献") # 步骤2: EFetch - 获取摘要(分批处理,每次100条) batch_size = 100 all_abstracts = [] for i inrange(0, len(id_list), batch_size): fetch_params = { "db": "pubmed", "rettype": "abstract", "retmode": "xml", "WebEnv": web_env, "query_key": query_key, "retstart": i, "retmax": batch_size } resp = requests.get(f"{NCBI_API}/efetch.fcgi", params=fetch_params) all_abstracts.append(resp.text) time.sleep(0.5) print(f"已获取 {len(id_list)} 篇摘要")五、数据清洗:pandas整理为结构化表格
获取的原始数据通常需要清洗和处理,pandas是Python数据处理的标准库:
# 数据清洗处理管道import pandas as pd # 1. 读入ClinicalTrials数据 df = pd.read_csv("clinical_trials_nsclc.csv") # 2. 清洗:去除重复NCT_ID df = df.drop_duplicates(subset="NCT_ID") # 3. 过滤:仅保留招募中的试验 df_active = df[df["Status"].isin([ "RECRUITING", "ACTIVE_NOT_RECRUITING" ])] # 4. 按入组数排序列,查看最大的试验 df_active = df_active.sort_values("Enrollment", ascending=False) # 5. 统计摘要print(f"活跃试验: {len(df_active)}") print(f"中位入组数: {df_active['Enrollment'].median()}") print(f"Phase分布:\n{df_active['Phase'].value_counts()}") # 6. 导出为Excel(含数据透视)with pd.ExcelWriter("trials_summary.xlsx") as writer: df_active.to_excel(writer, sheet_name="全部试验", index=False) df_active.groupby("Phase").size() \ .to_excel(writer, sheet_name="Phase汇总")六、合规的防反爬措施
| | | |
| | | PubMed建议3次/秒,ClinicalTrials 5次/秒 |
| | session = requests.Session() | |
| | | |
| | | |
实用建议:优先找API
几乎所有主流医学数据平台都提供API。爬HTML网页是最后的手段——HTML结构可能随时变化,你的爬虫可能某一天突然失效。API接口更稳定、返回结构化数据、且通常被明确许可使用。在论文方法部分可以引用API使用声明,增强可重复性。
需要医学数据采集支持?
系统综述数据提取、临床试验数据库检索、文献计量分析卫鑫医学数据团队提供合规高效的数据采集服务
添加小助理微信,回复关键词 【Python爬虫】 领取完整Python代码和数据源汇总