【问题标题】:Cheerio - Get text with html tags replaced by white spacesCheerio - 获取带有替换为空格的 html 标签的文本
【发布时间】:2019-11-19 21:24:13
【问题描述】:

今天我们使用Cheerio's,尤其是方法 .text() 从 html 输入中提取文本。

但是当html是

<div>
  By<div><h2 class="authorh2">John Smith</h2></div>
</div>

在页面上,“by”一词后面的 /div 可确保有空格或换行符。 但是当应用cheerio text() 时,我们得到的结果是错误的:

ByJohn smith => 这是错误的,因为我们需要在 By 和 john 之间留一个空格。

一般来说,是否有可能以一种特殊的方式获取文本,以便将任何 html 标记替换为空白。 (我可以在之后修剪所有多个空格...)

我们希望得到 John smith 的输出

【问题讨论】:

  • 可能与问题无关,但您的 html 示例无效,因为包含 John Smith 的 div 都是结束标签。
  • 肯定与真正的问题无关。谢谢,纠正错误
  • 在我看来,您只是没有应用正确的选择器。取你已经用过的,加上`h2`,分别获取header的内容。
  • @Mathieu 你一定要用cheerio吗?

标签: javascript cheerio


【解决方案1】:

您可以使用以下正则表达式将所有 HTML 标记替换为空格:

