Selenium 采集:能力与记录
使用明确的 Chromium 二进制、会话 capabilities、页面就绪条件和记录校验,构建有界 Selenium 采集流程。
BotBrowser Team
Selenium 可以控制浏览器执行获准的数据采集任务,但成功建立 WebDriver 会话只是其中一个环节。可靠的采集还需记录浏览器二进制与协商后的会话信息,等待自有页面的真实就绪信号,校验提取记录,并在站点政策或响应要求停止时及时停止。BotBrowser 文档说明可通过文件式 profile 输入和 CLI 参数配置浏览器启动;这些设置不保证 profile 已加载、Selenium/Grid 兼容性或第三方站点的响应。WebDriver 会话及应用专属的数据采集约定仍由使用方负责。
摘要
- 使用浏览器选项 API 显式选择浏览器可执行文件,然后记录新 WebDriver 会话返回的 capabilities,而不是假设请求的值已被采用。
- 将 BotBrowser 配置文件、Chromium 用户数据目录、Selenium 服务和运行输出分别管理,并为每项明确指定负责人。
driver.get()报告文档导航边界;目录应用程序的客户端呈现结果需要一个单独的、有限的就绪条件。- 将行验证为数据:在接受之前需要其 title、同源详细信息 URL 以及 price 或显式可用性状态。
navigator.webdriver值和返回的 capabilities 描述自动化控制的会话;它们不证明身份、授权、配置文件加载或网站的决定。
目录
定义运行及其证据
从您拥有或获准收集数据的目录开始。记下允许的入口 URL、下游任务需要的字段、记录数上限、数据所有者允许的请求频率和停止条件。这些约定能区分真正为空的目录页与悄然接收格式错误数据的脚本。有界运行可以读取一个目录页,最多收集 40 张可见卡片,并为每张通过校验的卡片输出一行 JSON。40 是该任务的数据所有者设定的上限,不是给其他站点的建议请求频率。
将任务契约放在代码旁边。例如,将 title 定义为修剪后的非空文本,将 href 定义为批准的目录源上的绝对 URL,将 price 定义为十进制金额加货币或显式 availability 值(例如 out-of-stock)。不要将缺失值变成虚构的默认值,例如零。零 price、不存在的 price 和隐藏在同意提示后面的 price 是三种不同的状态。保留记录中的区别,以便消费者可以决定该行是否可用。
运行记录应保存可能影响结果、但无需保留页面内容的输入:应用测试夹具版本、入口 URL、浏览器二进制标识、驱动程序版本、浏览器版本、页面加载策略、必要时的区域设置、开始时间,以及通过校验和被拒记录的数量。不要记录账号密钥、cookie、授权标头、完整 profile 目录或无关页面文本。若故障需要截图或 DOM 摘录,只捕获自有合成测试夹具或获准的最小区域,先编辑用户数据,并遵循应用的数据保留策略。
在接入接近生产环境的采集任务前,先使用已知的测试夹具。自有夹具可以提供可预测的就绪标记、少量记录、一个缺少 price 的记录,以及一个模拟拒绝访问的状态。这样可验证脚本会接收有效记录、拒绝不完整记录,并在策略边界处停止。夹具测试的是数据提取约定,不能证明其他站点会如何响应,也不能说明检测器会作何推断。
对于包含可能指向详细信息页面的链接的列表页面,请在实施之前确定详细导航是否在范围内。该示例故意仅记录目录卡。它不会访问它们的链接,因为详细信息页面导航引入了第二页合同、附加请求和新的权限边界。如果数据所有者授权,单独的任务可以定义这些规则。这种区别使列表快照保持可审计性,并防止方便的循环以静默方式扩展集合。
选择浏览器并配置会话
Selenium 的 Chrome 文档展示了如何通过 Chrome 选项选择浏览器二进制并添加启动参数。执行环境通过这些选项选择已安装的 Chromium 兼容程序;这不代表它与所有浏览器或驱动程序版本都兼容。应按环境的发布流程固定浏览器和驱动程序版本,并将两者都写入运行记录。如果会话无法启动,应先排查二进制文件定位和驱动程序兼容性,再考虑修改页面选择器或数据架构。
BotBrowser 的 CLI 配方展示了如何通过 --bot-profile 指定 profile 文件,并使用独立的 --user-data-dir。Profile 配置参考说明,CLI 参数优先于 profile 配置和默认值;公开的旧式 .enc 文件为只读,profile 以文件方式提供,且 profile 主版本必须与浏览器二进制的主版本一致。这些是浏览器启动参数,不是 Selenium capabilities,也不是通过 Options.set_capability() 创建的。在明确支持所选二进制和驱动程序的环境中,获准的执行节点可以将文档列出的浏览器参数传给 Chrome options。执行节点必须使用绝对、获准的 profile 文件路径和独立的用户数据目录,并遵循适用的产品与组织策略。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
options = Options()
options.binary_location = "/opt/approved-browser/chrome"
options.add_argument("--bot-profile=/srv/browser-inputs/profile.enc")
options.add_argument("--user-data-dir=/srv/browser-state/run-2026-11-08-01")
options.page_load_strategy = "eager"
driver = webdriver.Chrome(options=options)
try:
driver.set_page_load_timeout(35)
driver.get("https://catalogue.example.test/items")
WebDriverWait(driver, 18).until(
lambda browser: browser.find_element(
"css selector", "[data-testid='catalogue-ready']"
).get_attribute("data-state") == "ready"
)
finally:
driver.quit()
上面的路径是授权运行者配置的路径的占位符。不要将凭据放置在配置文件参数、源文件或日志中。不要同时重用配置文件或用户数据目录。托管目录可以包含在一次脚本调用之后持续存在的状态;分配一个所有者,决定它是临时的还是保留的,并在重用之前关闭浏览器。 Selenium 驱动程序会话指南 描述了通过构建驱动程序并以 quit() 结束来创建会话。 finally 块很重要,因为导航、准备就绪和提取都可能在运行正常结束之前引发。
对于远程 WebDriver 或 Grid,浏览器可执行文件和文件路径由启动浏览器的节点解析,不一定由提交 Python 命令的进程解析。客户端上的绝对路径不会因为本地存在,就自动成为远程节点上的有效路径。部署时应明确节点镜像、浏览器版本、驱动程序配置、profile 文件交付和临时存储约定。如果执行环境无法确认 profile 的挂载位置和用户数据目录的负责人,就不要在状态不明时启动会话。
选择文档边界的页面加载策略,而不是作为动态应用程序准备的捷径。 Selenium 将 normal 记录为等待 complete 文档状态,将 eager 记录为文档交互时返回,将 none 记录为不等待页面加载状态。该选项适用于 WebDriver 会话。较慢或较快的设置并不能确定单页应用程序的目录数据何时可用;显式就绪条件仍然拥有该应用程序合同。对于大多数数据页,从默认的 normal 开始,除非应用程序有理由提前返回并且脚本具有显式的页级谓词。
记录会话能力,不作过度解读
驱动程序启动后,只保存经审查的 driver.capabilities 字段。Selenium 的浏览器选项参考记录了常见会话能力字段,包括 browserName、browserVersion、platformName 和 pageLoadStrategy;使用相应的选项类时,浏览器名称由该类设置。这些值是 WebDriver 协商会话的回执,可用于比较执行节点的配置请求与驱动程序返回值;但不能证明加密 profile 已被接受、profile 支持的每个表面都已加载,或网站已接受该浏览器。
capabilities = driver.capabilities
capability_receipt = {
"browserName": capabilities.get("browserName"),
"browserVersion": capabilities.get("browserVersion"),
"platformName": capabilities.get("platformName"),
"pageLoadStrategy": capabilities.get("pageLoadStrategy"),
}
继续前先验证回执。如果 browserName 不属于预期的浏览器系列,或者 browserVersion 不在执行环境允许的范围内,就停止并将其归类为会话配置失败。不要根据请求的二进制路径改写返回的版本字段。驱动程序提供的其他厂商字段,只有在其含义有文档依据且采集任务确实需要时才记录。转储整个 capabilities 对象会保留实现相关的元数据、增加比较难度,却不一定改善结果。
将请求的配置与返回的回执保存在不同字段中。配置可能指定 page_load_strategy="eager",但驱动程序返回的协商值可能不同,应如实记录这项差异。同样,执行环境配置中的 profile 路径只是输入,不能据此断言浏览器已加载该 profile。只有通过产品文档支持的启动证据和相应授权流程,才能确认 profile 加载状态。这些代码是说明性示例,不是实测结果;当前没有 BotBrowser 运行记录或第三方响应记录可供引用。
回执应在导航前写入,并在运行结束时补上结果。可用的终止结果类别包括 session-start-failed、navigation-timeout、readiness-timeout、access-boundary、schema-rejected、complete 和 cleanup-failed。保留最先导致采集停止的故障类别;后续清理异常可另行记录。这样可避免脚本在网站要求停止后仍继续查找页面,并把访问限制误报成选择器错误。
等待目录准备就绪
成功的 driver.get() 并不意味着每个目录行都已插入或水化。它报告选定的文档导航边界。客户端代码可以在该点之后获取数据,呈现加载占位符,然后用卡片替换它。相反,页面可以在请求的列表为ready之后继续下载不相关的推荐。等待每个网络活动停止不一定是有意义的业务条件。该集合应命名特定标记,这意味着拥有的目录的请求行是 ready。
在授权页面公开的稳定条件周围使用一个显式 WebDriverWait。具有记录的 data-state="ready"、可访问的 status 消息或已知结果计数的元素可以构成合适的谓词。避免轮询仅由动画使用的任意类或等待固定的 10 秒。当结果很快为 ready 时,固定暂停可能会浪费时间,而当服务需要更长的时间时,固定暂停仍然会失败。当未达到条件时,有界等待会返回可操作的结果。从拥有的服务预算中选择其超时;代码中的 18 秒是说明性的,而不是通用的 Selenium 值。
就绪谓词应当只读。WebDriver 可能会重复计算它,因此谓词不能点击控件、提交表单、滚动触发更多记录,也不能以副作用方式修改页面。如果分页属于获准的采集范围,应把它建模为单独的状态转换:先验证当前页的就绪条件并收集通过校验的记录,再执行一次翻页操作,随后等待下一页的独立状态或页面标识。达到约定上限后停止并记录上限已触发;如果页码没有变化,不要继续点击。
节流可以产生不同的可观察结果。有界重试响应、要求用户等待的应用程序 status 区域、质询页面、登录重定向和不再匹配的选择器不可互换。尊重网站记录的访问政策和所有者允许的请求预算。当页面显示访问或策略边界时,停止运行并仅保留所有者所需的最小类别。不要自动解决挑战、绕过控制或增加请求强度来强制结果。 Selenium 脚本可以报告预期的应用程序状态未到达;它无法决定是否允许继续访问。
验证记录并对结果进行分类
在序列化之前将每一行提取到类型记录中。修剪可见文本中的空格,解析与目录 URL 相关的链接,要求允许的来源,并拒绝格式错误的值。如果价格具有特定于区域设置的分隔符,请在页面声明的区域设置下解析它们,而不是盲目删除标点符号。如果页面可以同时显示 price 和可用性,请保留两者。当任务仅需要 title、URL 和 price/status 对时,避免存储产品描述或页面文本。
from urllib.parse import urljoin, urlparse
def normalized_origin(url):
try:
parsed = urlparse(url)
hostname = parsed.hostname
port = parsed.port
except ValueError:
return None
scheme = parsed.scheme.lower()
if scheme not in {"http", "https"} or not hostname or parsed.username is not None or parsed.password is not None:
return None
effective_port = port if port is not None else (443 if scheme == "https" else 80)
return scheme, hostname.lower(), effective_port
def parse_card(card, page_url, allowed_origin):
title = card.find_element("css selector", "[data-testid='title']").text.strip()
href = card.find_element("css selector", "a[data-testid='detail-link']").get_attribute("href")
absolute_url = urljoin(page_url, href or "")
price_nodes = card.find_elements("css selector", "[data-testid='price']")
status_nodes = card.find_elements("css selector", "[data-testid='availability']")
price = price_nodes[0].text.strip() if price_nodes else None
status = status_nodes[0].text.strip() if status_nodes else None
if not title or normalized_origin(absolute_url) != allowed_origin:
return None, "schema-rejected"
if not price and not status:
return None, "missing-price-or-status"
return {"title": title, "href": absolute_url, "price": price, "status": status}, None
allowed_origin 应由获准的入口 URL 通过同一个 normalized_origin 函数计算,不要从提取出的链接中构造。该示例只接受 HTTP 和 HTTPS,并比较规范化后的协议、主机名和有效端口(默认值为 80 或 443);无效端口会被拒绝。应集中处理 URL 规范化,并测试默认端口、国际化主机名、重定向和相对链接。不要仅因卡片中出现 href 就访问该地址;提取数据和页面导航需要分别授权。
避免将空结果集视为成功的集合。空列表可能是有效的目录,但它也可能意味着准备标记出现在数据插入之前、过滤器删除了所有行或页面契约发生更改。需要结果计数或显式的空状态标记。如果页面声明零结果,则发出带有页面修订的 empty-catalogue 结果并停止;不要推断选择器已损坏或无限期地重试同一请求。如果预期的 ready 标记本身不存在,则对就绪超时进行分类,而不是返回空的成功文件。
提取循环应统计结果,并为每条被拒记录保留原因。例如,36 条通过校验、2 条缺少价格、2 条违反同源限制,与 40 条全部通过校验并不相同。摘要可以报告 accepted=36、rejected=4 及各原因计数,而不保存被拒页面的正文。这样能显示数据架构变化,同时尽量减少个人或商业数据留存。如果下游任务要求每个产品只保留一条记录,就拒绝重复的规范化 URL 并报告重复数,不要静默覆盖较早记录。
使用故障表将诊断直接指向拥有它的层:
| 结果 | 保留证据 | 下一位业主 |
|---|---|---|
| 浏览器或驱动程序无法创建会话 | 已脱敏的驱动程序错误、请求的二进制标识、驱动程序和浏览器版本 | 执行环境镜像负责人 |
| 功能与许可名单不匹配 | 四领域能力接收和要求的策略 | WebDriver 配置所有者 |
| 导航超出其界限 | 条目 URL 主机,超时类别,最后 ready 状态 | 应用程序或网络所有者 |
| 目录准备就绪 | ready-条件名称、经过的等待、结果计数(如果存在) | 页面合同所有者 |
| 记录不符合架构 | 字段级原因和被拒记录总数 | 数据提取约定负责人 |
| 站点报告访问边界 | 响应类别或可见status,无挑战内容 | 网站政策所有者 |
quit() 在已有故障后再次报错 | 原始故障和清理错误类别 | 执行环境生命周期负责人 |
此表并不许可收集超出需要的诊断信息。完整的 HAR、配置文件副本、浏览器数据库或经过身份验证的屏幕截图可以包含凭据和不相关的活动。从最小的有用证据开始,只有在所有者授权且隐私政策允许的情况下才进行扩展。将输出路径保持在运行范围内,并根据项目的数据保留规则使它们过期。
保持身份边界明确
profile 文件、浏览器二进制文件、WebDriver 选项和暴露给页面 JavaScript 的值彼此相关,但属于不同层。Profile 配置可以提供文档记载的浏览器设置,命令行覆盖优先于 profile 配置和默认值。按照产品文档,所选二进制文件必须与 profile 的浏览器主版本兼容。驱动程序 capabilities 描述协商后的 WebDriver 会话。这些事实都不能单独证明页面可见的每个值、不同运行之间的身份连续性,或服务器如何分类请求。
MDN 将 navigator.webdriver 描述为只读布尔值,指示用户代理是否由自动化控制。它是一种浏览器可见的自动化信号。它不是身份令牌、权限授予、总体指纹或站点检测系统的判决。如果授权拥有的设备需要记录它以进行兼容性测试,请记录布尔值及其页面和浏览器上下文;不要将其解释为不相关访问决策的 pass/fail 分数。
在报告中保持同样的区别。一个有用的示例表述是:“WebDriver 会话以浏览器版本 X 启动,测试夹具返回 36 条有效目录记录。”不受支持的表述包括“浏览器看起来像普通用户”“profile 证明了账户身份”或“网站会接受此会话”。第一种说法来自返回的 capabilities 和自身的数据架构校验;其他说法则超出了这些观察能够证明的范围。
BotBrowser的公开配置文件配置参考和CLI配方描述了浏览器输入;它们不建立 Selenium 驱动程序兼容性或重写 WebDriver 协议语义。团队必须在其支持的环境下验证其特定的二进制文件、驱动程序、配置文件包、操作系统和部署配置。请勿提供通用 Selenium 片段作为产品认证或承诺第三方应用程序将允许收集。
对于相邻的区别,Selenium 浏览器配置文件集成和会话所有权 涵盖存储所有权和配置文件生命周期,而 Selenium WebDriver 等待和稳定测试 更深入地涵盖等待语义。抓取记录受能力收据、一页就绪条件、行模式和停止边界的限制。浏览器实现细节不应取代数据所有者的许可或应用程序合同。
常见问题
driver.get() 是否意味着目录已准备就绪?
不。页面加载策略标记文档状态;应用程序可以稍后填充其结果。等待拥有的、有界的就绪信号。
功能是否证明配置文件已加载?
不。它们描述了协商的会话。配置文件加载需要产品支持的启动证据和授权程序。
是否应该打开每个提取的链接?
不会。详细导航扩展了任务及其请求。仅当范围和授权定义了第二份合同时才执行此操作。
来源
- Selenium Chrome 特定功能
- Selenium 驱动程序会话
- Selenium 浏览器选项和功能
- BotBrowser CLI 食谱
- BotBrowser配置文件配置
- MDN:导航器.webdriver
实践结论
将 Selenium 采集任务视为一组可分别检查的边界:获准的任务与记录模式、选定的浏览器和驱动程序、返回的 capabilities 回执、有界的就绪条件、通过校验的记录,以及正常关闭会话。保持 profile 输入与用户数据目录分开,在责任层记录首个故障,并在站点的访问边界处停止。运行记录只能说明执行环境在自有目录中观察到什么;没有产品自身的证据,就不能证明 profile 已加载,也不能预测第三方网站的决定。
本文属于指南: 浏览器自动化入门:首次运行地图