跳至内容
返回博客

Ruby HTML 和 XML 解析器:基于工作负载的比较

Raluca Penciuc最后更新于 3 min read
Ruby HTML 和 XML 解析器:基于工作负载的比较
简而言之:选择 Ruby HTML 和 XML 解析器,应根据具体工作负载来决定,而非依赖于泛泛的排名。对于广泛的 HTML 和 XML 树解析,建议首先使用 Nokogiri;针对特定的 XML 任务,可考虑 Ox 或 LibXML Ruby;仅当必须由真实浏览器渲染页面或与页面交互时,才添加 Selenium。

解析器将 HTML 或 XML 文本转换为 Ruby 代码可查询、转换或验证的结构。选择合适的 Ruby HTML 和 XML 解析器,与其说取决于流行度,不如说取决于进入解析边界的数据类型:不完美的网页 HTML、严格的 XML、海量数据流、序列化对象,或是浏览器渲染的 DOM。

本指南通过一套统一的框架对六种工具进行了比较。您将了解到 CSS 选择器、XPath、命名空间、验证、XSLT、树模型和流式 API 在何处至关重要,以及原生库在何处可能导致部署复杂化。此外,本指南还将数据检索与解析区分开来。 登录失败、请求被阻塞或客户端渲染元素缺失等问题,通常属于 HTTP 或浏览器层,而非解析器本身。

下文的建议均带有条件性。所提供的证据确立了工具的广泛作用,但关于发布版本、兼容性、解析器模式及性能的若干声明,仍需查阅最新的官方来源以确认。请将此比较视为候选名单和测试计划,并在最终确定前,根据您的 Ruby 运行时和部署平台验证确切的 gem 版本。

快速选择:按工作负载划分的最佳解析器

本草案中未对当前发布版本及支持的 Ruby 版本进行实时验证,因此对状态敏感的建议需在发布时进行核查。

  • 对于静态或不完整的 HTML 以及通用的 XML 树处理,建议选择 Nokogiri 作为实用的起点。
  • 当 XML 解析、写入、SAX 风格处理或对象序列化是核心任务时,请选择 Ox
  • 当您的 XML 处理流程需要 XPath 加上验证或 XSLT 功能时,请选择 LibXML Ruby
  • OgaHpricot 视为替代方案,但需特别仔细地审查其兼容性和维护情况。
  • 仅当渲染、点击、滚动、表单或浏览器状态是工作内容的一部分时,才使用 Selenium

这种以工作负载为先的划分,比宣称某一套 Ruby HTML 和 XML 解析器是放之四海皆准的最佳选择更为实用。

Ruby HTML 和 XML 解析器对比

相同的标准适用于所有六种工具。星号(*)标记的证据在当前官方文档中尚待确认。

工具

适用范围与最佳场景

CSS / XPath

模型

命名空间、有效性验证、XSLT

运行时需求

状态

Nokogiri

HTML/XML、通用提取

CSS 和 XPath

DOM;据报告支持 SAX/Push*

已报告的 XML 功能和 XSLT*

原生解析器后端*

立即验证

Ox

XML、写入、序列化

已报告的XPath限制*

树结构和SAX

报告了命名空间/XSLT限制*

待验证的扩展详情

立即验证

LibXML Ruby

XML 验证/转换

XPath

DOM/SAX/Reader/Writer 已报告*

已报告支持 DTD、RelaxNG、XSD、XSLT*

libxml2 链*

立即验证

Oga

HTML/XML、以XPath为中心的工作

XPath

XML命名空间;其他功能尚未确定

Ruby 加上少量扩展

立即验证

Hpricot

现有HTML代码

CSS/XPath 报告*

报告称支持 XML;但未进行验证*

报告的 C 组件*

立即验证

Selenium

已渲染页面及交互

CSS/XPath定位器

浏览器DOM

并非 XML 验证工具

浏览器和驱动程序

立即验证

Ruby ToolboxHTML 解析类别提供了有用的软件包背景信息,但仅凭目录数据无法确定当前的兼容性或通用排名。

根据文档类型和操作限制进行选择

在比较 API 之前先对输入进行分类。需确认其是 HTML 还是 XML,是否需要容错恢复或严格报错,是否能放入内存,以及需要哪些查询或转换功能。

添加运行时限制:原生编译、系统库、Ruby 运行时、容器镜像以及部署目标。最后,检查字节流中是否包含所需数据。若未包含,则必须先进行数据检索、身份验证或浏览器渲染,Ruby HTML 和 XML 解析器才能提取数据。

