在 Ruby 生态中,Nokogiri 是解析 HTML 和 XML 的首选方案。它会把一份文档转换成可遍历的节点树,每一个标签、属性、文本片段都变成对应的节点对象。这样一来,抓取网页数据时就不用再拼接正则表达式和字符串截取,解析 XML 接口时也能依靠标准化的查询语法。Nokogiri 底层依赖 libxml2 和 libxslt,处理速度较快,同时提供了 Ruby 风格的方法调用。下面从安装开始,分几个场景说明它的核心用法。

安装与基础解析方法
安装 Nokogiri 最直接的方式是使用 gem 命令,也可以在 Gemfile 里声明依赖。不同操作系统可能需要编译原生扩展,安装前通常需要确保系统已有 libxml2 和 libxslt 开发包。对于 Debian 或 Ubuntu 系统,可以先安装 libxml2-dev 和 libxslt1-dev,再执行 gem 安装。Windows 平台一般使用预编译包,RubyInstaller 会处理大部分依赖问题。
gem install nokogiri bundle add nokogiri
解析 HTML 使用 Nokogiri::HTML,解析 XML 使用 Nokogiri::XML。两个方法都接受字符串、File 对象或 IO 对象。HTML 模式会自动补全缺失的 <html>、<body> 等结构,所以只解析一段片段也能得到完整文档树。XML 模式则更严格,遇到未闭合标签会直接抛出错误,适合用来校验接口返回的格式是否合法。
require 'nokogiri'
html = '<div><p>Hello</p></div>'
doc = Nokogiri::HTML(html)
puts doc.at_css('p').text
xml = '<root><item id="1">数据</item></root>'
xml_doc = Nokogiri::XML(xml)
puts xml_doc.root.name
puts xml_doc.at_xpath('//item')['id']
从磁盘读取时,直接传 File 对象或字符串都可以。遇到中文页面建议先通过响应的 charset 设置编码,或者用 Nokogiri::HTML(html, nil, 'UTF-8') 指定编码,避免乱码。对于片段解析,使用 Nokogiri::HTML.fragment 可以避免自动包裹完整文档结构,这对把解析结果插入到其他文档时特别有用。
CSS选择器与XPath定位
Nokogiri 同时支持 CSS 选择器和 XPath,两者可以混合使用,也可以在同一节点上连续调用。css 方法返回一个 NodeSet,类似数组,可以遍历;at_css 只返回第一个匹配项。获取属性可以用 [] 或 attribute,获取文本用 text 或 content。CSS 写法直观,适合抓取结构清晰的网页。
html = '<div class="content"><p>第一段</p><p>第二段</p></div>'
doc = Nokogiri::HTML(html)
doc.css('div.content p').each do |p|
puts p.text
end
first_p = doc.at_css('div.content p')
puts first_p['class'].inspect
XPath 更擅长处理复杂条件,例如按文本内容匹配、选择包含某个属性的节点、按照位置选择子节点。处理 XML 时如果节点带命名空间,直接写 //ex:item 会找不到,需要把前缀和命名空间 URI 组成哈希传给 at_xpath 或 xpath。这种场景在解析 RSS、SOAP 响应时非常常见。
xml = '<root xmlns:ex="http://bbccb.com"><ex:item>数据</ex:item></root>'
doc = Nokogiri::XML(xml)
ns = { 'ex' => 'http://bbccb.com' }
item = doc.at_xpath('//ex:item', ns)
puts item.text
在大型文档中,频繁调用 css 或 xpath 会反复遍历整棵树。建议先在父节点上搜索一次,把结果保存为 NodeSet,再在需要时调用 NodeSet#at_css 等相对查询方法。比如已经拿到某个卡片节点,再用 card.at_css('.title'),范围会比从 doc 全局搜索小很多。
遍历、修改和序列化节点树
节点树可以沿着父子关系移动。常用方法包括 children、parent、next_element、previous_element 和 ancestors。要注意 children 返回的不仅仅是元素节点,还可能包括文本节点和注释节点,如果只想处理标签,可以使用 element_children。下面例子演示如何遍历一个列表项并过滤掉换行以外的文本节点。
html = '<ul id="list"><li>苹果</li><li>香蕉</li></ul>'
doc = Nokogiri::HTML(html)
doc.at_css('#list').element_children.each do |li|
puts li.text
end
修改节点内容非常直接。给 content= 赋值会替换节点内部的全部文本,给属性赋值可以写 node['class'] = 'new-class'。删除节点调用 remove,如果需要保留子节点,可以先解绑再删除。创建新节点时,推荐使用 Nokogiri::XML::Node.new 并传入当前文档对象,这样新节点会继承文档的编码和命名空间设置。添加节点可以用 add_child、before 和 after。
html = '<div><h1>原始标题</h1></div>'
doc = Nokogiri::HTML(html)
heading = doc.at_css('h1')
heading.content = '新标题'
heading['class'] = 'main-title'
doc.css('script, style').each(&:remove)
new_p = Nokogiri::XML::Node.new('p', doc)
new_p.content = '这是新增的段落'
doc.at_css('div').add_child(new_p)
puts doc.to_html
最后可以通过 to_html 或 to_xml 把修改后的文档序列化回去。HTML 模式输出的内容会比较紧凑,如果需要格式化,可以使用 to_html(indent: 2) 之类的参数,但需注意 Nokogiri 版本对缩进参数的支持差异。XML 输出前还要确认声明和编码信息是否保留,通常 to_xml 会生成 <?xml version="1.0"?> 声明。
HTML与XML解析模式的差异
HTML 模式会尽量修复错误,因此即使你只传入一段 <p> 文本,Nokogiri 也会自动补全 <html> 和 <body>。这个特性对处理真实网页非常有用,因为很多网页的标签嵌套并不规范。但对于 XML,这种自动修复并不存在,任何不匹配或未转义的实体都会导致解析异常,这正好可以用来发现上游接口产出的非法内容。
# HTML 容错示例
html = '<p>这是段落<div>嵌套内容'
doc = Nokogiri::HTML(html)
puts doc.at_css('div').text rescue nil
# XML 严格解析
begin
xml = '<root><item>未闭合'
Nokogiri::XML(xml)
rescue Nokogiri::XML::SyntaxError => e
puts e.message
end
HTML 解析器对标签和属性名的大小写不太敏感,通常会统一转换为小写。XML 则严格保留大小写,因此 <Item> 和 <item> 是两个完全不同的元素。命名空间在 XML 中也必须提前声明,否则 XPath 查询无法命中。处理第三方 XML 时,建议先打印 doc.root.namespaces 查看实际命名空间映射,再构造 XPath 的哈希参数,避免拿到空结果后反复调试查询表达式。
此外,如果一个 XML 文档很大,比如包含几万条记录,直接加载为完整节点树会占用大量内存。Nokogiri 提供了 Nokogiri::XML::Reader 用于流式读取,使用游标逐条访问节点,适合做增量处理。也可以使用 SAX 解析器,在事件回调中处理特定标签,但代码会更复杂一些。
网络抓取实战与内存优化
在爬虫场景中,通常先通过 open-uri 或 HTTP 客户端获取页面内容,再把响应体交给 Nokogiri。Ruby 3 之后的 open-uri 推荐使用 URI.open。抓取结束后,最好先移除 <script> 和 <style> 节点,这样可以避免脚本内容干扰后续文本提取。下面是一个抓取标题列表的简单示例。
require 'open-uri'
require 'nokogiri'
url = 'https://bbccb.com/articles'
html = URI.open(url).read
doc = Nokogiri::HTML(html)
doc.css('h2.title').each do |h|
puts h.text.strip
end
真实网页经常存在编码声明不准确的问题。可以先用响应头中的 charset 设置字符串的 encoding,再调用 Nokogiri::HTML。如果是压缩内容,需要先解压再解析。清洗数据时,不要直接对整个文档执行 to_text,而应该先定位到需要的主体容器,再提取段落,这样能显著减少导航、广告等无关文本的干扰。
对于需要定期同步的 XML 源,比如商品库存或 RSS,建议使用流式解析而不是一次性加载全部内容。先根据业务需求只保存需要的字段,不保留完整节点树。如果必须加载整棵树,也应该在处理完成后显式释放引用,让垃圾回收器尽快回收内存。Nokogiri 的原生扩展对象在 Ruby 侧被回收后,底层 C 结构也会相应释放,但短时间内解析大量文档时,手动置空对象或分批处理仍然是更稳妥的做法。
Ruby NokogiriHTML解析XML解析修改时间:2026-09-19 21:43:22