本文介绍为何无法仅用 requests + beautifulsoup 抓取 nd dmr 油气井页面的下拉表格数据,并详解如何通过 selenium 模拟用户交互,精准定位、遍历下拉选项并批量提取 html 表格。
本文介绍为何无法仅用 requests + beautifulsoup 抓取 nd dmr 油气井页面的下拉表格数据,并详解如何通过 selenium 模拟用户交互,精准定位、遍历下拉选项并批量提取 html 表格。
在处理类似 North Dakota DMR Bakken Wells 页面 这类前端驱动型网站时,一个常见误区是:认为 requests 获取 HTML 后用 BeautifulSoup 解析即可提取所有数据。但该页面本质上是一个服务端渲染不完整、依赖 JavaScript 动态加载内容的单页应用(SPA)——其下拉菜单(
- requests.get() 仅返回初始空壳 HTML,不含任何表格;
- BeautifulSoup 是静态解析器,无法执行 JS、触发事件或等待异步响应;
- 单纯“解析下拉选项”无法获取实际数据,必须模拟真实浏览器行为。
✅ 正确方案:使用 Selenium WebDriver(配合 Chrome 或 Edge 浏览器),实现以下关键步骤:
- 启动浏览器并访问目标页面;
- 显式等待下拉菜单元素加载就绪;
- 遍历
- 等待对应表格出现(如通过 CSS 选择器 table.table 或 ID);
- 提取表格 HTML 或结构化数据(如 pandas DataFrame);
- 可选:保存为 CSV / Excel 或合并为统一数据集。
以下是可直接运行的完整示例代码(需提前安装 selenium 并配置 ChromeDriver):
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
import pandas as pd
import time
# 初始化 WebDriver(推荐使用 context manager 或显式 quit)
driver = webdriver.Chrome() # 确保 chromedriver 在 PATH 中
wait = WebDriverWait(driver, 10)
try:
driver.get("https://www.php.cn/link/5f68d79da98d5ad7e2fa873c8c8c0383")
# 等待下拉菜单加载(ID 为 'county',根据页面源码确认)
select_element = wait.until(EC.presence_of_element_located((By.ID, "county")))
select = Select(select_element)
# 获取所有选项文本和值
options = [(opt.get_attribute("value"), opt.text) for opt in select.options[1:]] # 跳过首项提示(如 "-- Select County --")
all_dfs = []
for value, label in options[:3]: # 建议先测试前3个县,避免超时
print(f"Processing county: {label} (value={value})")
# 重新定位 select(DOM 可能重渲染)
select_element = wait.until(EC.element_to_be_clickable((By.ID, "county")))
select = Select(select_element)
select.select_by_value(value)
# 等待表格出现(页面中表格 class 为 'table',且位于 #results div 内)
table_elem = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#results table.table")))
# 使用 pandas 直接读取 HTML 表格(自动处理 <thead>/<tbody>)
dfs = pd.read_html(table_elem.get_attribute("outerHTML"), header=0)
if dfs:
df = dfs[0]
df["County"] = label # 添加来源标识列
all_dfs.append(df)
# 可选:添加短暂停顿,降低请求频率
time.sleep(1)
# 合并所有表格
if all_dfs:
final_df = pd.concat(all_dfs, ignore_index=True)
final_df.to_csv("bakken_wells_by_county.csv", index=False)
print(f"✅ Successfully scraped {len(all_dfs)} tables. Total rows: {len(final_df)}")
else:
print("⚠️ No tables extracted.")
finally:
driver.quit()? 关键注意事项:
- 元素定位需验证:务必查看页面源码(右键 → “查看页面源代码”),确认
- 显式等待优于 time.sleep():使用 WebDriverWait + expected_conditions 可提升稳定性,避免因网络波动导致的 NoSuchElementException;
- 反爬与合规性:该网站未明确禁止爬虫,但仍建议设置合理延迟(如 time.sleep(1))、添加 User-Agent 头(可通过 Options.add_argument('--user-agent=...')),并遵守 robots.txt(https://www.php.cn/link/a05def2ffe22028e9db25b4e6e99543d);
- 异常处理增强:生产环境应包裹 try/except 处理 TimeoutException、NoSuchElementException 等,并记录失败项以便重试;
- 替代轻量方案?:若页面存在隐藏的 AJAX 接口(如 /api/wells?county=...),可通过浏览器开发者工具(Network → XHR)捕获真实请求,再用 requests 直接调用——但本例中该接口未公开暴露,故 Selenium 是可靠首选。
总结而言,面对 JS 渲染的动态下拉数据,放弃“静态解析幻想”,拥抱浏览器自动化,是稳健抓取的必经之路。Selenium 不仅解决“能不能”,更通过可控交互保障“准不准”与“全不全”。