静态或格式错误的 HTML 与严格的 XML

Web HTML 通常并不完美。Ruby HTML 解析器应能在标签缺失、嵌套异常和片段的情况下恢复出可用的树结构,同时保持查询的可预测性。Nokogiri 基于源代码支持 CSS 和 XPath,并能处理格式不规范的 HTML,因此是合理的首选尝试。

XML 则有不同的规范。结构、命名空间和验证规则可能是业务需求,因此无提示的恢复可能比明确的错误更糟糕。请选择专注于 XML 的解析模式,并定义错误的报错方式。

数据获取是独立的。Cookie、身份验证、重定向和 Ajax 决定了您将收到哪些标记。如果响应中包含目标节点,请直接解析它。如果该节点是由 JavaScript 随后生成的,请先进行渲染,然后解析生成的 DOM。

DOM 树与 SAX 及流式处理模式

DOM 树将文档加载到内存中的节点图中。这便于进行父节点、兄弟节点或子节点的导航以及重复查询。内存占用量随文档大小和解析器的树形表示而增加。

SAX 或读取器风格的接口会在解析器推进时触发事件。它适用于大型数据源、仅追加式转换,以及无需保留整个文档即可生成记录的管道。其权衡在于状态管理:处理程序必须显式跟踪上下文、命名空间和部分记录。

在这些 Ruby HTML 和 XML 解析器中,提供的证据表明 Ox 支持 SAX 处理,而 Nokogiri 和 LibXML Ruby 则支持多种非 DOM 接口。在围绕它们进行设计之前,请通过测试用例确认确切的 API 并测量峰值驻留内存。

选择器、验证、依赖关系与维护

在数据提取方面,需确定团队更倾向于使用 CSS 选择器、XPath 还是两者兼用。CSS 对于类和属性模式而言更为简洁;而 XPath 通常更适合文本条件、轴以及支持命名空间的 XML。针对 XPath 与 CSS 选择器进行专项审查,可避免选择器风格演变为无意的架构。

对于 XML,请分别检查命名空间注册、DTD 或模式验证、XSLT 以及错误报告。切勿根据某一项功能推断另一项功能。安装环节也应同样严格:原生扩展虽可提升功能,但可能会增加编译器、系统库或镜像构建的工作量。

在决策记录中注明 gem 版本、Ruby 版本、原生库版本以及审查日期。升级时应重新运行测试用例,而非将“安装成功”视为兼容性的证明。

Nokogiri:通用的默认选择

在这一系列 Ruby HTML 和 XML 解析器中,Nokogiri 是源代码支持最全面的起点。它能解析 HTML 和 XML,提供 CSS 和 XPath 查询功能,并能从不完整的 HTML 中构建有用的树结构。这种组合适用于 Ruby 中的常规网页抓取、数据源清理、文档检查,以及需要对节点进行随机访问的转换操作。

require "nokogiri"

html = "<article><h1>Parser choice</h1><a href='/next'>Next</a></article>"
doc = Nokogiri::HTML.parse(html)

title = doc.at_css("article h1")&.text&.strip
next_url = doc.at_xpath("//a[normalize-space()='Next']")&.attr("href")

安全导航调用至关重要,因为即使某个选择器在单个样本上运行正常,在生产环境中仍可能失败。需判断字段缺失是表示 nil跳过一条记录,还是发生严重错误,并将其判断结果编码到测试中。

Nokogiri 不会抓取页面,也不会执行网站的 JavaScript。当响应中已包含数据时,请将其与 HTTP 客户端配合使用。当身份验证或客户端渲染导致返回的文档发生变化时,请升级数据获取层。

提供的资料还指出,Nokogiri 支持 SAX、Push 解析、XSLT 以及多种原生解析器后端。请将这些功能视为需经过验证才能启用的特性,特别是在 MRI 与其他运行时之间进行选择,或构建精简容器时。

Ox 与 LibXML Ruby 在 XML 优先场景中的对比

Ox 和 LibXML Ruby 分别解决了不同的“XML 优先”问题。Ox 涵盖基于树的 XML 输入和输出、Ruby 对象序列化以及事件驱动的 SAX 处理。当 Ruby XML 解析器需要序列化对象或处理大型文档且无需复杂查询时,值得将其列入候选名单。

