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

对于医疗数据这种结构化页面,Scrapy的分布式架构反而显得笨重。我们采用轻量级方案:Requests获取页面 → Lxml解析 → Pandas存储,30行代码即可完成核心功能。
以丁香园药品库的"阿莫西林胶囊"页面为例,关键数据分布在:
<h1 class="drug-name"><div class="ingredient"><div class="indication"><div class="dosage">通过浏览器开发者工具(F12)查看元素,发现所有数据都在<div class="drug-detail">容器内。
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')
//div[@class="ingredient"]//text()表示选取所有class为"ingredient"的div下的文本节点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)
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/'
}
import time
import random
def fetch_with_delay(url):
delay = random.uniform(1, 3) # 随机延迟1-3秒
time.sleep(delay)
return fetch_drug_data(url) # 使用基础爬虫函数
<p>用法用量:</p><p>口服,一次2粒</p>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
存储方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
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)
Q1:被网站封IP怎么办? A:立即启用备用代理池,建议使用住宅代理(如站大爷IP代理),配合每请求更换IP策略。对于大规模采集,可采用:
Q2:如何获取药品ID列表? A:可通过以下方式获取:
https://drugs.dxy.cn/search?page=2)https://drugs.dxy.cn/api/search?keyword=抗生素)Q3:数据抓取频率应该设置多少? A:建议遵循以下原则:
Q4:如何处理动态加载的数据? A:两种方案:
Q5:如何避免法律风险? A:必须遵守:
https://drugs.dxy.cn/robots.txt)通过本文的实战讲解,你已掌握医疗爬虫的核心技术:从页面解析到反爬虫应对,从数据清洗到存储优化。实际项目中,建议结合具体需求调整策略,例如:
记住:技术只是手段,合规才是根本。在享受数据红利的同时,务必遵守相关法律法规,让爬虫技术真正服务于医疗健康事业。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。