首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >医疗爬虫实战:手把手教你抓取丁香园药品信息库

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

原创
作者头像
富贵软件
发布2025-11-19 16:06:22
发布2025-11-19 16:06:22
1060
举报
文章被收录于专栏:编程教程编程教程

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

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

1. 核心工具包

  • Requests:发送HTTP请求的瑞士军刀,支持会话保持和代理设置。
  • Lxml:比BeautifulSoup快5倍的HTML解析器,支持XPath表达式精准定位数据。
  • Pandas:数据处理的瑞士军刀,支持CSV/Excel导出和清洗。
  • ProxyPool:开源代理池管理工具,自动维护高可用代理IP。

2. 为什么不用Scrapy?

对于医疗数据这种结构化页面,Scrapy的分布式架构反而显得笨重。我们采用轻量级方案:Requests获取页面 → Lxml解析 → Pandas存储,30行代码即可完成核心功能。

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

1. 页面结构分析

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

  • 药品名称:<h1 class="drug-name">
  • 成分:<div class="ingredient">
  • 适应症:<div class="indication">
  • 用法用量:<div class="dosage">

通过浏览器开发者工具(F12)查看元素,发现所有数据都在<div class="drug-detail">容器内。

2. 基础爬虫代码

代码语言:javascript
复制
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')

3. 关键技巧解析

  • XPath定位//div[@class="ingredient"]//text()表示选取所有class为"ingredient"的div下的文本节点
  • 文本清洗:使用列表推导式去除空白字符
  • 异常处理:捕获网络请求和解析异常,避免程序中断

三、反爬虫攻防战:如何突破限制?

1. 丁香园的反爬机制

  • IP频率限制:同一IP每分钟请求超过10次即触发验证
  • 行为指纹:通过Canvas指纹、WebGL指纹识别爬虫
  • 动态加载:部分数据通过AJAX异步加载

2. 破解方案

方案1:代理IP轮换
代码语言:javascript
复制
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:请求头伪装
代码语言:javascript
复制
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:动态延迟控制
代码语言:javascript
复制
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标签残留:如<p>用法用量:</p><p>口服,一次2粒</p>
  • 单位不统一:如"5mg"和"0.005g"
  • 缺失值处理:部分药品缺少"禁忌症"字段

2. 清洗方案

代码语言:javascript
复制
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

占用空间较大

五、完整项目代码(精简版)

代码语言:javascript
复制
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:可通过以下方式获取:

  1. 丁香园药品分类页面的分页链接(如https://drugs.dxy.cn/search?page=2
  2. 搜索接口API(如https://drugs.dxy.cn/api/search?keyword=抗生素
  3. 已有药品数据库的交叉验证

Q3:数据抓取频率应该设置多少? A:建议遵循以下原则:

  • 测试期:每10-30秒/请求
  • 正式采集:每3-10秒/请求(根据目标网站规模调整)
  • 关键时期:启用分布式爬虫,每个IP分配不同延迟

Q4:如何处理动态加载的数据? A:两种方案:

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

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

  1. 检查目标网站的robots.txt文件(如https://drugs.dxy.cn/robots.txt
  2. 控制采集频率,避免对服务器造成负担
  3. 不采集用户隐私数据(如患者信息)
  4. 仅用于个人学习研究,商业用途需获得授权

结语

通过本文的实战讲解,你已掌握医疗爬虫的核心技术:从页面解析到反爬虫应对,从数据清洗到存储优化。实际项目中,建议结合具体需求调整策略,例如:

  • 医疗研究:重点关注药品相互作用、不良反应等字段
  • 价格监控:需定期抓取并对比不同渠道价格
  • 药品对比:需要标准化单位并建立映射关系

记住:技术只是手段,合规才是根本。在享受数据红利的同时,务必遵守相关法律法规,让爬虫技术真正服务于医疗健康事业。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、技术选型:为什么选择这些工具?
    • 1. 核心工具包
    • 2. 为什么不用Scrapy?
  • 二、实战步骤:从0到1抓取药品数据
    • 1. 页面结构分析
    • 2. 基础爬虫代码
    • 3. 关键技巧解析
  • 三、反爬虫攻防战:如何突破限制?
    • 1. 丁香园的反爬机制
    • 2. 破解方案
      • 方案1:代理IP轮换
      • 方案2:请求头伪装
      • 方案3:动态延迟控制
  • 四、数据清洗与存储:让数据可用
    • 1. 常见数据问题
    • 2. 清洗方案
    • 3. 存储方案对比
  • 五、完整项目代码(精简版)
  • 六、常见问题Q&A
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档