在 Node.js 中解析没有 ID 或 CSS 选择器的 HTML 表格

Posted

技术标签:

【中文标题】在 Node.js 中解析没有 ID 或 CSS 选择器的 HTML 表格【英文标题】:Parse HTML table without IDs or CSS selectors in Node.js 【发布时间】:2014-04-29 11:08:45 【问题描述】:

此数据来自旧系统,输出保持原样。我们不能添加 CSS 选择器或 ID。大多数用于 node.js 解析的在线示例都涉及使用一些 ID 或 CSS 类解析表、行、数据,但到目前为止,我还没有遇到任何可以帮助解析以下页面的内容。这包括 JSDOM (AFAIK) 的示例。

我想要将每一行提取到 [fileName, link, size, dateTime] 元组中,然后我可以在这些元组上运行一些查询,例如组中的最新时间戳是什么等,然后提取文件名和链接 - 正在考虑使用 YQL。交替表行属性也使它有点挑战。 node.js 的新手,因此某些术语可能是错误的。任何帮助将不胜感激。

谢谢。

<html>
<body>
    <table  cellspacing="0" cellpadding="5" align="center">
        <tr> 
        <td align="left"><font size="+1"><strong>Filename</strong></font></td>
        <td align="center"><font size="+1"><strong>Size</strong></font></td>
        <td align="right"><font size="+1"><strong>Last Modified</strong></font></td>
        </tr>
        <tr>
        <td align="left">&nbsp;&nbsp;
        <a href="/path_to_file.csv"><tt>file1.csv</tt></a></td>
        <td align="right"><tt>86.6 kb</tt></td>
        <td align="right"><tt>Fri, 21 Mar 2014 21:00:19 GMT</tt></td>
        </tr>
        <tr bgcolor="#eeeeee">
        <td align="left">&nbsp;&nbsp;
        <a href="/path_to_file.csv"><tt>file2.csv</tt></a></td>
        <td align="right"><tt>20.7 kb</tt></td>
        <td align="right"><tt>Fri, 21 Mar 2014 21:00:19 GMT</tt></td>
        </tr>
        <tr>
        <td align="left">&nbsp;&nbsp;
        <a href="/path_to_file.xml"><tt>file1.xml</tt></a></td>
        <td align="right"><tt>266.5 kb</tt></td>
        <td align="right"><tt>Fri, 21 Mar 2014 21:00:19 GMT</tt></td>
        </tr>
        <tr bgcolor="#eeeeee">
        <td align="left">&nbsp;&nbsp;
        <a href="/path_to_file.xml"><tt>file2.xml</tt></a></td>
        <td align="right"><tt>27.2 kb</tt></td>
        <td align="right"><tt>Fri, 21 Mar 2014 21:00:19 GMT</tt></td>
        </tr>
    </table>
</body>
</html>

回答(感谢@Enragedmrt):

    res.on('data', function(data) 

        $ = cheerio.load(data.toString());
        var data = [];
        $('tr').each(function(i, tr)

            var children = $(this).children();
            var fileItem = children.eq(0);
            var linkItem = children.eq(0).children().eq(0);
            var lastModifiedItem = children.eq(2);

            var row = 
                "Filename": fileItem.text().trim(),
                "Link": linkItem.attr("href"),
                "LastModified": lastModifiedItem.text().trim()
            ;
            data.push(row);
            console.log(row);
        );
    );

【问题讨论】:

【参考方案1】:

我建议在 JSDOM 上使用 Cheerio,因为它明显更快、更轻量级。也就是说,您需要为每个循环执行一个获取“tr”元素以及随后的“td”元素的循环。这是一个粗略的例子(我的 Node.js/Cheerio 生锈了,但是如果你在 JQuery 中挖掘,你可以找到一些不错的例子):

var data = [];
$('tr').each(function(i, tr)
    var children = $(this).children();
    var row = 
        "Filename": children[0].text(),
        "Size": children[1].text(),
        "Last Modified": children[2].text()
    ;
    data.push(row);
);

【讨论】:

这很完美——唯一的改变是我必须使用 .eq(N) 来获得第 N 个孩子。 [] 符号似乎不适用于cheerio。是的 - 在解析真实数据集时确实比 jsdom 快得多。谢谢 Enragedmrt!【参考方案2】:

我不知道JSDom,但听起来它可以将HTML文档解析为DOM(文档对象模型)。从那里应该很有可能遍历节点并通过标签名称、属性或文档中的位置来识别它们,即使它们没有 ID。

谷歌搜索 5 秒,请稍等...

JSDom's documentation on GitHub 似乎证实了这一点。它显示了类似 jQuery 的选择器,例如 window.$("a.the-link").text()。因此,您可以选择tdth 甚至td[align="left"] 等选择器,而不是添加一个类。使用这样的选择器,以及.first.each 等方便的方法来遍历多个结果(如每一行),您应该能够很好地解析文档,尽管它当然会比使用更麻烦一些为每种不同类型的单元格提供方便的类名。

我仍然不认为我是 JSDom 专家,但花几分钟阅读他们项目的主页已经可以找到所有问题的答案,甚至更多。

【讨论】:

【参考方案3】:

JSFiddle

var rawData = new Array();
var rows = document.getElementsByTagName('tr');
for(var cnt = 1; cnt < rows.length; cnt++) 
    var cells = rows[cnt].getElementsByTagName('tt');
    var row = [];
    for (var count = 0; count < cells.length; count++) 
        row.push(cells[count].innerText.trim());
        
    rawData.push(row);


console.log(rawData);

【讨论】:

【参考方案4】:

其他方式

var cheerio = require('cheerio'),
    cheerioTableparser = require('cheerio-tableparser');

res.on('data', function(data) 

    $ = cheerio.load(data.toString());
    cheerioTableparser($);
    var data = [];
    var array = $("table").parsetable(false, false, false)
    array[0].forEach(function(d, i) 

        var firstColumnHTMLCell = $("<div>" + array[0][i] + "</div>");
        var fileItem = firstColumnHTMLCell.text().trim();
        var linkItem = firstColumnHTMLCell.find("a").attr("href");
        var lastModifiedItem = $("<div>" + array[2][i] + "</div>").text();

        var row = 
            "Filename": fileItem,
            "Link": linkItem,
            "LastModified": lastModifiedItem
        ;

        data.push(row);
        console.log(row);
    )
);

【讨论】:

以上是关于在 Node.js 中解析没有 ID 或 CSS 选择器的 HTML 表格的主要内容,如果未能解决你的问题,请参考以下文章

如何将 PHP 用户详细信息(会话)解析到 Node.js 服务器?

Node.js使用supervisor遭遇‘supervisor’不是内部或外部命令,如果解决?

node-1

在 Node.JS 中解析 Microsoft Office 文件

在 Node.js 中包含 CSS 和 JS 文件

node中的url常用方法解析