使用 Perl 在 HTML 中查找 Favicon

Tim*_*ler 3 favicon perl www-mechanize

我正在尝试使用 Perl 查找给定 URL 的网站图标(和变体)(我想避免使用外部服务,例如 Google 的网站图标查找器)。有一个 CPAN 模块,WWW::Favicon,但它已经有十多年没有更新了——在这十年中,现在重要的变体(例如“apple-touch-icon”)已经取代了古老的“ico”文件。

我以为我在 WWW::Mechanize 中找到了解决方案,因为它可以列出给定 URL 中的所有链接,包括<link>标题标签。但是,我似乎找不到一种干净的方法来使用“find_link”方法来搜索rel属性。

例如,我尝试使用“rel”作为搜索词,希望尽管文档中没有提到它,但它可能在那里,但它不起作用。此代码返回有关无效“链接查找参数”的错误。

my $results = $mech->find_link( 'rel' => "apple-touch-icon" );
use Data::Dumper;
say STDERR Dumper $results;
Run Code Online (Sandbox Code Playgroud)

我还尝试使用其他链接查找参数,但它们似乎都不适合搜索 rel 属性。

我能弄清楚如何做到这一点的唯一方法是遍历所有链接并寻找像这样的 rel 属性:

my $results = $mech->find_all_links(  );

foreach my $result (@{ $results }) {
    my $attrs = $result->attrs();
    #'tag' => "apple-touch-icon"
    
    foreach my $attr (sort keys %{ $attrs }) {
        if ($attrs->{'rel'} =~ /^apple-touch-icon.*$/) {
            say STDERR "I found it:" . $result->url();
        }

        # Add tests for other types of icons here.
        # E.g. "mask-icon" and "shortcut icon."

    }

}
Run Code Online (Sandbox Code Playgroud)

这有效,但看起来很混乱。有没有更好的办法?

bri*_*foy 7

这是我如何使用Mojo::DOM做到这一点。获取 HTML 页面后,请使用dom来进行所有解析。从中,使用 CSS 选择器找到有趣的节点:

link[rel*=icon i][href]
Run Code Online (Sandbox Code Playgroud)

此 CSS 选择器查找link同时具有relhref标签的标签。此外,我要求rel包含 ( *=) "icon"中的值不区分大小写 (the i)。如果您想假设所有节点都将具有href,只需关闭[href]

获得链接列表后,我只提取其中的值href并将该列表转换为数组引用(尽管我可以使用Mojo::Collection方法完成其余的工作):

use v5.10;

use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new->max_redirects(3);

my $results = $ua->get( shift )
    ->result
    ->dom
    ->find( 'link[rel*=icon i][href]' )
    ->map( attr => 'href' )
    ->to_array
    ;

say join "\n", @$results;
Run Code Online (Sandbox Code Playgroud)

到目前为止效果很好:

$ perl mojo.pl https://www.perl.org
https://cdn.perl.org/perlweb/favicon.ico

$ perl mojo.pl https://www.microsoft.com
https://c.s-microsoft.com/favicon.ico?v2

$ perl mojo.pl https://leanpub.com/mojo_web_clients
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-57x57-b83f183ad6b00aa74d8e692126c7017e.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-60x60-6dc1c10b7145a2f1156af5b798565268.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-72x72-5037b667b6f7a8d5ba8c4ffb4a62ec2d.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-76x76-57860ca8a817754d2861e8d0ef943b23.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-114x114-27f9c42684f2a77945643b35b28df6e3.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-120x120-3819f03d1bad1584719af0212396a6fc.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-144x144-a79479b4595dc7ca2f3e6f5b962d16fd.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/apple-touch-icon-152x152-aafe015ef1c22234133158a89b29daf5.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/favicon-16x16-c1207cd2f3a20fd50de0e585b4b307a3.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/favicon-32x32-e9b1d6ef3d96ed8918c54316cdea011f.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/favicon-96x96-842fcd3e7786576fc20d38bbf94837fc.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/favicon-128x128-e97066b91cc21b104c63bc7530ff819f.png
https://d3g6anj9jkury9.cloudfront.net/assets/favicons/favicon-196x196-b8cab44cf725c4fa0aafdbd237cdc4ed.png
Run Code Online (Sandbox Code Playgroud)

现在,如果您发现更多有趣的案例,而您无法轻松为其编写选择器,那么问题就来了。假设并非所有rel值都包含“图标”。您可以通过指定由逗号分隔的多个选择器来获得更多幻想,这样您就不必使用实验性大小写不敏感标志:

link[rel*=icon][href], link[rel*=ICON][href]
Run Code Online (Sandbox Code Playgroud)

或不同的值rel

link[rel="shortcut icon"][href], link[rel="apple-touch-icon-precomposed"][href]
Run Code Online (Sandbox Code Playgroud)

把你喜欢的尽可能多的排队。

但是,您也可以在没有选择器的情况下过滤结果。使用 Mojo::Collection'sgrep来挑选你想要的节点:

my %Interesting = ...;
my $results = $ua->get( shift )
    ->result
    ->dom
    ->find( '...' )
    ->grep( sub { exists $Interesting{ $_->attr('rel') } } )
    ->map( attr => 'href' )
    ->to_array
    ;
Run Code Online (Sandbox Code Playgroud)

Mojo::DOMMojo Web Clients 中有更多的例子,我想我现在就去添加这个例子。