NodeJS 中寻找可用的 HTMLParser

NodeJS 中寻找可用的 HTMLParser

NodeJS 中寻找可用的 HTMLParser

原文地址:http://huangx.in/2012/5/nodejs_for_available_htmlparser.html

简介

项目中需要实现对一个HTML页面解析的情况,开发环境是MacOSX lion,NodeJS v0.6.14。
本次实践,分别对**libxmljs, libxmljs-easy, jsdom, node-htmlparser, node-jqueryApricot** 五类框架进行测试,最终采用node-jquery实现了对不规范的HTML文档的兼容,能够正常解析。

试错

最初延续Python中的经验,找libxml相关的类库,因为libxml支持XPath,可以很方便的解析HTML。
node modules wiki页面中,找到了有两款,libxml和libxml-easy,后者是对前者API的一个再封装。使用npm安装后,发现无法解析页面,报错Error: Start tag expected, '<' not found,发现页面不是标准的HTML。
于是尝试jsdom,采用Javascript原生实现的W3C DOM操作类库。使用时发现他对抓取页面的逻辑封装还是挺到位的,能够直接通过网址抓取,不过使用中还是报错 SyntaxError: Unexpected token ILLEGAL。仍然无法解析。
再找到一个对HTML格式很"宽容"的解析器html-parser,编译一番API调用后,发现能够解析,虽然API很不友好,都是围绕DOM的属性进行操作,没有提供CSS selector,如果要检索页面内容会很麻烦,于是找到基于html-parser和jsdom的封装Apricout,部署一会儿部署不上,要求x86系统以及macos系统,感觉是作者粗心,将源码clone下来后,发现版本较老,一些系统类库都改名了,于是手工修改,改动如下:

diff --git a/lib/apricot.js b/lib/apricot.js
index 9e0251c..5cc3546 100644
--- a/lib/apricot.js
+++ b/lib/apricot.js
@@ -4,7 +4,7 @@ var http = require('http'),
     url = require('url');

exports.Apricot = Apricot; -sys = require(‘sys’); +sys = require(‘util’);

function Apricot(content,live) {

diff --git a/package.json b/package.json index 18cbdbd…fd8790b 100644 — a/package.json +++ b/package.json @@ -31,16 +31,18 @@ } ], “dependencies”: {

  •   "jsdom": "= 0.1.2",
    
  •   "htmlparser": "= 1.6.2"
    
  •   "jsdom": "&gt;= 0.1.2",
    
  •   "htmlparser": "&gt;= 1.6.2"
    
    }, “os”: [ “linux”, “macos”,
  •    "darwin",
       "win"
    
    ], “cpu”: [ “x86”,
  •    "x64",
       "ppc",
       "x86_64"
    
    ],

安装成功后,测试的时候发现报错innerHTML无法写入。查看源码后发现是Apricout调用sizzle构造DOM时,报错,没有正常返回document,所以找不到innherHTML,于是再去找其他类库。

最终使用node-jquery,简单的例子一下就返回了如预期的结果(CoffeeScript):

dom = jquery(body)
for i in dom.find(".targetCssName")
  console.log i.innerHTML

思考

数据挖掘中,一般有一个数据格式化的过程,这个步骤就包含了上述的内容,针对各个平台可能使用的方法有所差异,不过大体都是对互联网上未经处理的HTML内容,进行格式化处理。而处理的方式又有多种,下面列举一些常见的HTML格式化信息方法,以及他们的优缺点:

  • 进行前后边界查找后,定位内容区域,进行字符串的检索。优点:速度快,缺点:面对复杂的HTML可能需要很繁重的边界定位工作,我是指完成编码时的人工定位。

  • 正则表达式,属于上面的加强版,不过采用了更容易描述的正则表达式,不过也有人持不同的观点。优点:速度不慢,缺点:构造表达式很耗时。

  • 构造DOM然后采用CSS selector或者XPath进行检索。优点:检索方便,能够避免DOM顺序调整之类的页面变动,缺点:速度慢,开销大,需要构造DOM

    对于发展中的nodeJS生态圈表示仍然很乐观,因为本文也从一个侧面反映了基于Javascript的开发环境,基础框架层出不穷,很容易找到契合自己业务相关的实现,而且有一个活跃的社区,从事相关开发是一个不错的选择。

参考


10 回复

NodeJS 中寻找可用的 HTMLParser

简介

在项目中需要解析HTML页面,开发环境为MacOSX Lion,NodeJS版本为v0.6.14。本次实践中,我测试了以下几种HTML解析库:

  • libxmljs: 使用libxml的JavaScript绑定。
  • libxmljs-easy: libxmljs的简化版本。
  • jsdom: 基于JavaScript的W3C DOM操作库。
  • node-htmlparser: 一个对HTML格式非常宽容的解析器。
  • node-jquery: jQuery的NodeJS版本。
  • Apricot: 基于html-parser和jsdom的封装。

最终,选择node-jquery来解析非标准的HTML文档,并且能够正常解析。

试错

最初尝试使用libxml相关的库,因为libxml支持XPath,便于解析HTML。但发现这些库无法解析非标准的HTML,报错Error: Start tag expected, '<' not found

