我有一堆网址,我必须转为链接:
for my $url (@url_list) {
say "<a href='$url'>$url</a>";
}
是否有用于使可见网址更好的模块?有点像这样:
http://www.foo.com/ → www.foo.com http://www.foo.com/long_path → www.foo.com/lo…
我知道一个简单的正则表达式可能会在这里做,但我被CPAN宠坏了。 :)
答案 0 :(得分:5)
RFC 2396的附录B指定了解析URI引用的正则表达式。调整一下以获得你想要的东西:
#! /usr/bin/perl
use warnings;
use strict;
use 5.10.0; # for defined-or (//)
my $uri = qr{
^
(?:([^:/?\#]+):)? # scheme = $1
(?://([^/?\#]*))? # authority = $2
([^?\#]*) # path = $3
(\?[^\#]*)? # query = $4
(\#.*)? # fragment = $5
}x;
上面的代码使用/x
modifier
它告诉正则表达式解析器忽略大多数空格,这些空格既不是反斜杠也不是字符类。您可以使用它将正则表达式分解为(略微)更易读的部分。与普通的Perl代码一样,
#
字符也被视为引入注释的元字符。
但我们希望匹配文字#
字符,如果它们存在,这意味着我需要用反斜杠来逃避它们。出于习惯,我从qr/
开始,但由于模式中的斜杠,不得不更改分隔符。
一些测试用例:
my @cases = qw(
ftp://www.foo.com.invalid/
http://www.foo.com.invalid/
http://www.foo.com.invalid/long_path
http://www.foo.com.invalid/?query
http://www.foo.com.invalid?query
http://www.foo.com.invalid/#fragment
http://www.foo.com.invalid#fragment
);
有点逻辑
for (@cases) {
my $nice;
if (my($scheme,$auth,$path,@rest) = /$uri/) {
if ($scheme eq "http" && defined $auth) {
if (grep defined, @rest) {
$nice = join "" => map $_ // "" => $auth, $path, @rest;
}
else {
$nice = $auth
. ($path eq "/" ? "" : $path);
}
}
else {
$nice = $_;
}
}
print "$_ → $nice\n";
}
和输出:
ftp://www.foo.com.invalid/ → ftp://www.foo.com.invalid/ http://www.foo.com.invalid/ → www.foo.com.invalid http://www.foo.com.invalid/long_path → www.foo.com.invalid/long_path http://www.foo.com.invalid/?query → www.foo.com.invalid/?query http://www.foo.com.invalid?query → www.foo.com.invalid?query http://www.foo.com.invalid/#fragment → www.foo.com.invalid/#fragment http://www.foo.com.invalid#fragment → www.foo.com.invalid#fragment
答案 1 :(得分:4)
诀窍在于弄清楚你想如何打印各种URL,所以在这种情况下你需要告诉你的脚本在每种情况下做什么:
use URI;
while( <DATA> ) {
chomp;
my $uri = URI->new( $_ );
my $s = $uri->scheme;
my $rest = do {
if( $s =~ /(?:https?|ftp)/ ) {
$uri->host . $uri->path_query
}
elsif( $s eq 'mailto' ) {
$uri->path
}
elsif( ! $s ) {
$uri
}
};
print "$uri -> $rest\n";
}
__END__
http://www.example.com/foo/bar.html
www.example.com/foo/bar.html
ftp://www.example.com
mailto:joe@example.com
https://www.example.com/foo?a=b;c=d
http://joe:password@www.example.com/login
这会产生:
http://www.example.com/foo/bar.html -> www.example.com/foo/bar.html
www.example.com/foo/bar.html -> www.example.com/foo/bar.html
ftp://www.example.com -> www.example.com
mailto:joe@example.com -> joe@example.com
https://www.example.com/foo?a=b;c=d -> www.example.com/foo?a=b;c=d
http://joe:password@www.example.com/login -> www.example.com/login
如果您想要特定URL的不同内容,您只需要为它创建一个分支并将所需的部分组合在一起。请注意,URI
也处理无方案URI。
如果您不希望长漂亮的URI字符串用于漂亮的打印,那么您可能会抛出这样的内容,以便在这么多字符后切断字符串:
substr( $rest, 20 ) = '...' if length $rest > 20;
这是一个given
的解决方案,它稍微清洁一点,但也有点丑陋。这是Perl 5.010版本:
use 5.010;
use URI;
while( <DATA> ) {
chomp;
my $uri = URI->new( $_ );
my $r;
given( $uri->scheme ) {
when( /(?:https?|ftp)/ ) { $r = $uri->host . $uri->path_query }
when( 'mailto' ) { $r = $uri->path }
default { $r = $uri }
}
print "$uri -> $r\n";
}
这是更丑陋的,因为我必须重复$r
的任务。 Perl 5.14将解决这个问题,尽管让given
有一个返回值。由于该稳定版本尚不可用,您必须使用实验性的5.13轨道:
use 5.013004;
use URI;
while( <DATA> ) {
chomp;
my $uri = URI->new( $_ );
my $r = do {
given( $uri->scheme ) {
when( /(?:https?|ftp)/ ) { $uri->host . $uri->path_query }
when( 'mailto' ) { $uri->path }
default { $uri }
}
};
print "$uri -> $r\n";
}
答案 2 :(得分:1)
从cpan尝试the URI module。
答案 3 :(得分:0)
我不太确定你到底想要什么。我想你要删除http://
并显示一个缩短的网址。如果是这种情况,您可以执行以下操作:
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.1;
my @url_list = ('http://www.foo.com/','http://www.foo.com/long_path');
for my $url (@url_list) {
(my $short = $url) =~ s!\w+://!!;
$short =~ s!/$!!;
$short =~ s!^(.{15}).*$!$1...!;
say "<a href='$url'>$short</a>";
}
输出:
<a href='http://www.foo.com/'>www.foo.com</a>
<a href='http://www.foo.com/long_path'>www.foo.com/lon...</a>
答案 4 :(得分:-1)
Perl的部分乐趣并非依赖于模块:)我设法了解以下解决方案:
#!/usr/bin/perl -w
use strict;
my @url_list = ("<a href=http://www.test.com>www.test.com</a>",
"<a href=http://www.example.com>www.example.com</a>",
"<a href=http://www.this.com>www.this.com</a>");
my ($protocol, $domain_name);
foreach my $url (@url_list) {
$url =~ m|(\w+)://([^/:]+)(:\d+)?/(.*)|;
$protocol = $1;
$domain_name = $2;
my ($url_part, $name_part) = split(/>/, $domain_name);
$name_part =~ s/\<//g;
print $protocol, "://" ,$url_part, " -> ", $name_part , "\n";
}
这并不令人敬畏,我最终在域名中出现了一个迷路<
,取代了一个替代品。要回答原始问题,您可以将LWP::Simple
和HTML::LinkExtor
结合使用,从网上下载和解析HTML文档。强大的组合。
**免责声明:自Ruby和Python以来,我的Perl很糟糕。向纯粹主义者道歉,要残忍你的语言。