开发者指南

Selenium 网页采集:等待、提取与 JSON 导出

使用 Python Selenium 等待动态数据、点击加载更多、提取商品并保存 JSON。说明显式等待、DOM 更新、结果验证和代理配置边界。

下载示例文件
本地动态页面加载三个虚构商品后,Load more 按钮处于禁用状态。

所需数据在浏览器渲染或交互后才出现时,使用 Selenium 采集。关键是等待让数据可用的状态,而非假定页面加载事件或固定延迟意味着可以提取。

此 Python 示例打开虚构的本地商品目录,等待首批数据,点击 Load more,等待下一批并保存三个商品,将浏览器交互与后续记录校验分开。

流程概览

  1. 1. 准备浏览器与本地示例

    在 Python 环境中安装 Selenium:

  2. 2. 完成 Selenium 采集流程

    保存为 selenium_products.py:

  3. 3. 等待真正完成任务的条件

    示例先等待 data-page="1",再等待 data-page="2"。这比等待某个商品元素更具体,因为第二批加载前已有商品元素。

  4. 4. 页面变化后重新定位元素

    一些界面点击后会替换整个结果容器,之前定位的元素引用可能失效。示例在第二批完成后重新定位商品,而非保留交互前的引用。

  5. 5. 交给下游前校验结果

    确认预期字段已填充、需要唯一的标识符没有重复,且记录数量符合采集范围。定期任务还应保留来源与采集时间。

  6. 6. 启动会话前添加网络设置

    不包含嵌入式鉴权的 HTTP 代理,可以通过 Chrome 选项在浏览器启动前选择:

1. 准备浏览器与本地示例

在 Python 环境中安装 Selenium:

python -m pip install selenium

还需要兼容的浏览器与驱动。标准驱动配置可使用 Selenium Manager;受限网络或受管工作站可能需要显式配置驱动。请遵循环境对应的官方浏览器配置文档。

使用 python3 demo_server.py 启动附带服务,打开 http://127.0.0.1:8766/dynamic。服务器返回空商品容器,JavaScript 加入前两条记录,按钮再加载第三条。

示例故意在商品容器暴露 data-page,每批插入后改变其值。此属性属于教程测试页面,并非所有网站的通用功能。

2. 完成 Selenium 采集流程

保存为 selenium_products.py:

import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver=webdriver.Chrome()
try:
    driver.get("http://127.0.0.1:8766/dynamic")
    wait=WebDriverWait(driver,10)
    wait.until(lambda d:d.find_element(By.ID,"products").get_attribute("data-page")=="1")
    wait.until(EC.element_to_be_clickable((By.ID,"load"))).click()
    wait.until(lambda d:d.find_element(By.ID,"products").get_attribute("data-page")=="2")
    rows=[]
    for card in driver.find_elements(By.CSS_SELECTOR,"article.product"):
        rows.append({"id":card.get_attribute("data-id"),"name":card.find_element(By.TAG_NAME,"h2").text,"price_usd":card.find_element(By.CSS_SELECTOR,".price").text.lstrip("$")})
    with open("selenium-products.json","w",encoding="utf-8") as f:json.dump(rows,f,ensure_ascii=False,indent=2)
    print(json.dumps(rows,indent=2))
finally:
    driver.quit()

在文件目录执行 python selenium_products.py。脚本写入 selenium-products.json,并通过 finally 关闭浏览器,即使提取因异常中断也会清理。

输出含 Travel Mug、Desk Lamp 和 Notebook 三条虚构记录。前两条在初次渲染后出现,第三条在 Load more 完成后出现。

本地动态页面加载三个虚构商品后,Load more 按钮处于禁用状态。
Load more 完成后,本地示例显示三条虚构记录,按钮随即禁用。

3. 等待真正完成任务的条件

条件

能够确认

不能确认

元素存在

存在匹配节点

节点可见、启用或已填充

元素可见

具有显示状态与尺寸

业务操作已完成

元素可点击

可见且启用

遮罩或后续页面变化不会打断点击

预期数据或状态出现

选定的完成信号满足

已采集网站全部记录

示例先等待 data-page="1",再等待 data-page="2"。这比等待某个商品元素更具体,因为第二批加载前已有商品元素。

其他网站可选择结果数量改变、已知记录 ID 出现,或加载标记消失且新行出现。根据页面行为选择能够解释的条件。

避免混用隐式和显式等待;Selenium 文档说明这种组合可能产生意外等待时间,参见等待指南。

4. 页面变化后重新定位元素

一些界面点击后会替换整个结果容器,之前定位的元素引用可能失效。示例在第二批完成后重新定位商品,而非保留交互前的引用。

遇到 StaleElementReferenceException 时,检查网站何时更新 DOM,在有意义的状态变化后重新定位。无限重试同一个失效引用不能解决根本的时序问题。

多次加载时,为点击次数或页数设置上限,并记录已见数据。达到规定的完成条件后停止。本测试页面的禁用按钮是一种信号,真实网站可能不同。

5. 交给下游前校验结果

确认预期字段已填充、需要唯一的标识符没有重复,且记录数量符合采集范围。定期任务还应保留来源与采集时间。

可选字段缺失时决定是否保留部分记录;必填字段缺失时报告问题,不要用看似真实的值填补。浏览器提取的文本仍需进行货币、日期和类型检查。

数据已在 HTTP 响应中且不需要交互时,可使用 HTTP 客户端与解析器。Python 采集指南介绍了这种较轻的方式。

6. 启动会话前添加网络设置

不包含嵌入式鉴权的 HTTP 代理,可以通过 Chrome 选项在浏览器启动前选择:

import os
from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=" + os.environ["MIYA_PROXY_URL"])
driver = webdriver.Chrome(options=options)

使用端点提供的实际协议、主机与端口。不要假定此标记能够处理用户名密码鉴权;认证代理的配置取决于浏览器和环境。遵循 MIYAIP 连接指南,单独验证浏览器路由。

分别配置网络路径、等待条件和选择器。目标改变后,确认所需元素与返回数据范围。

常见问题

Selenium 会渲染 JavaScript 吗?

它控制能够执行页面脚本的浏览器。代码仍需要等到结果状态出现后再读取。

每次点击后都应使用 time.sleep() 吗?

应等待与预期页面变化关联的条件。固定延迟可能在一次运行中过慢,在另一次运行中又不够。

无头模式会改变结果吗?

可能影响渲染或网站行为。先用可见浏览器调试,再在任务实际使用的模式下验证相同字段和交互。

应用需要直接控制浏览器时继续使用 Selenium。任务已被 MIYAIP 接口目录支持时,可比较异步 Crawler API 方式。

选择由谁执行采集