接下来尝试了jsdom,它能够通过网址抓取页面,但仍然无法解析,报错SyntaxError: Unexpected token ILLEGAL

然后尝试了node-htmlparser,虽然能够解析非标准的HTML,但其API不够友好,没有提供CSS选择器,使用起来不方便。因此,又尝试了Apricot,但在部署时遇到了一些问题,最终未能成功。

最后,选择了node-jquery,因为它能够很好地解析非标准的HTML文档,并且使用简单。以下是一个简单的示例代码:

const $ = require('jquery');
const html = `
<html>
  <body>
    <div class="targetCssName">Hello World</div>
  </body>
</html>
`;

// 解析HTML
const dom = $(html);

// 查找所有目标CSS名称的元素并打印内容
dom('.targetCssName').each((i, elem) => {
  console.log($(elem).text());
});

思考

在数据挖掘中,通常需要对HTML内容进行格式化处理。这可以通过以下几种方式实现:

  1. 前后边界查找

    • 优点:速度快。
    • 缺点:对于复杂的HTML结构需要大量的边界定位工作。
  2. 正则表达式

    • 优点:速度较快,表达式易于编写。
    • 缺点:构造表达式耗时。
  3. 构造DOM并使用CSS选择器或XPath

    • 优点:检索方便,能避免DOM顺序调整等问题。
    • 缺点:速度较慢,开销较大。

对于NodeJS生态圈来说,有许多基础框架不断涌现,很容易找到适合自身需求的实现。活跃的社区也使得NodeJS成为一个很好的开发选择。

参考

希望以上内容对你有所帮助!


我的小经验是: 1、先用正则粗略匹配出有用的html片段,因为对于抓取的页面,为很多没用的信息付出构造DOM的 开销很不值得~
2、把粗略HTML片段parse为DOM对象来进一步细提取有用信息~~

我是使用phantomjs作为基础采集的,因为目前采集的站点集中在一个上面,所以效率上到还不是问题。打算用下老兄介绍的方法试验下,多谢

您的思路非常的好,也比较实用。

可以查查 UC web 和 Opera 是怎么做的,他们的页面压缩都需要想进行页面分析,而且效率要非常的高才行。

就是需要这种类型的文章,NodeClub很少有这么好的文章。 赞!

不明白 你所指UC web 或者Opera 的网页浏览提速,不是他们自己提供的服务器代理吗?和数据抓取的关系是? 按照我的理解,他们无法解析的东西,比如插入的flash都是直接过滤掉的,也就不去渲染,不去请求,自然浏览速度提升了。 这是他渲染时干的事情,解析仍然会碰到我的问题,而且这种问题极其平台相关,放到Java,Csharp,Python,Ruby可能不会是问题,但在NodeJS这方面可能类库仍然还是很欠缺。

好东西,正寻找一款html parser,楼主提供了很好的建议,不用再试啦

在Node.js环境中,解析HTML是一项常见任务。以下是几种常用的HTML解析库及其使用示例。

libxmljs

libxmljs 是一个基于libxml2的XML和HTML解析器。它支持XPath查询,但可能对非标准HTML不那么友好。

npm install libxmljs
const libxml = require('libxmljs');

const html = '<html><body><div class="example">Hello World!</div></body></html>';
const xmlDoc = libxml.parseHtml(html);
const divs = xmlDoc.find('//div[@class="example"]');
console.log(divs[0].text()); // 输出 "Hello World!"

jsdom

jsdom 是一个纯JavaScript的浏览器DOM实现,可以让您在Node.js中使用jQuery或类似的库。

npm install jsdom
const { JSDOM } = require("jsdom");

const html = '<html><body><div class="example">Hello World!</div></body></html>';
const dom = new JSDOM(html);
const document = dom.window.document;

const divs = document.querySelectorAll('.example');
console.log(divs[0].textContent); // 输出 "Hello World!"

node-htmlparser

node-htmlparser 是一个比较旧的HTML解析器,但仍然有用。

npm install htmlparser
const HtmlParser = require('htmlparser').HtmlParser;

const html = '<html><body><div class="example">Hello World!</div></body></html>';
const handler = new HtmlParser.DefaultHandler((error, dom) => {
  if (error) throw error;
  const divs = dom.filter(node => node.type === 'tag' && node.name === 'div' && node.attribs.class === 'example');
  console.log(divs[0].children[0]); // 输出 "Hello World!"
});
const parser = new HtmlParser(handler);
parser.parseComplete(html);

node-jquery

node-jquery 是一个非常友好的库,允许你在Node.js中使用jQuery。

npm install node-jquery
const $ = require('jquery');
const cheerio = require('cheerio');

const html = '<html><body><div class="example">Hello World!</div></body></html>';
const $html = cheerio.load(html);

$html('.example').each((i, elem) => {
  console.log($(elem).text()); // 输出 "Hello World!"
});

总结

每种库都有其特点,选择哪个取决于您的具体需求。如果您需要强大的XPath查询功能,libxmljs 是一个好选择。如果您习惯于使用jQuery语法,node-jqueryjsdom 更合适。对于更简单的场景,node-htmlparser 可能就足够了。

回到顶部