使用Perl模块HTML::TreeBuilder::LibXML提取h2对应p文本并判断节点类型
这是一个很典型的HTML结构化提取需求,我来给你一步步拆解解决方案:
完整实现代码
use strict; use warnings; use HTML::TreeBuilder::LibXML; # 你的HTML内容 my $html = <<'HTML'; <h2><a name="abbey">Abbey</a></h2> <p>text paragraph 1</p> <p>text paragraph 2</p> <p>text paragraph 3</p> <h2><a name="abbess">Abbess</a></h2> <p>text paragraph 4</p> <p>text paragraph 5</p> <h2><a name="abbot">Abbot</a></h2> <p>text paragraph 6</p> <p>text paragraph 7</p> <p>text paragraph 8</p> HTML # 初始化解析器并加载HTML my $tree = HTML::TreeBuilder::LibXML->new(); $tree->parse($html); $tree->eof(); my %title_to_paragraphs; my $current_title; # 按文档顺序遍历所有h2和p节点 foreach my $node ($tree->findnodes('//h2 | //p')) { # 获取当前节点的标签名(转小写避免大小写问题) my $tag = lc($node->nodeName()); if ($tag eq 'h2') { # 提取h2内部a标签的文本作为当前标题 my $a_element = $node->findnodes('./a')->shift; if ($a_element) { $current_title = $a_element->textContent(); # 初始化数组(确保键存在) $title_to_paragraphs{$current_title} //= []; } } elsif ($tag eq 'p' && defined $current_title) { # 将p的文本追加到当前标题对应的列表中 push @{$title_to_paragraphs{$current_title}}, $node->textContent(); } } # 验证结果 foreach my $title (sort keys %title_to_paragraphs) { print "* $title: (" . join(', ', @{$title_to_paragraphs{$title}}) . ")\n"; } # 清理资源 $tree->delete();
关键细节说明
1. 构建标题-段落哈希的逻辑
核心思路是按文档顺序跟踪当前标题:
- 用XPath表达式
//h2 | //p获取所有目标节点,保证遍历顺序和HTML中的出现顺序完全一致 - 遇到
<h2>节点时,提取其内部<a>标签的文本作为哈希的键,并更新$current_title变量 - 遇到
<p>节点且当前有活跃标题时,将段落文本追加到对应键的数组中
2. 判断节点类型的方法
在HTML::TreeBuilder::LibXML中,节点基于LibXML对象,判断标签类型有两种常用方式:
$node->nodeName(): 返回节点的标签名,建议转成小写后判断(lc($node->nodeName())),避免HTML中标签大小写不一致的问题$node->tag(): 这是兼容传统HTML::TreeBuilder的方法,效果和nodeName()完全一致
如果需要确保节点是元素节点(排除文本、注释等非元素节点),可以先做类型判断:
use XML::LibXML qw(XML_ELEMENT_NODE); if ($node->nodeType() == XML_ELEMENT_NODE) { # 处理元素节点的逻辑 }
额外优化建议
- 文本清理:如果段落文本有多余的空格或换行,可以用正则清理:
$node->textContent() =~ s/^\s+|\s+$//gr - 容错处理:如果
<h2>下没有<a>标签,或者<a>没有文本,可以添加默认值或直接跳过该标题 - 内存管理:记得调用
$tree->delete()释放解析器占用的内存,避免长期运行出现内存泄漏
内容的提问来源于stack exchange,提问作者daliaessam
相关产品推荐
相关产品推荐