LibXML Ruby 封装了 libxml2,适用于需要大量查询的处理管道,例如涉及 XPath 查找、模式检查或 XSLT 转换的场景。该 gem 的目录快照还列出了树、事件、拉取式读取器和写入器接口,以及 DTD、RelaxNG 和 XSD 验证功能。请根据您计划部署的具体版本核对这些细节。

xml = "<person><name>Ada</name></person>"

require "ox"
ox_tree = Ox.parse(xml)

require "xml"
libxml_doc = XML::Parser.string(xml).parse
name = libxml_doc.find("//name").first&.content

切勿将过时的基准测试结果演变成“Nokogiri 与 Ox”的传说。 公平的测试应统一 Ruby 版本、编译器参数、原生库版本、文档结构、编码方式、预热时间、重复次数,并明确测量对象是树结构构建、查询、序列化还是流式处理。请分别报告吞吐量、延迟、内存分配量和峰值内存占用。

提供的资料报告了 Ox 在 XPath、命名空间和 XSLT 方面的潜在限制,以及 LibXML Ruby 的原生依赖链。在选择之前请核实这两点,特别是对于命名空间密集型集成和可移植容器构建。

Oga 和 Hpricot:需注意现状的替代方案

Oga 和 Hpricot 属于决策树中需关注当前状态的分支。 Oga 的大部分实现采用 Ruby 编写,使用一个小型扩展,并通过 XPath 以及支持命名空间的 XML 查询来处理 HTML 和 XML。Hpricot 的核心功能是 HTML,尽管现有证据表明它也能处理 XML,并提供 CSS 和 XPath 选择器。

现有证据还指出 Oga 近期活动寥寥,而 Hpricot 已不再维护,但这些并非最新的官方核查结果。在采用其中任何一个之前,请检查最新版本、Ruby 支持情况、兼容性问题,以及您的目标环境是否能构建其扩展。

对于现有应用程序,替换风险可能需要通过由测试用例保护的适配器来应对。对于新应用程序,在选择其中任何一个作为 Nokogiri 的替代方案之前,必须确保通过兼容性测试,并明确维护负责人。

何时应将 Selenium 纳入流程

Selenium 是一种浏览器自动化工具,而非普通的 Ruby HTML 解析器。当 JavaScript 生成了所需的 DOM,或者工作流必须进行点击、滚动、提交表单或保留浏览器状态时,请使用它。官方的 Selenium WebDriver 文档详细解释了其浏览器控制模型。

建议从 HTTP 请求配合解析器开始。如果保存的响应缺少浏览器中可见的数据,则仅将渲染或交互操作移交至 Selenium。随后使用定位器提取数据,或将渲染后的页面源代码传递给 Nokogiri 进行 CSS 或 XPath 处理。

这会增加浏览器进程、驱动程序管理、等待操作以及更多故障模式。当前的等待语义和驱动程序配置受版本影响,因此请务必进行验证,而非照搬过时的指导。对于 HTTP 客户端已经返回的静态标记,请勿为此付出额外开销。

面向生产环境的解析工作流

对于生产环境的数据解析,请采用明确的输入和输出约定:

  1. 接收或获取字节数据后,记录最终的 URL、状态码、内容类型及声明的字符集。
  2. 在运行选择器之前解析编码。在转码或输入格式不正确时,请保留原始字节,以便后续排查。
  3. 根据所选模式进行解析:HTML 容错模式、严格 XML 模式或流式解析模式。
  4. 使用能刻意处理缺失节点的选择器来提取字段。
  5. 在输出记录之前,验证必填字段、类型、命名空间或模式。
  6. 保存具有代表性的测试数据,包括格式错误的标记、布局变更、空字段以及非 UTF-8 样本。
  7. 在每次解析器或依赖项升级时,针对这些测试数据运行选择器测试。
def extract_title(html)
  doc = Nokogiri::HTML.parse(html)
  title = doc.at_css("article h1")&.text&.strip
  raise KeyError, "article h1 missing" if title.to_s.empty?
  { title: title }
end

使用对测试数据安全的上下文记录解析错误,而非记录完整的敏感文档。将不可信的 XML 视为单独的安全审查对象,而非默认认为解析器的默认设置是安全的。

最终解析器选择检查清单

