网页爬取如何定位表单元素?bs4无法识别表单解决方案
israeldrugs.health.gov.il 药品查询页爬取方案
问题根因
你用bs4找不到表单是必然结果:该站点是AngularJS构建的单页应用,所有DOM元素(搜索表单、输入框、按钮、查询结果)全部由前端JavaScript在客户端动态渲染生成。bs4只能解析服务器初始返回的静态HTML,这部分静态内容里只有Angular的脚本挂载占位节点,不存在任何实际业务元素,和你选择器写法是否正确没有关系。
之前的同类解答失效,是因为站点后续迭代调整了接口路径、DOM属性规则,旧方案的选择器、请求地址已经和当前站点逻辑不匹配。
方案1:直接调用后端接口(优先选择,效率最高)
不需要加载浏览器资源,爬取速度是渲染方案的10倍以上,适合批量查询场景
- 打开浏览器开发者工具的Network面板,筛选Fetch/XHR类型请求,手动在页面触发一次查询就能抓到核心查询接口,请求逻辑非常简单:
- 接口地址:
https://israeldrugs.health.gov.il/IdServer/api/Drugs/GetDrugsBy - 请求方法:POST
- 请求格式:JSON
- 核心参数:
drugName字段传入你要搜索的关键词,page、pageSize字段控制分页
- 接口地址:
- 请求必须携带正确的
Referer、Content-Type头,否则会被站点拦截,参考实现代码:
import requests req_headers = { "Accept": "application/json, text/plain, */*", "Content-Type": "application/json;charset=UTF-8", "Referer": "https://israeldrugs.health.gov.il/", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36" } search_payload = { "drugName": "输入你要查询的药品关键词", "page": 1, "pageSize": 20, # 其余固定参数可以直接从浏览器抓到的请求体里复制,不需要修改 } resp = requests.post( "https://israeldrugs.health.gov.il/IdServer/api/Drugs/GetDrugsBy", headers=req_headers, json=search_payload ) # 接口直接返回结构化JSON数据,不需要做HTML解析 result_data = resp.json() print(result_data)
方案2:浏览器自动化渲染(接口逆向难度大时使用)
兼容性最强,不需要关心接口签名、参数加密逻辑,完全模拟真人操作
- 放弃
requests + bs4的静态爬取组合,使用支持JS渲染的工具:- 轻量场景可以选
requests-html,代码量小 - 稳定长期使用选
Playwright,自动匹配浏览器驱动,等待API封装完善
- 轻量场景可以选
- 核心注意点:必须加显式等待,等目标元素完全渲染到DOM树之后再执行输入、点击操作,不要页面刚加载就去定位元素,参考Playwright实现代码:
from playwright.sync_api import sync_playwright with sync_playwright() as pw: # headless=True可以后台静默运行,不需要弹出浏览器窗口 browser = pw.chromium.launch(headless=True) page = browser.new_page() page.goto("https://israeldrugs.health.gov.il/#!/byDrug", wait_until="networkidle") # 定位搜索输入框,输入关键词 search_input = page.wait_for_selector("input[ng-model='search.drugName']", timeout=15000) search_input.fill("输入你要查询的药品关键词") # 点击左侧蓝色查询按钮 page.click("button[ng-click='searchDrugs()']") # 等待查询结果加载完成后提取内容 page.wait_for_selector(".result-item", timeout=10000) all_results = [item.inner_text() for item in page.locator(".result-item").all()] print(all_results) browser.close()
注意事项
- 爬取时控制请求频率,单IP请求间隔建议不低于2秒,避免被站点封禁
- 如果后续接口新增签名校验,优先检查Network面板里的请求头、请求体新增字段,更新参数即可,不需要直接切换到浏览器渲染方案
内容的提问来源于stack exchange,提问作者gtomer
相关产品推荐
相关产品推荐

