跳至内容
免费开始
返回博客

网站数据抓取:一个实用的 DOM 工作流

Raluca Penciuc最后更新于 1 min read
网站数据抓取:一个实用的 DOM 工作流

开始提取

免费试用 WebScrapingAPI

领取 1,000 个免费 API 积分。无需信用卡。

开始使用
简而言之:网站数据抓取是指从网站的呈现层提取信息。对于普通网页,应先读取渲染后的DOM,再考虑截图和OCR;随后需验证记录,并证明数据采集已达到预定的终止点。

产品详情页上显示着名称和价格,但脚本下载的 HTML 代码中却只包含一个空容器。网站屏幕抓取通过在浏览器完成页面渲染工作后收集数据,来弥补这一差距。实际的挑战在于确定哪种呈现形式包含所需信息,以及该呈现形式何时准备就绪。

该术语也可用于描述从屏幕截图中读取像素。这两者属于不同的工程问题。浏览器可以在不识别任何图像的情况下,暴露结构化的元素、属性和文本。而 OCR 则从像素出发,推测其中包含的字符,从而丢失了 HTML 原本提供的许多关联关系。

本指南将构建一个小型、受控的浏览器数据提取练习,并将其思路扩展到实际的数据采集任务中。 您将定义记录、选择定位器、等待进入有效状态、识别不完整的结果,并在提取失败时保留证据。本示例使用在浏览器内部创建的页面,因此不依赖第三方网站,也不会向其产生流量。请仅将此工作流应用于您有权收集的页面和数据。

为网站屏幕抓取选择合适的层级

从单个必填字段开始,例如当前标价。在选择工具之前,先检查该字段的位置。它可能存在于初始 HTML 中,也可能在 JavaScript 运行后插入到 DOM 中,绘制在画布上,或包含在授权导出数据中。每种可能性都会改变最简单且可靠的采集方法。

可用表示形式

实用方法

需重点关注的主要限制

已文档化的API或导出文件

使用受支持的结构化接口

覆盖范围、访问范围和刷新时间

初始响应 HTML

解析下载的文档

数据可能仅在渲染后显示

渲染后的DOM

使用浏览器和作用域定位器

就绪状态与呈现结构的变化

Canvas 或图片中的文本

使用经过验证的图像提取和OCR技术

识别错误与关联关系丢失

从网站抓取数据并不需要截图。如果价格是产品卡片内的一个文本节点,提取该节点即可保留其与卡片之间的关联。将同一价格裁剪为图片会增加额外工作量:选择裁剪范围、识别字符、将结果与正确的产品关联,以及检测不确定的输出结果。

当初始文档包含所需字段时,HTTP 解析器仍然是一个不错的起点。我们的 Python HTTP 客户端指南详细解释了解析之前的请求层。当数据采集任务依赖于页面执行或允许的交互(例如选择标签页)时,浏览器便派上了用场。

将这种选择视为一次实验。通过最简单的方法检索一个经过授权的示例,将其字段与可见页面进行比较,并记录差异。不要仅仅因为某个网站在某些地方使用了 JavaScript 就采用浏览器自动化。反之,成功的 HTTP 响应并不能证明该响应包含所需的数据。

请在输出元数据中保留源数据表示形式。即使最终值看起来完全相同,从渲染文本中获取的记录与从图像中推断出的记录,其审核要求也可能不同。

在编写浏览器操作前定义记录

明确记录每行数据的含义。以商品目录观察为例,一份有用的记录可能包含源产品 ID、显示名称、原始价格文本、货币、页面 URL、观察时间以及提取版本。在运行浏览器之前,需确定哪些字段为必填项,哪些字段在合理范围内可以缺失。

当选择器在后台自动决定数据结构时,网站屏幕抓取会变得不稳定。缺失的价格节点不应自动变为零。标有“起价”的价格不应在未保留该限定词的情况下被视为确切价格。在不同轮播图中出现两次的产品卡片不应自动生成两个不同的产品条目。

