如何在 html 树中向上移动一个节点并提取链接?
Posted
技术标签:
【中文标题】如何在 html 树中向上移动一个节点并提取链接?【英文标题】:How to move up a node in html tree and extract the link? 【发布时间】:2012-09-06 06:29:56 【问题描述】:我知道我的问题标题不是描述性的,但让我在这里解释一下。
我正在尝试使用 html::TreeBuilder 解析给定的html document。现在在这个 html 文档中的值 5,1,ABC,DEF
将根据用户提供的值进行验证,如果验证成功,我必须提取 href
链接。
所以,我的代码是:
my @tag = $tree->look_down( _tag => 'tr', class => qr\bepeven\scompleted\b );
for (@tag)
query_element($_);
sub query_element
my @td_tag = $_[0]->look_down( _tag => 'td' );
my $num1 = shift @td_tag; #Get the first td tag
my $num2 = shift @td_tag; # Get the second td tag
#Making sure first/second td tag has numeric value
$num1 = $1 if $num1->as_text =~ m!(\d+)! or die "no match found";
$num2 = $1 if $num2->as_text =~ m!(\d+)! or die "no match found";
#Validating that above value's match the user provided value 5 and 1.
if ( $num1 eq '5' && $num2 eq '1' )
say "hurray..!!";
#Iterating over rest of the td tag to make sure we get the right link from it.
for (@td_tag)
#Check if contains ABC and than procede to fetch the download href link.
if ($_->look_down(_tag => 'td', class => qr[c], sub
$_[0]->as_text eq 'ABC'; )
)
my $text = $_->as_text;
say "Current node text is: ", $text; #outputs ABC
#Now from here how do I get the link I want to extract.
现在,我的方法是首先从td tags
中提取值,如果成功则将其与用户指定的值进行匹配,而不是寻找另一个用户指定的值ABC or DEF
在我的情况下是ABC
匹配的不仅仅是提取链接。
现在,标签包含ABC or DEF
没有固定位置,但它们将位于包含5 and 1
值的标签下方。所以,我使用$_[0]->as_text eq 'ABC';
来检查标签现在在我的树中是否包含ABC
我目前在text node
ABC 从这里如何提取链接href,即如何向上移动对象树并提取价值。
PS:我会在这里尝试 xpath,但 html 元素的位置不是那么明确和结构化。
编辑:
所以,我尝试了$_->tag()
并返回了td
,但如果我在 td 标签上,那么为什么以下代码不起作用:
my $link_obj = $_->look_down(_tag => 'a') # It should look for `a` tag.
say $link_obj->as_text;
但它给出了以下错误:
Can't call method "as_text" on an undefined value.
【问题讨论】:
+1 为我们提供了文档化代码。 【参考方案1】:我希望以下内容(使用我自己的 Marpa::R2::HTML)对您有所帮助。注意 HTML::TreeBuilder 答案只找到一个答案。下面的代码发现 二,我认为这是本意。
#!perl
use Marpa::R2::HTML qw(html);
use 5.010;
use strict;
use warnings;
my $answer = html(
( \join q, <DATA> ),
td => sub return Marpa::R2::HTML::contents() ,
a => sub
my $href = Marpa::R2::HTML::attributes()->href;
return undef if not defined $href;
return [ link => $href ];
,
'td.c' => sub
my @values = @ Marpa::R2::HTML::values() ;
if ( ref $values[0] eq 'ARRAY' ) return $values[0]
return [ test => 'OK' ] if Marpa::R2::HTML::contents eq 'ABC';
return [ test => 'OK' ] if Marpa::R2::HTML::contents eq 'DEF';
return [ test => '' ];
,
tr => sub
my @cells = @ Marpa::R2::HTML::values() ;
return undef if shift @cells != 5;
return undef if shift @cells != 1;
my $ok = 0;
my $link;
for my $cell (@cells)
my ( $type, $value ) = @$cell;
$ok = 1 if $type eq 'test' and $value eq 'OK';
$link = $value if $type eq 'link';
return $link if $ok;
return undef;
,
':TOP' => sub return Marpa::R2::HTML::values();
);
die "No parse" if not defined $answer;
say join "\n", @$answer;
__DATA__
<table>
<tbody>
<tr class="epeven completed">
<td>5</td>
<td>1</td>
<td class="c">ABC</td>
<td class="c">satus</td>
<td class="c"><a href="/path/link">Download</a></td>
</tr>
<tr class="epeven completed">
<td>5</td>
<td>1</td>
<td class="c">status</td>
<td class="c">DEF</td>
<td class="c"><a href="/path2/link">Download</a></td>
</tr>
</table>
【讨论】:
【参考方案2】:如果你可以放弃 HTML::TreeBuilder,你可能会像这样解析:
for my $r ($content =~ m<tr class="epeven completed">(.*?)</tr>gs)
my ($n1, $n2) = $r =~ m<td>(\d+)</td>\s*<td>(\d+)</td>g;
next if $n1 != 5 || $n2 != 1;
next if $r !~ m<td class="c">ABC</td>g;
my ($link) = $r =~ m<a href="(.*?)">Download</a>g;
say $link;
【讨论】:
***.com/questions/1732348/… 它有效。它更快。代码更短。你永远不能依赖 html 来格式化,这就是为什么人们推荐模块而不是正则表达式,但他们不能完美地解析一个不完美的文档,所以我只是放弃并用正则表达式弄脏我的手。考虑到问题的领域,这不是一个糟糕的解决方案。【参考方案3】:我不确定我是否了解您想要做什么,但是否了解这些方面的内容?使用look_down来描述你想要什么,没有必要尝试在树周围导航;那会很脆弱。
use strict;
use warnings;
use HTML::TreeBuilder 5 -weak;
use 5.014;
my $tree = HTML::TreeBuilder->new_from_content(<DATA>);
for my $e ($tree->look_down( _tag => 'a',
sub my $e = $_[0];
my $tr = $e->parent->parent; ### Could also use ->lineage to search up through the
### containing elements
return unless $tr->attr('_tag') eq 'tr' and $tr->attr('class') eq 'epeven completed';
return ( $tr->look_down( _tag => 'td', sub $_[0]->as_text eq '1'; )
and $tr->look_down( _tag => 'td', sub $_[0]->as_text eq '5'; )
and $tr->look_down( _tag => 'td', class => 'c', sub $_[0]->as_text eq 'ABC'; )
);
)
)
say $e->attr('href');
__DATA__
<table>
<tbody>
<tr class="epeven completed">
<td>5</td>
<td>1</td>
<td class="c">ABC</td>
<td class="c">satus</td>
<td class="c"><a href="/path/link">Download</a></td>
</tr>
<tr class="epeven completed">
<td>5</td>
<td>1</td>
<td class="c">status</td>
<td class="c">DEF</td>
<td class="c"><a href="/path2/link">Download</a></td>
</tr>
</table>
输出:
/path/link
【讨论】:
以上是关于如何在 html 树中向上移动一个节点并提取链接?的主要内容,如果未能解决你的问题,请参考以下文章