本文详解如何使用 python 爬取 annumed(https://annumed.sante-dz.com/annuaire)上分页加载的医疗从业者信息,解决因忽略分页机制与 dom 动态结构导致的“空数据表”问题,并提供健壮、可扩展的 beautifulsoup 解析方案。
本文详解如何使用 python 爬取 annumed(https://annumed.sante-dz.com/annuaire)上分页加载的医疗从业者信息,解决因忽略分页机制与 dom 动态结构导致的“空数据表”问题,并提供健壮、可扩展的 beautifulsoup 解析方案。
Annumed 是阿尔及利亚权威的医疗从业者黄页网站,其首页(/annuaire)采用服务端分页 + 静态 HTML 渲染方式呈现数据——这意味着所有内容实际存在于 HTML 中,但默认仅加载第 1 页;后续页面需通过 ?page=N 参数显式请求。原始代码失败的根本原因有三点:
- ❌ 未处理分页:仅请求首页,而目标数据分散在多页中;
- ❌ 选择器失效:.element 类在当前页面结构中已不存在,真实数据容器为 .info:has(header);
- ❌ 属性与文本提取错误:如 href_='/filter/type/me' 是无效语法(应为 href='/filter/type/me'),且 itemprop_='addressLocality' 并非直接子元素,而是嵌套在 .address__wrapper 内。
以下是经过验证、稳定可用的改进方案:
import pandas as pd
import requests
from bs4 import BeautifulSoup
data = []
# 遍历前两页(可根据需要调整 range(1, N+1))
for page in range(1, 3):
print(f"正在抓取第 {page} 页...")
url = f"https://annumed.sante-dz.com/annuaire?page={page}"
# 添加基础请求头,降低被拦截风险
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 抛出 HTTP 错误
soup = BeautifulSoup(response.content, "html.parser")
# 定位每个从业者区块:包含 header 的 .info 容器(语义精准,抗干扰强)
for block in soup.select(".info:has(header)"):
# 提取姓名(h3 标签内文本)
name = block.h3.get_text(strip=True) if block.h3 else "N/A"
# 提取专业描述(header 下首个 p 标签)
spec = block.select_one("header p").get_text(strip=True) if block.select_one("header p") else "N/A"
# 提取多个专科标签(.speciliate 类可能有多个)
cats = [s.get_text(strip=True) for s in block.select(".speciliate")]
categories = ", ".join(cats) if cats else "N/A"
# 提取地址(合并 .address__wrapper 内所有文本,用空格分隔)
addr_elem = block.select_one(".address__wrapper")
address = addr_elem.get_text(strip=True, separator=" ") if addr_elem else "N/A"
data.append((name, spec, categories, address))
except requests.RequestException as e:
print(f"第 {page} 页请求失败:{e}")
continue
# 构建结构化 DataFrame
df = pd.DataFrame(data, columns=["姓名", "专业描述", "专科领域", "地址"])
print("\n✅ 抓取完成,共获取", len(df), "条记录:")
print(df.head(5))
关键优化说明:
- ✅ 分页遍历:通过 ?page=1, ?page=2 等参数主动翻页,覆盖全部公开数据;
- ✅ CSS 选择器升级:使用 .info:has(header) 精准定位每个从业者区块(比泛化的 .element 更可靠);
- ✅ 容错性增强:所有 .get_text() 均配合 if ... else 判断,避免 .text 在元素缺失时抛出 AttributeError;
- ✅ 地址清洗:separator=" " 可将换行/缩进地址(如 "Alger\n Hydra")规范化为 "Alger Hydra";
- ✅ 请求头与超时:模拟真实浏览器行为,提升请求成功率。
注意事项与建议:
- ⚠️ 遵守 robots.txt 与网站条款:访问前请检查 https://annumed.sante-dz.com/robots.txt,并限制请求频率(如添加 time.sleep(1));
- ⚠️ 动态内容预警:本例为静态渲染页面;若未来改用 JavaScript 渲染(如 React/Vue),则需切换至 Selenium 或 Playwright;
- ✅ 扩展提示:如需导出 Excel,只需追加 df.to_excel("annumed_professionals.xlsx", index=False);若需去重或清洗地址,可结合 pandas.str.extract() 或 geopy 地理编码进一步处理。
该方案已在真实环境中验证有效,可作为医疗类黄页网站结构化采集的标准实践参考。










