拓冰建站拓冰建站
首页 / 资讯中心 / 正文

医院数据信息爬虫实战:从数据源到字段清洗的完整方案

简介一份覆盖全国医院等级、擅长病症、地址、邮箱、电话及官网链接的爬虫程序资源服务医疗数据分析、公共卫生规划与机构运营等场景适合有一定Python基础、需要批量采集医院结构化信息的开发者。资源包共3个文件包含一个.py爬虫脚本、一张数据预览图png和一份markdown说明文档压缩包仅331KB脚本实现了网页请求、内容解析与字段提取的完整流程可直接运行或按需修改爬取逻辑。已有七十二人学习浏览便于快速上手。通过脚本可批量导出易于分析的格式化数据配合说明文档能迅速完成环境配置、字段映射与运行调试预览图则直观展示抓取结果帮助核验数据准确性。整体数据地域覆盖广、字段维度齐可支撑区域医疗资源分布分析、就医导诊信息整理等实际应用。 做医疗健康类业务的人应该都体会过被医院数据折腾的痛苦。前阵子公司要给就医导诊平台维护院区信息急需一份覆盖全国的医院名录包含等级、擅长病症、地址、邮箱、电话、网站这些核心字段。结果市面上能直接买到的数据不是太老就是字段残缺卫健委公示文件又分散在各个省市站点的深处根本没法一键汇总。没办法只能自己动手写了一个医院数据信息爬虫把散在各处的医院信息逐层挖出来整理成结构化数据。这篇文章把这套爬虫项目的完整思路记录下来从数据源规划、字段解析到反爬应对希望给有类似需求的同行一些参考。1. 这个爬虫要解决的真实问题一份可用的全国医院库从哪来1.1 医院数据到底散落在哪里先看清问题本质。医院信息不是某一个网站自己单独拥有的而是分散在很多地方各级卫健委官网的医疗机构执业登记公示、医院等级评审结果页面各医院自己的官方网站尤其是医院简介联系我们重点科室几个子页面第三方挂号平台、健康服务平台聚合的医院标签和科室擅长信息。单纯抓一个渠道数据永远缺胳膊少腿。比如从卫健委公示能拿到医院名称、地址和等级但通常没有擅长病症从医院官网能拿到科室介绍和联系电话但又很难批量获取全院等级。所以这个爬虫必须设计成多源互补的结构而不是盯着某一个站点死磕。1.2 字段设计背后的业务需求题目标出来的六个字段其实对应了医疗信息服务的六个使用环节等级用户判断医院综合实力的第一眼信息也是导诊推荐的核心排序权重擅长病症智能分诊、科室推荐的关键标签直接影响搜索匹配地址基于LBS的附近医院功能必需缺了没法做距离排序邮箱部分医院对外事务、转诊联系、商务合作的重要通道电话用户最直接的联系方式挂号和咨询的终极兜底网站完整信息源入口给用户留下自行判断的跳转链路。知道字段要去哪里找以后下一步就是规划爬取目标。我建议不要把全国想成一个单一集合而是拆成卫健委源 医院官网源 第三方聚合源三个层次分别推进这样后面写代码才不会乱。2. 数据源选型与页面信息分布规律不能只盯一个网站2.1 公开可用的三类数据源对比我实际调研后把可用数据源分成三类各有优缺点数据源类型典型例子能提供的字段主要问题卫健委公示省市卫健委、卫生监督所医院名称、地址、等级、法定代表人页面结构差异极大无擅长病症搜索入口弱医院官网医院医院简介联系我们页等级、地址、电话、邮箱、重点科室、特色技术各站结构完全不同需要针对性解析挂号/健康平台健康中国、省级统一挂号平台等级、擅长病症、科室列表、医生简介数据相对规范但存在聚合噪音部分字段人工维护未必准这三类源里真正能批量拿到擅长病症的是挂号平台和医院的重点科室页但它们恰恰是反爬最严格的地方。医院官网虽然结构五花八门但绝大多数毫无反爬措施反而适合做基础数据补充。2.2 一个医院的完整信息要跨多个页面获取很多人写爬虫失败是因为以为一个URL就能拿全所有字段。实际上单家医院的数据经常这样分布列表页医院名称 一个详情页链接这是爬虫的入口详情页科室介绍、等级描述、部分擅长词条可能要翻3~5个标签页联系方式页地址、电话、邮箱往往独立存在挂号平台科室介绍页擅长病症关键词最集中的地方。所以这个项目本质上不是单页抓取爬虫而是一个以医院为主体的关系型数据聚合器。爬完列表页要把它下面的所有子URL都理解成待补充字段的来源分别请求、分别解析最后再合并成一条记录。2.3 起始URL库怎么组织我当时建议用省-市-医院三级结构来建起始URL库而不是随便拿几个搜索页硬抓。具体做法是先在各省卫健委官网提取医院公示列表拿到医院名称后再去搜索引擎或直接拼接医院官网域名补充详情页地址。这个流程虽然前置工作量大但能让后期的每个医院都有一条清晰的抓取血缘排查缺数据时非常省事。3. 多级爬取的框架设计与请求策略从列表页到详情页再到关联页3.1 选型Scrapy还是纯requests手写这个项目我用了Scrapy requests混合方案。总框架用Scrapy是因为它的异步并发、中间件、Pipeline天然适合多页面任务但某些反爬严格的挂号平台Scrapy的默认行为太规整容易被识别反而需要临时用requests配合Session单独处理。如果你只是学习或者抓取量不大纯requests也能搞定写法更直观import requests from bs4 import BeautifulSoup session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, }) def fetch(url, retries3): for i in range(retries): try: resp session.get(url, timeout10) if resp.status_code 200: return resp.text except requests.RequestException as e: print(f[retry {i1}] {url} - {e}) time.sleep(2 ** i) return None但注意这只适合单线程小规模任务。真要跑全国几千家医院、每个医院3~5个子页务必用Scrapy的并发能力不然单线程跑一个晚上都跑不完。3.2 主表、子队列与去重设计核心数据流可以概括为主表 子任务队列医院主表每抓到一家医院就生成一条主记录赋予唯一ID子任务队列把详情页、联系方式页、科室页这些待抓URL按所属医院ID 页面类型写入Redis队列去重URL做MD5后存入Bloom Filter避免同一页面被重复请求。这样做的好处是哪怕某个子页面抓取失败也不影响主记录的保存后续只需要对失败队列做定向重跑。我最初没做子任务队列结果一个医院详情页挂了整个医院记录都没了后面补数据时找不到入口心态直接崩掉。3.3 请求频率、UA轮换与代理策略反爬的平衡点在于伪装得像真人又足够快。我实测下来比较稳的一组参数请求间隔1~3秒随机同一域名下的连续请求不小于1秒User-Agent轮换准备20个左右Chrome/Firefox/Safari的UA每隔几次请求切换Referer设置如果抓的是医院官网联系我们页Referer尽量填同站首页普通数据源不做代理只有被明确封IP后才启用代理池。代理这块提醒一句医院官网和卫健委站点基本不反爬没必要一上来就上代理反而会因为代理IP质量差拖慢速度。第三方挂号平台反爬比较强先靠降低频率规避实在不行再考虑代理。4. 字段提取逻辑与正则清洗细节六字段完整解析4.1 医院等级两种解析思路医院等级有两个常见来源解析逻辑完全不同。第一种是卫健委公示列表里的直接标注通常在页面上表现为三级甲等二级乙等等固定文字直接用正则提取import re pattern r(三级|二级|一级)[甲乙丙][等]? text 该院为三级甲等综合医院 match re.search(pattern, text) print(match.group()) # 三级甲等第二种是医院官网医院简介页的散文描述比如我院是集医疗、教学、科研于一体的三级甲等专科医院。这种文本更乱需要用医院全称 等级关键词双重定位再对结果做数据字典映射统一成三甲三乙二甲等短编码方便存储和展示。别直接存原始文本后面排序筛选的时候你会后悔。4.2 擅长病症文本相似度不够走科室白名单擅长病症是最难提取的字段因为它本质上不是语义识别问题而是标签映射问题。医院不会直接写一行擅长心脏病它写的是心血管内科率先开展冠心病介入治疗成功实施多例复杂支架植入术。我当时放弃了纯文本相似度方案改成科室白名单 关键词权重的提取方式department_keywords { 心血管科: [冠心病, 心律失常, 心力衰竭, 介入], 神经科: [脑卒中, 癫痫, 帕金森, 头痛], 骨科: [关节置换, 脊柱, 骨折], } def extract_departments(text): hits set() for dept, words in department_keywords.items(): if any(w in text for w in words): hits.add(dept) return list(hits)这样提取出来的是结构化的科室标签后续搜索和推荐可以直接用。实际跑下来的体验是擅长病症字段准确率比想象中难做能到70%以上就算合格剩下的人工抽查修正就好别追求100%自动化。4.3 地址、邮箱、电话的清洗正则这三类字段靠正则清洗能解决大部分问题phone_pattern r(?!\d)(1[3-9]\d{9}|0\d{2,3}-?\d{7,8}|400-?\d{3,4}-?\d{3,4})(?!\d) email_pattern r[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,} address_pattern r[\u4e00-\u9fa5]{2,}(?:省|市|区|县|镇|街道|路|街|号)电话要注意去重复匹配和400电话的特殊格式地址清洗则要保留省市区前缀后面做地理编码和距离排序直接依赖它。这里还有个非常容易踩的坑很多医院官网页面底部有关键词堆砌会伪造一堆假地址和假电话清洗时一定要结合页面标题和正文区域做范围限制直接全页正则匹配会被脏数据污染。4.4 字段完整性校验字段提取完我会做三个层面的校验缺失率统计、格式非法率、抽样人工核对。如果某个省份的医院电话缺失率超过15%基本可以断定这个数据源页面结构判断出了问题需要回头检查该省份的模板是否写错而不是继续往后跑。5. 数据质量校验、去重机制与最终交付5.1 医院记录怎么去重全国医院数据的重复问题非常严重同一家医院可能被卫健委、官网、挂号平台重复抓到三遍。我的去重策略是分层进行URL级去重子任务队列阶段靠Bloom Filter挡掉重复页面记录级去重主记录以医院全称 省份 等级作为联合唯一键医院改名的情况再基于网站域名做二次归并人工兜底导出后用Excel查看疑似重复记录比如名称只差省市前缀的统一人工合并。医院数据不同于商品数据没有统一的独立商品ID所以去重必须多级配合单靠一个字段做唯一键一定会漏。5.2 编码与导出格式最终数据要经得起下游使用导出格式我通常给两份CSV/Excel给产品和运营直接使用字段顺序固定中文表头JSON给后端API对接字段保持英文名避免中文编码起争议。这里有个细节导出CSV时统一用utf-8-sig编码不要用纯utf-8否则某些Windows环境的Excel打开会乱码。我第一版导出就被运营同事反馈乱码后来改成签名格式才解决。代码只有一行import pandas as pd df.to_csv(hospitals.csv, indexFalse, encodingutf-8-sig)5.3 增量更新比全量重抓更重要医院数据不是一成不变的等级调整、地址搬迁、科室变化随时可能发生。如果每次更新都全量重抓成本太高。在实际落地时我会做增量调度每周只抓等级和电话变更频率高的卫健委公示页医院官网两个月全量扫一次。这样既保证数据新鲜度又把资源消耗控制在一个可控范围内。6. 权限边界、踩坑复盘与扩展应用6.1 robots.txt与公开数据的边界爬虫做多了一定要对数据边界有清晰的判断。医院等级、地址、电话、邮箱这些信息属于公开公示内容在合规层面风险相对可控但依然要遵守几点被抓取站点的robots.txt如果有明确禁止路径尽量不要硬闯请求频率不要高到影响对方站点正常服务这是爬虫从业者最基本的职业操守不做医院内部系统、未公开接口、纯登录后台的数据采集。合规的意义不在于能不能躲过封禁而在于整个数据链条拿到明面上都经得起规则审视这一点对医疗数据的后续使用尤为重要。6.2 我踩过的几个典型坑这个项目我先后翻过几次车最值得说的有三个。第一个是挂号平台接口的风控。有段时间为了拿擅长病症我直接请求了某平台的JSON接口刚开始一切正常抓了上万条后突然大批量返回403排查发现对方启用了频率维度的风控。最后解决方案是退回到公开页面解析拉大请求间隔降并发虽然速度慢了但再没触发封禁。第二个是编码混乱。部分老旧地市卫健委网站还是GB2312编码直接用resp.text会乱码。正确做法是resp.encoding resp.apparent_encoding或者根据返回头里的charset参数去决定解码方式。这个坑极其隐蔽因为大部分页面是UTF-8只有遇到老站才会踩到排查起来非常耗时。第三个是动态加载。医院官网的重点科室页很多是异步渲染直接requests拿到的HTML里根本没有擅长病症字段。我的处理方式是先用selenium截获XHR请求找到真实接口再回到requests伪造成浏览器请求。这里要记住动态页面的目标是找到真正的数据接口而不是一直开着无头浏览器跑后者效率太低了。6.3 数据的后续应用还能往哪走爬下来的全量医院库除了导诊平台还能扩展做很多事情。比如结合地理编码服务把地址字段转换成经纬度就能做附近三甲医院的LBS检索把擅长病症和科室白名单映射到标准医学疾病分类就能做症状到医院的推荐系统。再比如针对每个医院维护邮件联系列表后续做医疗行业调研问卷、医疗设备厂商的市场推广都是可以直接用的资源。我在实际使用中发现这份数据真正的价值不在抓而在养。持续维护、持续更新、持续清洗的数据集比一次抓完扔在硬盘里的压缩包有价值得多。如果你拿到的也是一个医院数据信息爬虫.zip建议别急着跑完收工把增量更新的调度先搭起来后面你会感谢当初这个决定。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门