在确定采用这些 Ruby HTML 和 XML 解析器之前,请回答以下问题:

  • 输入是静态 HTML、格式错误的 HTML、严格 XML,还是渲染后的浏览器 DOM?
  • 文档能否完全加载到内存中,还是需要流式处理记录?
  • 是否需要 CSS、XPath、命名空间、验证或 XSLT?
  • 工作负载中是否包含对象序列化?
  • 必须支持哪些原生库、编译器、浏览器或驱动程序?
  • 是否已针对目标 Ruby 版本和操作系统对该 gem 进行了兼容性验证?
  • 该工具是否属于应置于适配器后方的遗留依赖项?
  • 已保存的测试数据是否涵盖了编码、格式错误的输入、缺失的选择器以及版本升级?

最好的 Ruby XML 解析器或 HTML 解析器,就是能在满足这些约束条件的同时,拥有最小且可靠的运行时大小的那个。

关键要点

  • 在选择 gem 之前先对工作负载进行分类:静态 HTML、严格 XML、流式 XML、对象序列化和渲染后的页面需要不同的工具。
  • 将数据检索、身份验证、渲染和解析作为独立的管道阶段,以便将故障归因于正确的层级。
  • 使用您实际运行的 Ruby 版本和部署镜像,验证当前版本、原生依赖项以及平台支持情况。
  • 在将任何解析器视为生产就绪之前,请对具有代表性的测试数据、测试选择器、缺失节点、编码以及格式错误的输入进行基准测试。

常见问题

Nokogiri 能否解析 JavaScript 在页面加载后添加的内容?

不能。Nokogiri 仅解析其接收到的 HTML 或 XML 字符串,不会执行页面脚本。 当 JavaScript 创建目标元素时,使用浏览器工具检索渲染后的 DOM,然后直接查询该 DOM 或将其页面源代码传递给 Nokogiri。如果某个端点返回了与 JSON 或 HTML 相同的数据,使用该响应通常比通过浏览器控制更简单。

Ruby 代码在何种情况下应使用流式解析器或 SAX 解析器,而不是构建 DOM 树?

当文档过大导致构建树结构不方便,或者每个记录可以按顺序处理一次时,应使用流式或 SAX 解析器。这对于以增量方式输出结果的源数据、导出和转换非常有效。当提取需要任意导航、重复查询或修改文档中相距较远的部分时,应优先使用 DOM。

Hpricot 和 Oga 适合用于新的 Ruby 项目吗?

应将二者视为需根据具体情况权衡的选择,而非自动默认选项。采用前,请验证最新版本、对您所用 Ruby 版本的支持情况、在每个部署目标上的构建成功率,以及问题响应是否令人满意。 一个小型兼容性测试应能解析您的实际测试数据,并验证命名空间、格式错误的输入以及选择器的处理情况。即使新构建会做出不同的选择,现有代码也可能需要通过适配器继续保留。

公平的 Ruby 解析器性能比较应以什么为衡量标准?

公平的比较首先需要输出和失败行为完全一致,因为如果解析器构建的树结构不同,那么更快的恢复速度就毫无意义。 请针对小文档、大文档、命名空间、格式错误的输入、流式处理和序列化分别设置独立的测试数据组。报告运行结果的分布情况而非单一最佳结果,纳入垃圾回收的影响,并公开测试框架以便其他工程师能够复现测试。

结论

一旦明确了工作负载,在 Ruby HTML 和 XML 解析器之间进行选择就会变得简单得多。对于 HTML 和基于混合树的工作,Nokogiri 是实用的通用起点。 Ox 值得在 XML 序列化和事件处理方面进行评估,而 LibXML Ruby 则适用于需要 XPath、验证或转换功能的 XML 管道。Oga 和 Hpricot 需要更严格的维护和兼容性检查,而 Selenium 仅适用于真正需要渲染或交互的场景。

无论您选择哪种解析器,都应将其封装在狭窄的接口之后。保留原始测试数据、测试选择器及缺失节点行为,记录编码假设,并锁定影响原生扩展的运行时细节。请使用您自己的文档进行基准测试,而非直接采用他人的速度排名。

数据提取最终可能比解析更困难。如果阻塞请求、验证码或代理轮换正在消耗工程预算,WebScrapingAPI 提供的 Scraper API 可以在处理这些请求层工作时返回原始 HTML,从而让您的 Ruby 代码专注于一个小而可测试的提取阶段。 这才是最清晰的职责边界:检索产生可信的标记,而选定的解析器将其转换为经过验证的数据。

关于作者

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

Raluca Penciuc

全栈开发工程师

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

开始构建

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

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