医疗爬虫实战:手把手教你抓取丁香园药品信息库

简介: 本文以丁香园药品库为例,用Python实战讲解医疗数据爬取技术。涵盖Requests、Lxml、Pandas等工具应用,解析反爬策略、代理轮换、数据清洗与存储方案,助你高效获取结构化药品信息,兼顾合规与实用性。(238字)

​

在医疗数据爆炸的时代,药品信息库已成为医生、药师和患者的重要参考。丁香园作为国内领先的医疗服务平台,其药品数据库包含药品成分、用法用量、禁忌症等关键信息。本文将以实战为导向,用通俗易懂的方式讲解如何用Python爬虫抓取这些数据,并解决反爬虫、数据清洗等核心问题。
探秘代理IP并发连接数限制的那点事 (11).png

一、技术选型:为什么选择这些工具?

  1. 核心工具包
    Requests:发送HTTP请求的瑞士军刀,支持会话保持和代理设置。
    Lxml:比BeautifulSoup快5倍的HTML解析器,支持XPath表达式精准定位数据。
    Pandas:数据处理的瑞士军刀,支持CSV/Excel导出和清洗。
    ProxyPool:开源代理池管理工具,自动维护高可用代理IP。
  2. 为什么不用Scrapy?
    对于医疗数据这种结构化页面,Scrapy的分布式架构反而显得笨重。我们采用轻量级方案:Requests获取页面 → Lxml解析 → Pandas存储,30行代码即可完成核心功能。

二、实战步骤:从0到1抓取药品数据

  1. 页面结构分析
    以丁香园药品库的"阿莫西林胶囊"页面为例,关键数据分布在:

药品名称:


成分:


适应症:

用法用量:

通过浏览器开发者工具(F12)查看元素,发现所有数据都在
容器内。
  1. 基础爬虫代码
    import requests
    from lxml import etree
    import pandas as pd

def fetch_drug_data(drug_url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}

try:
    response = requests.get(drug_url, headers=headers, timeout=10)
    response.raise_for_status()
    html = etree.HTML(response.text)

    # 提取数据
    name = html.xpath('//h1[@class="drug-name"]/text()')[0].strip()
    ingredient = html.xpath('//div[@class="ingredient"]//text()')
    ingredient = ''.join([i.strip() for i in ingredient if i.strip()])

    # 其他字段提取类似...

    return {
        '药品名称': name,
        '成分': ingredient,
        # 其他字段...
    }

except Exception as e:
    print(f"抓取失败: {e}")
    return None

示例调用

drug_url = "https://drugs.dxy.cn/drug/123456.htm"
data = fetch_drug_data(drug_url)
if data:
df = pd.DataFrame([data])
df.to_csv('drug_data.csv', index=False, encoding='utf_8_sig')

  1. 关键技巧解析
    XPath定位://div[@class="ingredient"]//text()表示选取所有class为"ingredient"的div下的文本节点
    文本清洗:使用列表推导式去除空白字符
    异常处理:捕获网络请求和解析异常,避免程序中断
    三、反爬虫攻防战:如何突破限制?
  2. 丁香园的反爬机制
    IP频率限制:同一IP每分钟请求超过10次即触发验证
    行为指纹:通过Canvas指纹、WebGL指纹识别爬虫
    动态加载:部分数据通过AJAX异步加载
  3. 破解方案
    方案1:代理IP轮换
    from proxypool import ProxyPool

pool = ProxyPool() # 初始化代理池

def fetch_with_proxy(url):
proxy = pool.get_proxy() # 获取代理
proxies = {
'http': f'http://{proxy}',
'https': f'https://{proxy}'
}
try:
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response.text
else:
pool.mark_invalid(proxy) # 标记无效代理
return fetch_with_proxy(url) # 递归重试
except:
pool.mark_invalid(proxy)
return fetch_with_proxy(url)

方案2:请求头伪装
def get_random_headers():
return {
'User-Agent': random.choice([
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15...'
]),
'Accept-Language': random.choice(['zh-CN,zh;q=0.9', 'en-US,en;q=0.8']),
'Referer': 'https://drugs.dxy.cn/'
}

方案3:动态延迟控制
import time
import random

def fetch_with_delay(url):
delay = random.uniform(1, 3) # 随机延迟1-3秒
time.sleep(delay)
return fetch_drug_data(url) # 使用基础爬虫函数