当区分原始文本与规范化值至关重要时,请同时保留两者。例如,将“€1.249,00”作为观察到的原始文本存储,并仅在已知区域设置规则的情况下推导出数值金额。 猜测标点符号的惯例可能会将一个有效的价格转换成相差几个数量级的错误值。原始观察数据使您无需重新访问页面即可更正该转换。

选择记录标识时应独立于视觉顺序。如果页面以允许的表示形式提供了源 ID,则优先使用源 ID。当规范化商品 URL 的标识规则明确时,也可使用该 URL。页面上的第三张卡片并非稳定的标识,因为排序、推荐和库存变化都可能改变其位置。

同时需定义捕获上下文。视口、语言、账户及已选筛选条件均可能影响显示结果。当涉及授权账户时,应显式管理 HTTP Cookie 和会话所有权。切勿仅因“原本可行”就让互不相关的客户任务共享同一会话。

一条有用的验收规则应像测试用例一样:每张被接受的卡片都应具有不为空的 ID 和名称,价格文本应原样保留,重复的 ID 应被分类处理,且运行记录应注明是否完成。这为屏幕抓取网站提供了一个可观察的成功条件,而不仅仅是“脚本退出时未抛出异常”。

使用 Playwright 提取渲染后的测试数据

本练习将在短暂的异步延迟后创建两个虚构产品,标记商品目录已就绪,并提取结构化记录。在可丢弃的 Node.js 项目中安装 Playwright,安装其 Chromium 浏览器,并将 JavaScript 保存为 ES 模块,例如 collect.mjs。该示例不包含任何 API 请求、凭据或外部目标。

import assert from 'node:assert/strict';
import { chromium } from 'playwright';

const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.setContent(`
    <main id="catalog" data-ready="false"></main>
    <script>
      setTimeout(() => {
        document.querySelector('#catalog').innerHTML =
          '<article data-id="p1"><h2>Desk lamp</h2>' +
          '<span class="price">EUR 24.00</span></article>' +
          '<article data-id="p2"><h2>Notebook</h2>' +
          '<span class="price">EUR 6.50</span></article>';
        document.querySelector('#catalog').dataset.ready = 'true';
      }, 30);
    </script>
  `);
  await page.locator('#catalog[data-ready="true"]').waitFor();
  const records = await page.locator('#catalog article').evaluateAll(
    cards => cards.map(card => ({
      id: card.getAttribute('data-id'),
      name: card.querySelector('h2')?.textContent?.trim() ?? '',
      priceText: card.querySelector('.price')?.textContent?.trim() ?? ''
    }))
  );
  assert.deepEqual(records, [
    { id: 'p1', name: 'Desk lamp', priceText: 'EUR 24.00' },
    { id: 'p2', name: 'Notebook', priceText: 'EUR 6.50' }
  ]);
  console.log(JSON.stringify(records, null, 2));
} finally {
  await browser.close();
}

关键部分在于就绪契约,而非用于模拟工作的延迟。收集器等待一个表示测试对象已完成记录插入的属性。它绝不会假设三十毫秒在真实网站上也足够。该断言会验证整个结果,包括记录数量及其关联关系。

Playwright 的页面 API 文档介绍了如何设置页面内容,而其定位器 API 文档则介绍了如何评估匹配的元素。回调在页面上下文中执行并返回纯数据。浏览器 DOM 对象本身不会成为持久的应用程序记录。

在实际对网站进行屏幕抓取时,请将测试用例替换为导航至已批准的页面,并将它的就绪标记替换为该页面特有的验证依据。保留验证步骤。一个有用的初始负向测试是移除一个价格元素,并检查您的生产环境验证器是否会拒绝或隔离该不完整的行,而不是将其作为正常结果发布。

该测试数据集故意设计得足够简单以便理解。它不包含调度、持久化存储或通用爬虫。当您准备扩展到单一提取之外时,我们更全面的 Playwright 抓取指南将提供额外的浏览器工作流背景信息。

明确准备状态和选择器

