You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cheerio获取HTML锚点对应标题层级时prev()返回空文本问题排查

Let's break down why your code is returning empty text for those previous headers, and fix it up:

The Root Problem

Your current code uses prev("h" + level) to look for the previous header, but Cheerio's prev() method only checks the immediately preceding sibling element. In your HTML structure:

<h1>Title</h1>
<p>Some content...</p>
<h2><a id="123"></a>Foo</h2>

The <h2>'s immediate previous sibling is a <p>, not the <h1>. So prev("h1") returns an empty Cheerio collection, and text() gives you an empty string.

The Fix: Use prevAll() Instead

To find all preceding sibling headers (not just the immediate one) and grab the closest matching one for each level, use prevAll("h" + level).first(). Here's how to adjust your code:

const linkModel: IDictionary<ILinkModelEntry> = {};
const $ = cheerio.load(html);
let count = 0; // Assuming count is defined in your scope

$("a").each((_i, elt) => {
  const anchor = $(elt);
  const id = anchor.attr("id"); // Simplified attribute access
  if (id) {
    const parent = anchor.parent();
    const parentTag = parent.prop("tagName");
    let headerHierarchy: any[] = [];
    
    if (["H1", "H2", "H3", "H4", "H5", "H6"].includes(parentTag)) {
      let currentLevel = parseInt(parentTag[1]);
      // Start with the current header
      headerHierarchy = [{ level: currentLevel, text: parent.text().trim() }];
      
      currentLevel--;
      while (currentLevel > 0) {
        // Find ALL preceding siblings with the current level, then take the closest one
        const prevHeader = parent.prevAll(`h${currentLevel}`).first();
        if (prevHeader.length) { // Only add if we found a valid matching header
          headerHierarchy.unshift({ 
            level: currentLevel, 
            text: prevHeader.text().trim() 
          });
        }
        currentLevel--;
      }
    }
    
    linkModel[`#${id}`] = {
      originalId: id,
      count: count++,
      headerHierarchy
    };
  }
});

Key Improvements:

  • prevAll() instead of prev(): This searches all sibling elements that come before the current parent header, not just the immediate one.
  • .first(): Ensures we grab the closest matching header (the one most recent before the current title), which is what you need for a proper hierarchy.
  • .trim(): Cleans up any extra whitespace in the title text (optional but helpful for consistency).
  • Check prevHeader.length: Avoids adding empty entries if a lower-level header doesn't exist, making the code more robust for edge cases.

Testing with Your Example HTML:

  • For id="123" (parent <h2>), the code will:
    1. Add { level: 2, text: "Foo" } to the hierarchy
    2. Find the <h1>Title</h1> via prevAll("h1"), add it to the start of the array
    3. Result: [{level: 1, title: "Title"}, {level: 2, title: "Foo"}] (matches your expected output)
  • For id="456" (parent <h3>), it will:
    1. Add { level: 3, text: "Bar" }
    2. Find the <h2>Foo</h2> via prevAll("h2"), add to start
    3. Find the <h1>Title</h1> via prevAll("h1"), add to start
    4. Result: [{level: 1, title: "Title"}, {level: 2, title: "Foo"}, {level: 3, title: "Bar"}] (perfect match)

内容的提问来源于stack exchange,提问作者omg_me

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:27:42