Perl 无法提取与Mojolicous的链接

Perl 无法提取与Mojolicous的链接,perl,web-scraping,mojolicious,Perl,Web Scraping,Mojolicious,我试图使用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\"]")

我试图使用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>

第2页,共100页
  • . . .

如果您能展示一个您正在处理的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>&nbsp;
            </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>&nbsp;
            </li>
            <li>
               <a class="prev-next" href="/page100">Next</a>
            </li>
         <ul>
      </div>

   </body>
</html>
这将为您提供一个
Mojo::Collection
,其中包含具有
分页块的
div
的两个实例

my $prev_next_elements = $pagination_elements->find('a[class*="prev-next"]')
这类似于一个
映射
,将
Mojo::集合
的每个成员替换为对其执行
查找
的结果。由于
find
返回另一个
Mojo::Collection
,您现在拥有两个集合的集合,每个集合都有两个
Mojo::DOM
对象。澄清

  • $prev\u next\u elements
    是一个
    Mojo::Collection
    对象,其
    大小为2

  • $prev\u next\u元素->[0]
    $prev\u next\u元素->[1]
    都是
    Mojo::Collection
    对象,每个对象的大小为2

  • $prev\u next\u elements->[0][0]
    $prev\u next\u elements->[0][1]
    $prev\u next\u elements->[1][0]
    $prev\u next\u elements->[1][/code>都是
    Mojo DOM
    对象,每个对象都包含HTML文档中的
    元素

my$next\u page\u link=$prev\u next\u elements->last

这将获取
$prev\u next\u elements
的第二个元素。它与
$prev\u next\u elements->[1]
相同,它也是一个
Mojo::Collection
对象,包含两个
Mojo::DOM
元素,它们保存HTML文档中最后两个
元素

my$next\u page\u url=$next\u page\u link->attr('href')

现在您正在执行另一个
map
操作:将
attr
应用于集合的两个元素,并返回另一个包含两个
href
字符串
/page98
/page100
的集合。t对这个
Mogo::Collection
进行加密,只需将它的所有元素连接起来,就可以得到
“/page98\n/page100”

要解决所有这些问题,请获取
$pagination_元素的
最后一个
,为您提供一个
Mojo::DOM
对象。然后对
prev
next
元素执行
find
,为您提供
Mojo::Collection
“prev”和 “下一步”
元素,最后使用
attr('href')
将这些元素映射到链接。最后,您将看到
Mojo::Collection
,其中包含最后一个分页块中“prev”和“next”链接的
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;
输出

/page98
/page100
你可以把这些折叠成更方便的东西,像这样

my $pagination_elements = $dom->find('div[class*="pagination-block"]');
my $prev_next_links = $pagination_elements->last->find('a[class*="prev-next"]')->attr('href');
my ($prev_page_link, $next_page_link) = @$prev_next_links;
say $prev_page_link;
say $next_page_link;
如果您使用(或某个等效模块)而不是,您将得到关于发生了什么的线索:

use Data::Dump;
dd $next_page_url;
dd $next_page_url_rel;
产出:

bless(["PREV LINK", "NEXT LINK"], "Mojo::Collection")
"NEXT LINK"
"NEXT LINK"
"NEXT LINK"
如您所见,第一个变量实际上包含一个集合,而不是一个字符串

出现问题的原因是返回:

对集合执行后续的
find
操作会返回一个嵌套集合,它不会像调用
last
那样执行预期的操作

以下是三种不同的解决方案,用于修复您第一次尝试查找链接文本的问题:

  • 使用该方法查找DOM结构中与CSS选择器匹配的第一个元素

    my $pagination_elements = $dom->at('div[class*="pagination-block"]');
    
  • 在后续的
    find
    之前,使用或隔离集合中的特定元素

    my $pagination_elements
        = $dom->find('div[class*="pagination-block"]')->last();
    
  • 用于将后续的
    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>
    

    如果你也能提供一份报告,建议就容易多了。以html页面为例,将其缩减为您正在讨论的部分,并将这些数据包含在您的文章中。@Miller示例已添加。谢谢!你的解释很有道理。但是,我正在解析的HTML只有一个
    分页块
    。我在上面粘贴了一个示例。我希望你也能帮我解决这个问题?@Jibran:我的修复程序只需一个
    分页块
    。唯一的区别是对
    last
    的调用将返回一个块中的最后一个,而不是两个块中的最后一个。您必须以某种方式从
    Mojo::Collection
    中提取
    Mojo::DOM
    对象,并且使用
    last
    是一种与任何方法一样好的方法。@Borodin我创建了一个答案,其中有两个额外的问题解决方案。因为我学到了一些东西,所以给你发了个通知。
    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"