页面可能在有用数据到达之前就已完成文档加载。此外,在所需数据已可用后,页面也可能保持网络连接处于打开状态。对网站进行屏幕抓取时,需要将条件与采集任务绑定,例如“结果标题”加上“加载完成指示器”,而不是任意暂停。

对于交互式操作,请通过稳定的属性定位控件,并将其范围限定在相关区域内。一个页面可能在产品图库、搜索结果和相关内容中包含多个“下一步”按钮。仅点击第一个匹配的文本并不能充分证明采集器已跳转到预期的列表。Playwright 在其定位指南中建议使用面向用户的属性并制定明确的契约。

区分操作就绪状态与数据集完整性。按钮可见且可点击并不意味着所有结果行都已显示。点击后,需验证页面标记或记录集是否发生变化,并确认特定页面的完成条件是否成立。若您维护源应用程序,添加一个稳定的就绪属性可使该契约可测试。

审慎选择文本提取方式。MDN 描述 innerText 会考虑渲染后的外观,而 textContent 读取节点文本时则不考虑渲染效果。两者均不能自动定义业务字段。应从特定的价格元素中提取数据,而不是读取整个卡片并期望稍后分离标签。

例如,一张卡片可能包含当前价格、划掉的价格、配送说明以及隐藏的辅助功能标签。宽泛的文本提取可能会将这四者混为一谈。请先确定记录需要显示当前价格、原价,还是两者都需要,然后使用独立的命名字段和定位器。

当选择器失效时,应明显提示错误。避免使用那种逐步扩大范围直至找到无关文本的回退链。在抓取网站数据时,明确的“字段缺失”错误比运行成功但结果充满看似合理却不正确的值的情况更容易修复。

验证分页和滚动是否完整

如果任务要求获取整个类别,那么仅从第一页提取的十张正确卡片仍不构成完整的数据集。在引入循环之前,应定义停止条件。对于分页页面,这可能是文档中记录的最后一页指示符或已禁用的“下一页”控件;对于经过批准的“加载更多”界面,则可能是明确的结束标记。

对采用无限滚动功能的网站进行屏幕抓取时需格外谨慎,因为某些列表会“虚拟化”其内容。当用户浏览大型数据集时,这些列表会重复使用一小部分 DOM 节点。因此,仅统计当前文档中的元素可能会低估已出现的记录数量,而仅在最后时刻截取一次 DOM 快照则可能遗漏早期的记录。

在满足就绪条件后收集每批数据,并利用源标识符对观察结果进行去重。跟踪已接受的 ID、最后的导航标记,以及连续未产生新 ID 的尝试次数。将“无进展”视为一种保护性停止,状态标记为“未完成”,除非源还提供了可靠的结束条件。

即使接口看似无穷无尽,也要采用有界处理。根据任务需求设置最大页数、耗时和记录数上限。记录是哪种边界条件终止了运行。“在配置的页面预算用尽后停止”和“到达源的最后一页”对下游用户而言具有不同的含义。

在已接受的数据被持久化存储后保存检查点。如果进程在采集和持久化之间崩溃,从过早保存的光标恢复可能会跳过某些记录。当您的接收端根据稳定的标识和观察策略进行去重时,重放页面会更安全。

对于不断变化的目录,完整性也具有时间性。在运行期间,项目可能会在页面之间移动。请记录采集时间窗口,并避免将输出称为精确快照,除非数据源提供了此类保证。每日观察任务即使不假装描述某个瞬时状态,也仍具有实用价值。

这些边界将网站屏幕抓取从一个浏览器宏转变为一个可解释其限制并可进行测试的数据采集过程。

仅在像素细节至关重要时才使用截图和 OCR

当视觉呈现本身是问题的一部分时,截图便是有用的证据。它可以显示缺失的价格、意外弹出的对话框,或者不再符合数据提取协议的布局。Playwright 通过其截图接口支持页面和元素的捕获。

OCR 是对该图像应用的独立转换。当授权信息确实仅以像素形式存在(例如绘制在画布上的文本),且不存在合适的结构化表示时,才应考虑使用它。切勿将其用于解决访问难题或绕过网站的访问限制。请暂停操作,并通过网站所有者解决权限或访问问题。

