如何使用 Cheerio 从字符串中加载和操作 HTML 片段?

How to load and manipulate an HTML fragment from a string with Cheerio?

提问人:gordie 提问时间:5/13/2023 最后编辑:ggorlengordie 更新时间:5/13/2023 访问量:210

问:

我正在努力尝试使用 Cheerio 加载 HTML 片段。

var htmlString = '<div class="artist"><i class="user blue circle icon"></i> Skyy</div>';
var $ = cheerio.load(data);
console.info($.html());

输出

<html><head></head><body><div class="artist"><i class="user blue circle icon"></i> Skyy</div></body></html>

我认为,我的问题是 Cheerio 将我的内容包装在 HTML 文档中,这使得直接访问节点变得困难。

我最终可以使用这个选择器,它工作得很好:

var el = $('body').children().first();

但它并不总是有效。例如

var htmlString = '<meta name="description" content="My description">';
var $ = cheerio.load(data);
console.info($.html());

将输出不同类型的文档,其中不起作用:var el = $('body').children().first();

<html><head><meta name="description" content="My description"></head><body></body></html>

那么,有没有办法在不使用选择器的情况下加载 HTML 片段并将其作为 Cheerio 元素访问?

我希望能够在填充的节点上使用 Cheerio 函数,例如 或 。.text().html().attr()

JavaScript 解析 Dom Cheerio

评论

1赞 code 5/13/2023
那么你想要的输出到底是什么?
0赞 gordie 5/13/2023
节点文本或属性值。

答:

1赞 code 5/13/2023 #1

cheerio 中有一个选项可以禁止将你的 html 包装在其他标签中,第三个参数(第二个参数包含一个包含其他选项的对象;我们可以将其设置为 null):cheerio.load

const $ = cheerio.load(htmlString, null, false)
console.log($.html()) // <-- just the html, not wrapped

您可以查看以获取更多信息。

评论

0赞 gordie 5/13/2023
使用最后一个 Cheerio 版本 (^1.0.0-rc.12),它给了我.TS2554: Expected 1-2 arguments, but got 3.
0赞 code 5/13/2023
@gordie我刚刚尝试过,它有效。你确定没有其他干扰吗?
0赞 gordie 5/13/2023
你在用 TypeScript 吗?
0赞 code 5/13/2023
@gordie没有;我想这是 TS 的问题。你用的是哪个版本的?@types/cheerio
0赞 gordie 5/13/2023
类型为 ^0.22.31
0赞 gordie 5/13/2023 #2

我找到了一个解决方案。

通过加载一个空白文档,我可以手动将我的 html 字符串添加到其中 - 所以我确定它会在 中,即使它是 Cheerio 通常会加载到 .<body/><head/>

var $ = cheerio.load('');
$('body').append(htmlString);
var el = $('body').children().first();
0赞 ggorlen 5/13/2023 #3

这个答案提供了一个很好的开端,但它没有显示访问标签的一致方式。

$(":root").first();似乎是提取第一个标签的好方法,未经广泛测试,但在抽查中看起来很有希望。

const cheerio = require("cheerio"); // 1.0.0-rc.12
const {strict: assert} = require("node:assert");

const loadOneTag = html => {
  const $ = cheerio.load(html, null, /*isDocument=*/false);
  assert.equal($.html(), html);
  return $(":root").first();
};

{
  // one top-level child
  const html = `
    <div class="artist"><i class="user blue circle icon"></i> Skyy</div>
  `;
  assert.equal(loadOneTag(html).attr("class"), "artist");
}

{
  // multiple top-level children
  const html = `
    <div class="artist"><i class="user blue circle icon"></i> Skyy</div>
    <p>asdf</p>
  `;
  assert.equal(loadOneTag(html).attr("class"), "artist");
}

{
  // meta tag
  const html = `
    <meta name="description" content="My description">
  `;
  assert.equal(loadOneTag(html).attr("name"), "description");
}

评论

0赞 gordie 5/13/2023
似乎有效!除了,根据 TypeScript 的说法,的第二个参数应该是 而不是 。cheerio.loadundefinednull
0赞 ggorlen 5/13/2023
谢谢,如果您愿意,请随时将其插入,但源代码似乎显示 null,这是我见过的大多数 API 中的标准(“跳过此参数”)。他们应该放入选项参数中。否则,它并不是真正的可读性。这可能是一个向后兼容的问题。但我跑题了......isDocument
0赞 gordie 5/14/2023
Typescript 触发带有 null 的错误,他们希望它未定义(或已定义)。但也许他们的类型不好。