四、数据清洗与存储:让数据可用

  1. 常见数据问题
    HTML标签残留:如

    用法用量:

    口服,一次2粒


    单位不统一:如"5mg"和"0.005g"
    缺失值处理:部分药品缺少"禁忌症"字段
  2. 清洗方案
    def clean_data(raw_data):

    去除HTML标签

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(raw_data['适应症'], 'html.parser')
    clean_text = soup.get_text(strip=True)

    单位统一

    if 'mg' in raw_data['剂量']:

     raw_data['剂量_g'] = float(raw_data['剂量'].replace('mg', '')) / 1000
    

    填充缺失值

    raw_data['禁忌症'] = raw_data.get('禁忌症', '未提及')

    return raw_data

  3. 存储方案对比
    存储方式 适用场景 优点 缺点
    CSV 小规模数据 通用性强 不支持复杂查询
    SQLite 中等规模 无需服务器 并发性能有限
    MongoDB 大规模数据 灵活Schema 占用空间较大
    五、完整项目代码(精简版)
    import requests
    from lxml import etree
    import pandas as pd
    import random
    import time
    from proxypool import ProxyPool

class DrugSpider:
def init(self):
self.pool = ProxyPool()
self.base_url = "https://drugs.dxy.cn/drug/{}.htm"

def get_proxy(self):
    return self.pool.get_proxy()

def fetch_page(self, drug_id):
    url = self.base_url.format(drug_id)
    proxy = self.get_proxy()
    proxies = {'http': f'http://{proxy}', 'https': f'https://{proxy}'}

    try:
        headers = self.get_random_headers()
        time.sleep(random.uniform(1, 3))
        response = requests.get(url, headers=headers, proxies=proxies, timeout=10)
        response.raise_for_status()
        return response.text
    except Exception as e:
        print(f"抓取失败: {e}")
        return None

def parse_page(self, html):
    if not html:
        return None

    html = etree.HTML(html)
    data = {
        '名称': html.xpath('//h1[@class="drug-name"]/text()')[0].strip(),
        '成分': ''.join([i.strip() for i in html.xpath('//div[@class="ingredient"]//text()') if i.strip()]),
        # 其他字段解析...
    }
    return data

def run(self, drug_ids):
    results = []
    for drug_id in drug_ids:
        html = self.fetch_page(drug_id)
        data = self.parse_page(html)
        if data:
            results.append(data)

    df = pd.DataFrame(results)
    df.to_csv('drug_data.csv', index=False, encoding='utf_8_sig')
    print(f"成功抓取{len(results)}条药品数据")

使用示例

spider = DrugSpider()
drug_ids = ['123456', '654321', '789012'] # 实际应从列表获取
spider.run(drug_ids)

六、常见问题Q&A
Q1:被网站封IP怎么办?
A:立即启用备用代理池,建议使用住宅代理(如站大爷IP代理),配合每请求更换IP策略。对于大规模采集,可采用:

混合使用数据中心代理和住宅代理
设置请求间隔为3-10秒随机值
实现代理健康度监测,自动剔除失效代理
Q2:如何获取药品ID列表?
A:可通过以下方式获取:

