使用Perl在HTML中查找Favicons

问题描述

我正在尝试使用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;

我还尝试使用其他链接查找参数,但似乎都不适合搜索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."

    }

}

那行得通,但是看起来很混乱。有更好的方法吗?

解决方法

这就是我要使用Mojo::DOM的方法。提取HTML页面后,请使用dom进行所有解析。然后,使用CSS选择器找到有趣的节点:

link[rel*=icon i][href]

此CSS选择器将查找同时具有linkrel标签的href标签。另外,我要求rel中的值包含(*=)“ icon”,不区分大小写(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;

到目前为止,效果很好:

$ 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

现在,如果您发现无法轻松编写选择器的更多有趣案例,就会出现问题。假设并非所有rel值中都包含“ icon”。您可以通过指定多个选择器(以逗号分隔)来获得更多效果,因此不必使用实验用的不区分大小写标志:

link[rel*=icon][href],link[rel*=ICON][href]

rel中的其他值:

link[rel="shortcut icon"][href],link[rel="apple-touch-icon-precomposed"][href]

尽可能多地排列它们。

但是,您也可以不使用选择器来过滤结果。使用Mojo :: Collection的grep来选择所需的节点:

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

我在Mojo Web Clients中有Mojo::DOM的更多示例,我想现在就添加该示例。

,

这个问题很容易解决:

  • 允许加载网页的任何模块的帮助
  • 为所有可能的 favicon 变体定义$regex
  • 寻找<link rel="$regex" href="icon_address" ...>

注意: 该脚本的代码中嵌入了默认的 YouTube 网址

use strict;
use warnings;
use feature 'say';

use HTTP::Tiny;

my $url = shift || 'https://www.youtube.com/';

my $icons = get_favicon($url);

say for @{$icons};

sub get_favicon {
    my $url = shift;
    
    my @lookup = (
                    'shortcut icon','apple-touch-icon','image_src','icon','alternative icon'
                );
                
    my $re      = join('|',@lookup);
    my $html    = load_page($url);
    my @icons   = ($html =~ /<link rel="(?:$re)" href="(.*?)"/gmsi);
    
    return \@icons;
}

sub load_page {
    my $url = shift;
    
    my $response = HTTP::Tiny->new->get($url);
    my $html;

    if ($response->{success}) {
        $html = $response->{content};
    } else {
        say 'ERROR:  Could not extract webpage';
        say 'Status: ' . $response->{status};
        say 'Reason: ' . $response->{reason};
        exit;
    }

    return $html;
}

script.pl身份运行

https://www.youtube.com/s/desktop/8259e7c9/img/favicon.ico
https://www.youtube.com/s/desktop/8259e7c9/img/favicon_32.png
https://www.youtube.com/s/desktop/8259e7c9/img/favicon_48.png
https://www.youtube.com/s/desktop/8259e7c9/img/favicon_96.png
https://www.youtube.com/s/desktop/8259e7c9/img/favicon_144.png
https://www.youtube.com/img/desktop/yt_1200.png

script.pl "http://www.microsoft.com/"身份运行

https://c.s-microsoft.com/favicon.ico?v2

script.pl "http://finance.yahoo.com/"身份运行

https://s.yimg.com/cv/apiv2/default/icons/favicon_y19_32x32_custom.svg