使用Node.js和XPath对页面进行高效解析

pol*_*.ph 29 html javascript xpath node.js phantomjs

我正在使用Node.js进行一些网络抓取.我想使用XPath,因为我可以使用几种GUI半自动生成它.问题是我找不到有效地做到这一点的方法.

  1. jsdom非常慢.它在一分钟左右的时间内解析了500KiB文件,并且CPU负载全部,内存占用空间很大.
  2. 用于HTML解析的流行库(例如cheerio)既不支持XPath,也不暴露符合W3C的DOM.
  3. 显然,有效的HTML解析在WebKit中实现,因此使用phantomcasper将是一个选项,但那些需要以特殊方式运行,而不仅仅是node <script>.我不能依赖这种变化所暗示的风险.例如,找到如何运行node-inspector起来要困难得多phantom.
  4. Spooky是一个选项,但它足够多,所以它在我的机器上根本没有运行.

那么用XPath解析HTML页面的正确方法是什么?

小智 35

您可以通过几个步骤完成此操作.

  1. 用HTML解析HTML parse5.坏的部分是结果不是DOM.虽然它足够快和W3C兼容.
  2. 将其序列化为XHTML,xmlserializer并接受类似DOM的parse5输入结构.
  3. 再次解析XHTML xmldom.现在你终于拥有了那个DOM.
  4. xpath库基于xmldom,允许您运行XPath查询.请注意,XHTML有自己的命名空间,而查询//a不起作用.

最后你得到这样的东西.

const fs = require('mz/fs');
const xpath = require('xpath');
const parse5 = require('parse5');
const xmlser = require('xmlserializer');
const dom = require('xmldom').DOMParser;

(async () => {
    const html = await fs.readFile('./test.htm');
    const document = parse5.parse(html.toString());
    const xhtml = xmlser.serializeToString(document);
    const doc = new dom().parseFromString(xhtml);
    const select = xpath.useNamespaces({"x": "http://www.w3.org/1999/xhtml"});
    const nodes = select("//x:a/@href", doc);
    console.log(nodes);
})();
Run Code Online (Sandbox Code Playgroud)


mb2*_*b21 14

Libxmljs目前是最快的实现(类似于基准测试),因为它只绑定到支持XPath 1.0查询的LibXML C库:

var libxmljs = require("libxmljs");
var xmlDoc = libxmljs.parseXml(xml);
// xpath queries
var gchild = xmlDoc.get('//grandchild');
Run Code Online (Sandbox Code Playgroud)

但是,您需要首先清理HTML并将其转换为正确的XML.为此你可以使用HTMLTidy命令行实用程序(tidy -q -asxml input.html),或者如果你想让它保持仅节点,像xmlserializer这样的东西就可以了.