如何使用HtmlAgilityPack更好地解析同级内容?
解决方案
问题根源
paragraphNode.NextSibling无法获取UL节点,是因为HTML中P和UL之间存在空白文本节点(换行、空格这类无意义节点),直接调用会拿到空白节点而非目标UL。另外原代码仅遍历P节点,会遗漏UL这类关联内容。
核心思路
- 跳过HTML中的空白文本节点,避免干扰节点遍历。
- 以包含
<strong>的P节点作为内容块起始,后续非P节点(如UL)归入该块,直到遇到下一个P节点。 - 普通P节点单独作为一个内容块。
具体代码实现
public static void Parse(string html) { var document = new HtmlDocument(); document.LoadHtml(html); var contentBlocks = new List<string>(); StringBuilder currentBlock = new StringBuilder(); // 遍历body下的所有直接子节点 foreach (var node in document.DocumentNode.SelectSingleNode("//body").ChildNodes) { // 跳过空白文本节点 if (node.NodeType == HtmlNodeType.Text && string.IsNullOrWhiteSpace(node.InnerText)) { continue; } // 处理P节点 if (node.Name.Equals("p", StringComparison.OrdinalIgnoreCase)) { // 保存已有的内容块 if (currentBlock.Length > 0) { contentBlocks.Add(currentBlock.ToString().Trim()); currentBlock.Clear(); } // 添加当前P的文本到块中 currentBlock.AppendLine(node.InnerText.Trim()); // 如果当前P包含标题(strong标签),收集后续关联节点 if (node.SelectSingleNode("./strong") != null) { var nextNode = node.NextSibling; // 遍历后续兄弟节点,直到遇到下一个P或无节点 while (nextNode != null) { // 跳过空白节点 if (nextNode.NodeType == HtmlNodeType.Text && string.IsNullOrWhiteSpace(nextNode.InnerText)) { nextNode = nextNode.NextSibling; continue; } // 遇到下一个P则停止收集 if (nextNode.Name.Equals("p", StringComparison.OrdinalIgnoreCase)) { break; } // 处理UL节点,提取LI内容 if (nextNode.Name.Equals("ul", StringComparison.OrdinalIgnoreCase)) { foreach (var li in nextNode.SelectNodes("./li")) { currentBlock.AppendLine($" - {li.InnerText.Trim()}"); } } // 可扩展处理其他节点类型(如OL、DIV等) nextNode = nextNode.NextSibling; } } } } // 保存最后一个内容块 if (currentBlock.Length > 0) { contentBlocks.Add(currentBlock.ToString().Trim()); } // 按需处理内容块,此处示例为控制台输出 foreach (var block in contentBlocks) { Console.WriteLine(block); Console.WriteLine("---"); } }
效果说明
运行后会生成符合需求的内容块:
- 前四个普通P各为独立块
- 包含Heading 1的P和后续UL合并为一个块,UL的LI以列表形式呈现
- 包含Heading 2的P为独立块
内容的提问来源于stack exchange,提问作者monkeySeeMonkeyDo
相关产品推荐
相关产品推荐