丁香园药品分类页面的分页链接(如https://drugs.dxy.cn/search?page=2)
搜索接口API(如https://drugs.dxy.cn/api/search?keyword=抗生素)
已有药品数据库的交叉验证
Q3:数据抓取频率应该设置多少?
A:建议遵循以下原则:

测试期:每10-30秒/请求
正式采集:每3-10秒/请求(根据目标网站规模调整)
关键时期:启用分布式爬虫,每个IP分配不同延迟
Q4:如何处理动态加载的数据?
A:两种方案:

分析AJAX请求:通过浏览器开发者工具的Network面板,找到数据接口直接请求
Selenium模拟浏览器:适用于复杂JavaScript渲染的页面
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless') # 无头模式
driver = webdriver.Chrome(options=options)
driver.get("https://drugs.dxy.cn/drug/123456.htm")
html = driver.page_source
driver.quit()

Q5:如何避免法律风险?
A:必须遵守:

检查目标网站的robots.txt文件(如https://drugs.dxy.cn/robots.txt)
控制采集频率,避免对服务器造成负担
不采集用户隐私数据(如患者信息)
仅用于个人学习研究,商业用途需获得授权
结语
通过本文的实战讲解,你已掌握医疗爬虫的核心技术:从页面解析到反爬虫应对,从数据清洗到存储优化。实际项目中,建议结合具体需求调整策略,例如:

医疗研究:重点关注药品相互作用、不良反应等字段
价格监控:需定期抓取并对比不同渠道价格
药品对比:需要标准化单位并建立映射关系
记住:技术只是手段,合规才是根本。在享受数据红利的同时,务必遵守相关法律法规,让爬虫技术真正服务于医疗健康事业。

​

目录
相关文章
|
8月前
|
自然语言处理 数据挖掘 知识图谱
生成式搜索中的向量空间锚定实践:一次医疗GEO工程的技术拆解
本文基于真实医疗案例,从技术视角解析生成式搜索优化(GEO):通过知识图谱构建、Embedding维度压缩(1536→1024维)、RAG幻觉控制与Schema结构化,提升向量召回与引用稳定性。5个月实测显示AI可见度+26%、SOV+133%、幻觉率下降13%。(239字)
574 2
|
2月前
|
JSON 自然语言处理 API
药品信息查询 API 接口,快速获取药品基础数据
本文系基于阿里云云市场商品页(cmapi00043217)公开数据整理的技术文档,客观介绍全品类药品信息查询API:覆盖近10万种中西药/OTC/处方药,支持多维度检索与30+结构化字段返回,毫秒级响应、100% SLA,提供免费试用及多语言接入示例。
630 0
药品信息查询 API 接口,快速获取药品基础数据
|
11月前
|
前端开发 Java Python
Python高效实现Word转HTML:从基础到进阶的全流程方案
本文介绍如何利用Python实现Word文档(.docx)高效转换为HTML,解决企业数字化转型中文档格式迁移的痛点。通过对比python-docx、pandoc和Mammoth等工具,结合样式保留、图片处理、表格优化与批量转换方案,提供低成本、高灵活性的自动化流程。适用于产品手册、技术文档、课件等场景,提升转换效率达40倍,成本降低90%。
1354 0
|
8月前
|
人工智能 搜索推荐 SEO
零基础从0到1学GEO优化第1课:搞懂“什么是GEO”,建立底层认知
GEO=让AI推荐你:通过优化内容适配AI思维,成为AI答案的“引用源”。 GEO不是SEO升级版:是从“被找到”到“被引用”的范式转移,两者相辅相成(75%的AI引用链接来自SEO排名前12的网站)。 零基础起点:转变思维(引用>点击)+ 内容为真实问题提供解决方案(用案例/数据说话)。 马上行动:用AI搜索你的行业关键词,看看谁正在被推荐——那就是你的“对手”,也是你的“榜样”。
零基础从0到1学GEO优化第1课:搞懂“什么是GEO”,建立底层认知
|
7月前
|
人工智能 Linux API
OpenClaw保姆级图文教程!阿里云/Win11/MacOS/Linux部署+百炼API配置+5款生态工具深度测评
“AI交互正在从网页端向桌面端迁移”——这是2026年AI工具领域最显著的趋势。过去,用户习惯在浏览器中与大模型互动,但网页端的速度限制、权限不足、网络依赖等问题,让越来越多人将目光转向桌面端。而OpenClaw生态的崛起,恰好填补了这一空白——官方桌面版、社区驱动的轻量UI、厂商定制工具等多款产品,正在重新定义“桌面AI”的价值:更快的响应速度、更高的系统权限、更强的隐私安全,以及更自然的操作体验。
1367 2
|
7月前
|
人工智能 算法 中间件
消除语义黑洞:隐形正畸 GraphRAG 联合推理架构的生产事故复盘
本文复盘隐形正畸领域一次线上语义塌陷故障,提出GraphRAG+Neo4j Cypher联合推理方案,引入多跳衰减率、图谱聚集系数等冷门指标,依托爱搜光年医疗GEO中间件,实现品牌技术能力在知识图谱中的可信锚定。(239字)
293 7
|
9月前
|
人工智能 搜索推荐 知识图谱
信任重构与AI采信:医药行业Geo优化的“双核四驱”实战范式
Geo(生成式引擎优化)作为适配AI心智的新型优化技术,已成为医药企业构建品牌护城河的关键。
384 1
信任重构与AI采信:医药行业Geo优化的“双核四驱”实战范式
|
文字识别 并行计算 JavaScript
PaddleOCR + Django 实现一个OCR在线识别网站,一起来玩呀
除了PaddleOCR之外,之前还介绍过一些其它好玩的开源项目,例如老照片修复 Bringing-Old-Photos-Back-to-Life 、黑白照片上色DeOldify 。因此,最近准备启动一个项目,做一个在线网站,将之前一些好玩的功能都陆续集成在这个网站中
PaddleOCR + Django 实现一个OCR在线识别网站,一起来玩呀