我正在尝试使用Mojo :: DOM提取搜索结果页面中下一页的链接。但是,我遇到的问题是,在现有元素上使用->find()
后,我得到一个字符串而不是Mojo :: DOM元素。
我有:
my $pagination_elements = $dom->find("div[class*=\"pagination-block\"]");
my $page_counter_text = $pagination_elements->find("div[class=\"page-of-pages\"]")->text();
$page_counter_text =~ /^Page (\d+) of (\d+)$/;
my $current_page = int($1);
my $last_page = int($2);
my $prev_next_elements = $pagination_elements->find("a[class*=\"prev-next\"]");
my $next_page_link = $prev_next_elements->last();
my $next_page_url = $next_page_link->attr("href");
在每个页面上,可能有2个链接标记,其类别为prev-next
。我得到的不是获取最后一个元素的链接,而是包含两个标记的href
的字符串(如果两者都在页面上可用)。
现在,如果不是这样,我会这样做:
my $next_page_link = $dom->find("div[class*=\"pagination-block\"] > ul > li > a[class*=\"prev-next\"]")->last();
my $next_page_url_rel = $next_page_link->attr("href");
我收到了所需的链接。
我的问题是,为什么第二个版本有效而不是第一个?为什么我必须从根DOM元素开始获取元素列表,以及为什么从根的子元素开始返回包含所有链接标记的字符串而不仅仅是我想要的那个?
修改 我正在解析的HTML的一个例子是:
<div class="pagination-block clearfix">
<div class="page-of-pages">
Page 2 of 100
</div>
<ul class="pagination-links">
<li>
.
.
.
</li>
<li>
<a class="page-option prev-next" href="PREV LINK">Prev</a>
</li>
<li>
<a class="page-option prev-next" href="NEXT LINK">Next</a>
</li>
</ul>
</div>
答案 0 :(得分:2)
如果您可以显示正在处理的HTML示例,那将会有很大帮助。相反,我想到了这一点,我希望这很接近。
<html>
<head>
<title>Title</title>
</head>
<body>
<div class="pagination-block">
<div class="page-of-pages">Page 99 of 100</div>
<ul>
<li>
<a class="prev-next" href="/page98">Prev</a>
</li>
<li>
<a class="prev-next" href="/page100">Next</a>
</li>
<ul>
</div>
<div class="pagination-block">
<div class="page-of-pages">Page 99 of 100</div>
<ul>
<li>
<a class="prev-next" href="/page98">Prev</a>
</li>
<li>
<a class="prev-next" href="/page100">Next</a>
</li>
<ul>
</div>
</body>
</html>
现在让我们看看你的代码
my $pagination_elements = $dom->find('div[class*="pagination-block"]')
这会为您提供一个Mojo::Collection
,其中包含div
类pagination-block
的两个{。}}实例。
my $prev_next_elements = $pagination_elements->find('a[class*="prev-next"]')
这类似于map
,将Mojo::Collection
的每个成员替换为对其进行find
的结果。由于find
会返回另一个Mojo::Collection
,因此您现在拥有两个集合的集合,每个集合都有两个Mojo::DOM
个对象。澄清
$prev_next_elements
是一个Mojo::Collection
对象,size
为2
$prev_next_elements->[0]
和$prev_next_elements->[1]
都是Mojo::Collection
个对象,每个对象的大小为2
$prev_next_elements->[0][0]
,$prev_next_elements->[0][1]
,$prev_next_elements->[1][0]
和$prev_next_elements->[1][1]
都是Mojo::DOM
个对象,每个对象都包含一个<a>
元素HTML文档
my $next_page_link = $prev_next_elements->last
这需要$prev_next_elements
的第二个元素。它与$prev_next_elements->[1]
相同,因此Mojo::Collection
对象包含两个Mojo::DOM
元素,这些元素包含HTML文档中的最后两个<a>
元素。
my $next_page_url = $next_page_link->attr('href')
现在您正在执行另一项map
操作:将attr
应用于集合的两个元素,并返回包含两个href
字符串/page98
和{{1}的另一个集合}}。 Stinrgifying这个/page100
只是连接所有元素并给你Mogo::Collection
。
要解决所有问题,请抓住"/page98\n/page100"
的{{1}},为您提供last
个对象。 然后为$pagination_elements
和Mojo::DOM
元素执行find
,为您提供&#34; prev&#34;的prev
和
&#34;下一个&#34; next
个元素,最后使用Mojo::Collection
将这些元素映射到链接。最后,<a>
包含&#34; prev&#34;的attr('href')
文字。和&#34; next&#34;最后一个分页栏中的链接。
Mojo::Collection
<强>输出强>
href
您可以将所有内容折叠为更方便的内容,例如
my $pagination_elements = $dom->find('div[class*="pagination-block"]');
my $last_pagination_element = $pagination_elements->last;
my $prev_next_elements = $last_pagination_element->find('a[class*="prev-next"]');
my $prev_next_links = $prev_next_elements->attr('href');
my ($prev_page_link, $next_page_link) = ($prev_next_links->first, $prev_next_links->last);
say $prev_page_link;
say $next_page_link;
答案 1 :(得分:1)
如果您使用Data::Dump
(或某些等效模块)代替print
,您将获得有关进展情况的线索:
use Data::Dump;
dd $next_page_url;
dd $next_page_url_rel;
输出:
bless(["PREV LINK", "NEXT LINK"], "Mojo::Collection")
"NEXT LINK"
如您所见,您的第一个变量实际上包含一个集合,而不是一个字符串。
问题出现是因为Mojo::DOM->find
返回Mojo::Collection
:
my $pagination_elements = $dom->find('div[class*="pagination-block"]');
对集合执行后续find
将返回一个嵌套集合,该集合无法像last
之类的调用那样执行。
以下是三种不同的解决方案,可帮助您解决首次尝试查找链接文本的问题:
使用Mojo::DOM->at
方法{DOM}与DOM选择器匹配的第一个元素find
。
my $pagination_elements = $dom->at('div[class*="pagination-block"]');
使用Mojo::Collection->first
或->last
在后续find
之前隔离集合中的特定元素。
my $pagination_elements
= $dom->find('div[class*="pagination-block"]')->last();
使用Mojo::Collection->flatten
将后续find
创建的嵌套集合展平为包含所有元素的新集合:
my $pagination_elements = $dom->find('div[class*="pagination-block"]');
my $prev_next_elements
= $pagination_elements->find('a[class*="prev-next"]')->flatten();
所有这些方法都会使您的脚本按预期运行:
use strict;
use warnings;
use Mojo::DOM;
use Data::Dump;
my $dom = Mojo::DOM->new(do { local $/; <DATA> });
# Fix 1
my $pagination_elements = $dom->at('div[class*="pagination-block"]');
# Fix 2
#my $pagination_elements
# = $dom->find('div[class*="pagination-block"]')->last();
# Fix 3
#my $pagination_elements = $dom->find('div[class*="pagination-block"]');
#my $prev_next_elements
# = $pagination_elements->find('a[class*="prev-next"]')->flatten();
my $prev_next_elements = $pagination_elements->find('a[class*="prev-next"]');
my $next_page_link = $prev_next_elements->last();
my $next_page_url = $next_page_link->attr("href");
dd $next_page_url;
$next_page_link = $dom->find('div[class*="pagination-block"] > ul > li > a[class*="prev-next"]')->last();
my $next_page_url_rel = $next_page_link->attr("href");
dd $next_page_url_rel;
__DATA__
<html>
<head>
<title>Paging Example</title>
</head>
<body>
<div class="pagination-block clearfix">
<div class="page-of-pages">
Page 2 of 100
</div>
<ul class="pagination-links">
<li>
.
.
.
</li>
<li>
<a class="page-option prev-next" href="PREV LINK">Prev</a>
</li>
<li>
<a class="page-option prev-next" href="NEXT LINK">Next</a>
</li>
</ul>
</div>
</body>
</html>
输出:
"NEXT LINK"
"NEXT LINK"