通过 OCR 抓取网站内容时,需要制定不确定性处理策略。相似字符、小数点符号、被截断的文本以及低对比度都可能改变输出结果。即使通过了基础数值解析器的字符串,仍可能代表错误的价格。请将相关图像区域、识别出的文本、转换版本以及任何可用的置信度信号一并保存。

针对高影响字段,需参照独立约束条件进行复核。识别出的货币金额可能既需要已知的货币标签,也需要合理的格式。产品标识符可能具有固定长度或校验和。这些检查虽能检测到部分错误,但并不能证明看似合理的结果与源图像完全一致。

对于用作调试证据的截图,应裁剪至相关区域并限制保留时间。获得页面的授权访问权限并不意味着每个可见的账户详情都应纳入永久性诊断档案。应记录截图原因,限制访问权限,并在调查不再需要时将其删除。

对 HTML 快照也应采取同样的谨慎态度:即使截图看起来无害,其中仍可能包含隐藏字段和特定于账户的内容。仅存储重现数据提取问题所需的最低限度证据。

关键要点

  • 在选择工具之前,请先检查源数据表示形式。原始 HTML、渲染后的 DOM 和图像像素需要不同的提取和验证策略。
  • 确保“就绪”状态能准确描述所需数据,并对完整的提取记录进行验证,而非仅检查浏览器操作是否成功。
  • 在将采集器扩展到一页以上之前,需明确定义身份识别、分页终止、检查点时间以及运行未完成的状态。
  • 当规范化可能改变数据含义时,请保留原始观测数据。有选择地使用截图,并对 OCR 输出应用明确的审核规则。

常见问题

浏览器扩展能否作为有用的原型设计工具?

是的。在构建定时收集器之前,扩展程序可帮助您检查页面或测试小型数据提取方案。在将其安装到经过身份验证的浏览器之前,请先评估其数据访问和导出行为。成功的手动原型在成为可靠的生产任务之前,仍需具备可重现的输入、验证机制以及运维责任归属。

在采集过程中是否应将提取的文本进行翻译?

通常应在原始记录中保留源语言,并在单独的步骤中进行翻译。翻译会改变观察结果,并可能使不同运行之间的比较变得复杂。请分别存储源语言、翻译结果和转换版本,这样在修正翻译时就无需再次访问源页面。

团队应如何移交现有的采集器?

应提供提取协议、一个经批准的小型测试用例、预期输出以及分类失败的示例。需明确源数据的访问权限归属,以及哪些下游任务会使用该结果。接手维护者应能够复现通过验证的提取结果,并识别不完整的运行结果,而无需依赖原始作者的浏览器历史记录。

构建结果可解释的采集器

在网站屏幕抓取中,第一个有用的里程碑是具有已知来源的经过验证的记录。以此为起点,仅在导航、数据持久化和调度任务的成功条件同样明确时,才添加这些功能。您应能够解释每个字段的来源、页面为何准备就绪,以及导致运行停止的原因。

在进行更广泛的部署之前,请模拟字段缺失、页面重复以及源头变更但未产生新记录的情况。确认这些场景会导致明确的分类结果,而非空白的成功报告。这一小部分失败案例往往能揭示您的工作流是否具有真正的数据契约,还是仅仅是一系列浏览器操作。

如果浏览器基础设施的运维成为限制授权数据采集工作流的关键因素,请评估 WebScrapingAPI 的浏览器 API 以实现托管式浏览器控制。请将记录模式、就绪规则和验证逻辑与基础设施选择分离,以便在系统扩展时仍能对其进行审查。

关于作者

Raluca Penciuc, 全栈开发工程师 @ WebScrapingAPI

Raluca Penciuc

全栈开发工程师

Raluca Penciuc 是 WebScrapingAPI 的全栈开发工程师,主要负责开发爬虫、优化规避机制,并探索可靠的方法以降低在目标网站上的被检测概率。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。