/<\/?[a-zA-Z0-9=" ]*>/g

所以当你用这个正则表达式替换你的 HTML 时,它可能会产生多个空格。在这种情况下,您可以使用replace(/\s\s+/g, ' ') 将所有空格替换为单个空格。

查看结果:

console.log(document.querySelector('div').innerHTML.replaceAll(/&lt;\/?[a-zA-Z0-9=" ]*&gt;/g, ' ').replace(/\s\s+/g, ' ').trim())
<div>
  By<div><h2 class="authorh2">John Smith</h2></div>
</div>

【讨论】:

    【解决方案2】:

    您可以使用纯 JavaScript 来完成此任务。

    const parent = document.querySelector('div');
    console.log(parent.innerText.replace(/(\r\n|\n|\r)/gm, " "))
    <div>
      By<div><h2 class="authorh2">John Smith</h2></div>
    </div>

    【讨论】:

      【解决方案3】:

      一般来说,是否有可能以一种特殊的方式获取文本,以便将任何 html 标记替换为空白。 (我可以在之后修剪所有多个空格...)

      只需在所有标签前后添加' '即可:

      $("*").each(function (index) {
          $(this).prepend(' ');
          $(this).append(' ');
      });
      

      然后处理多个空格:

      $.text().replace(/\s{2,}/g, ' ').trim();
      //=> "By John Smith"
      

      由于 cheerio 只是 NodeJS 的 jQuery 实现,您可能会发现 these answers 也很有用。


      工作示例:

      const cheerio = require('cheerio');
      const $ = cheerio.load(`
          <div>
              By<div><h2 class="authorh2">John Smith</h2></div>
          </div>
      `);
      
      $("*").each(function (index) {
          $(this).prepend(' ');
          $(this).append(' ');
      });
      
      let raw = $.text();
      //=> "        By  John Smith" (duplicate spaces)
      
      let trimmed = raw.replace(/\s{2,}/g, ' ').trim();
      //=> "By John Smith"
      

      【讨论】:

        【解决方案4】:

        您可以使用htmlparser2,而不是cheerio。它允许您在解析 HTML 时每次遇到开始标记、文本或结束标记时定义回调方法。

        此代码产生您想要的输出字符串:

        const htmlparser = require('htmlparser2');
        
        let markup = `<div>
        By<div><h2 class="authorh2">John Smith</h2></div>
        </div>`;
        
        var parts = [];
        var parser = new htmlparser.Parser({
            onopentag: function(name, attributes){
                parts.push(' ');
            },
            ontext: function(text){
                parts.push(text);
            },
            onclosetag: function(tagName){
            // no-op
            }
        }, {decodeEntities: true});
        
        parser.write(markup);
        parser.end();
        
        // Join the parts and replace all occurances of 2 or more
        // spaces with a single space.
        const result = parts.join('').replace(/\ {2,}/g, ' ');
        
        console.log(result); // By John Smith
        

        这是另一个使用方法的例子:https://runkit.com/jfahrenkrug/htmlparser2-demo/1.0.0

        【讨论】:

          【解决方案5】:

          Cheerio 的 text() 方法主要用于从抓取中获取干净的文本。正如您已经体验过的那样,这与将 HTML 页面转换为纯文本有点不同。如果您只需要文本进行索引,则使用正则表达式替换添加空格将起作用。对于其他一些场景,例如转换为音频,它并不总是有效,因为您需要区分空格和换行。

          我的建议是使用一个库来将 HTML 转换为 markdown。一种选择是turndown

          var TurndownService = require('turndown')
          
          var turndownService = new TurndownService()
          var markdown = turndownService.turndown('<div>\nBy<div><h2>John Smith</h2></div></div>')
          

          这将打印出来:

          'By\n\nJohn Smith\n----------'
          

          最后一行是因为 H2 标题。 Markdown 更容易清理,您可能只需要删除 URL 和图像。文字显示也更容易被人类阅读。

          【讨论】:

            【解决方案6】:

            如果您想要内容的清晰文本表示,我建议使用lynx(古腾堡项目使用)或pandoc。两者都可以安装,然后使用spawn 从节点调用。这些将提供比运行 puppeteer 和使用 textContent 或 innerText 更清晰的文本表示。

            您也可以尝试遍历 DOM 并根据节点类型添加新行。

            import "./styles.css";
            import cheerio from "cheerio";
            
            const NODE_TYPES = {
              TEXT: "text",
              ELEMENT: "tag"
            };
            
            const INLINE_ELEMENTS = [
              "a",
              "abbr",
              "acronym",
              "audio",
              "b",
              "bdi",
              "bdo",
              "big",
              "br",
              "button",
              "canvas",
              "cite",
              "code",
              "data",
              "datalist",
              "del",
              "dfn",
              "em",
              "embed",
              "i",
              "iframe",
              "img",
              "input",
              "ins",
              "kbd",
              "label",
              "map",
              "mark",
              "meter",
              "noscript",
              "object",
              "output",
              "picture",
              "progress",
              "q",
              "ruby",
              "s",
              "samp",
              "script",
              "select",
              "slot",
              "small",
              "span",
              "strong",
              "sub",
              "sup",
              "svg",
              "template",
              "textarea",
              "time",
              "u",
              "tt",
              "var",
              "video",
              "wbr"
            ];
            
            const content = `
            <div>
              By March
              <div>
                <h2 class="authorh2">John Smith</h2>
                <div>line1</div>line2
                     line3
                <ul>
                  <li>test</li>
                  <li>test2</li>
                  <li>test3</li>
                </ul>
              </div>
            </div>
            `;
            
            const isInline = (element) => INLINE_ELEMENTS.includes(element.name);
            const isBlock = (element) => isInline(element) === false;
            const walkTree = (node, callback, index = 0, level = 0) => {
              callback(node, index, level);
              for (let i = 0; i < (node.children || []).length; i++) {
                walkTree(node.children[i], callback, i, ++level);
                level--;
              }
            };
            
            const docFragText = [];
            const cheerioFn = cheerio.load(content);
            const docFrag = cheerioFn("body")[0];
            
            walkTree(docFrag, (element) => {
              if (element.name === "body") {
                return;
              }
            
              if (element.type === NODE_TYPES.TEXT) {
                const parentElement = element.parent || {};
                const previousElement = element.prev || {};
            
                let textContent = element.data
                  .split("\n")
                  .map((nodeText, index) => (/\w/.test(nodeText) ? nodeText + "\n" : ""))
                  .join("");
            
                if (textContent) {
                  if (isInline(parentElement) || isBlock(previousElement)) {
                    textContent = `${textContent}`;
                  } else {
                    textContent = `\n${textContent}`;
                  }
                  docFragText.push(textContent);
                }
              }
            });
            
            console.log(docFragText.join(""));
            
            

            【讨论】:

              猜你喜欢
              • 2015-04-11
              • 1970-01-01
              • 2015-09-14
              • 2017-09-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多