如何在正则表达式中的下一个特定字符处停止
How to stop at the next specific character in regex
我在一个大变量中有很多 link,我正在使用正则表达式提取 link。最理想的 link 应该是
<a href="/search/product/?vendornum=StaplesA03">View Stock</a>
我的正则表达式可以完美地寻找两个匹配项:完整的 Link 和 vendornum。
/<a href="\/search\/\product/(.*?)\/.*?>(.*?)<\/a>/igm
但有时,link 会包含其他信息,例如 class,它有自己的引号
<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
额外的 "s 让我失望。我想不出第一个匹配项,应该是前两个 "s
<a href="([^"]+)".*[^>].*?>View Stock</a>
我知道正则表达式可能非常具有挑战性,我正在使用 RegEx101.com,一个真正的救星。
但我似乎无法弄清楚如何匹配第一个模式,完整的 href link,但在我到达结尾之前排除任何其他 classes 与他们自己的 >
正则表达式方面的专家可以指导我吗?
如果我没看错,您想从 URL 和 link 文本中提取 vendornum 值。最好使用 html 解析器。
如果你想冒险使用可能会破坏的代码,你可以使用正则表达式来解析 html:
my $html = '<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>';
if($html =~ /<a href="[^\?]*\?vendornum=([^"]*)[^>]*>([^<]*).*$/) {
print "vendornum: , link text: \n";
} else {
print "no match";
}
输出:
vendornum: StaplesA03, link text: View Stock
解释:
vendornum=([^"]*)
- 扫描 vendornum=
,并捕获之后直到 "
之前的所有内容
[^>]*>
- 扫描剩余的属性,例如 class=""
,直至右尖括号
([^<]*)
- 捕获 link 文本
.*$
- 扫描到文本末尾
通常没有理由从头开始手动构建 HTML 解析器,但通常会遇到麻烦;正则表达式很挑剔,对细节很敏感,即使是微小的输入变化也很脆弱,而需求往往会发生变化。为什么不使用几个很棒的 HTML 库之一?
带有HTML::TreeBuilder的示例(也提取了links,需要在评论中说明)
use warnings;
use strict;
use feature 'say';
use HTML::TreeBuilder;
my $links_string =
q(<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
<a href="/search/title/?vendornum=StaplesA17" >View More Stock</a> );
my $dom = HTML::TreeBuilder->new_from_content($links_string);
my @links_html;
foreach my $tag ( $dom->look_down(_tag => "a") ) {
push @links_html, $tag->as_HTML; # the whole link, as is
my $href = $tag->attr("href");
my ($name, $value) = $href =~ /\?([^=]+)=([^&]+)/; #/
say "$name = $value";
say $tag->as_trimmed_text; # or: ->as_text, keep some spaces
# Or:
# say for $tag->content_list; # all children, and/or text
};
#say for @links_html;
我在 links 之间使用换行符表示你的“many links in a large variable”,也许周围有一些空格以及。这不会影响库完成的解析。
一些意见
这里的主力是HTML::Elementclass,它具有强大而灵活的look_down
方法。如果字符串确实只有 links 那么你可能可以直接使用那个 class,但是当像上面那样完成时,一个完整的 HTML 文档也可以解析
获得 URL 后,我使用一个非常简单的正则表达式来提取单个名称-值对。调整是否可以有更多对,或者让我知道。最重要的是,如果还有更多内容,请使用 URI
元素子元素的 as_trimmed_text
returns 文本部分,在本例中可能只是 link 的文本。 content_list returns 所有子节点(此处相同)
根据 RFC 3986
,如果有百分比编码的字符要转换,则使用 URI::Escape
这会打印
vendornum = StaplesA03
View Stock
vendornum = StaplesA17
View More Stock
另一种选择是Mojo::DOM,它是整个生态系统的一部分
use warnings;
use strict;
use feature 'say';
use Mojo::DOM;
my $links_string = q( ... ); # as above
my $dom = Mojo::DOM->new($links_string);
my @links_html;
foreach my $node ( $dom->find('a')->each ) {
push @links_html, $node->to_string; # or $node, gets stringified to HTML
my $href = $node->attr('href');
my ($name, $value) = $href =~ /\?([^=]+)=([^&]+)/; #/
say "$name = $value";
say $node->text;
}
#say for @links_html;
我用的方法和上面一样,打印出来的也是一样的。但请注意 Mojolicious 提供了其他方便的方法。通常,使用一系列有用的方法链接调用,使用 CSS 选择器可以轻松完成 HTML 的非常精细的导航。
虽然像上面那样循环在这里可能很有用,但作为示例我们也可以这样做
my $v = $dom -> find('a')
-> map(
sub {
my ($name, $value) = $_->attr('href') =~ /\?(.+?)=([^&]+)/;
say "$name = $value";
say $_->text;
}
);
what 打印与上面相同。请参阅 Mojo::Collection 以更好地使用它。
URL里面的参数,如果你真的知道名字,可以用Mojo::URL解析
my $value = Mojo::URL->new($href)
-> query
-> param('vendornum');
如果这些问题没有解决,那么 Mojo::Parameters 很有用
my $param_names = Mojo::Parameters
-> new( Mojo::URL->new($href)->query )
-> names
其中 $param_names
是一个包含查询中所有参数名称的数组引用,或者使用
my $pairs = Mojo::Parameters->new( Mojo::URL->new($href)->query ) -> pairs;
# Or
# my %pairs = @{ Mojo::Parameters->new(Mojo::URL->new($href)->query) -> pairs };
其中 returns 一个包含所有名称、值对的 arrayref 连续列出(例如,什么可以直接分配给散列)。
HTML 文档也可以使用 XML::LibXML
很好地解析。
首先你应该考虑使用 HTML::TreeBuilder 这样的事情。一旦掌握了它,它就会比想出正则表达式更容易。然而,对于快速而肮脏的任务,正则表达式就可以了。
$text =
'<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
<a x=y href="/search/product/?Vendornum=651687" foo=bar>View Stockings</A>';
$regex =
qr{<a\s[^>]*?href="(?<link>[^"]*?\?vendornum=(?<vendornum>\w+)[^"]*)"[^>]*?>(?<desc>(?:(?!</a>).)*)</a>}i;
while($text =~ m/$regex/g){ Data:Dump::pp1 %+; }
Returns
{
# tied Tie::Hash::NamedCapture
desc => "View Stock",
link => "/search/title/?vendornum=StaplesA03",
vendornum => "StaplesA03",
}
{
# tied Tie::Hash::NamedCapture
desc => "View Stockings",
link => "/search/product/?Vendornum=651687",
vendornum => 651687,
}
HTH
我在一个大变量中有很多 link,我正在使用正则表达式提取 link。最理想的 link 应该是
<a href="/search/product/?vendornum=StaplesA03">View Stock</a>
我的正则表达式可以完美地寻找两个匹配项:完整的 Link 和 vendornum。
/<a href="\/search\/\product/(.*?)\/.*?>(.*?)<\/a>/igm
但有时,link 会包含其他信息,例如 class,它有自己的引号
<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
额外的 "s 让我失望。我想不出第一个匹配项,应该是前两个 "s
<a href="([^"]+)".*[^>].*?>View Stock</a>
我知道正则表达式可能非常具有挑战性,我正在使用 RegEx101.com,一个真正的救星。
但我似乎无法弄清楚如何匹配第一个模式,完整的 href link,但在我到达结尾之前排除任何其他 classes 与他们自己的 >
正则表达式方面的专家可以指导我吗?
如果我没看错,您想从 URL 和 link 文本中提取 vendornum 值。最好使用 html 解析器。
如果你想冒险使用可能会破坏的代码,你可以使用正则表达式来解析 html:
my $html = '<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>';
if($html =~ /<a href="[^\?]*\?vendornum=([^"]*)[^>]*>([^<]*).*$/) {
print "vendornum: , link text: \n";
} else {
print "no match";
}
输出:
vendornum: StaplesA03, link text: View Stock
解释:
vendornum=([^"]*)
- 扫描vendornum=
,并捕获之后直到"
之前的所有内容
[^>]*>
- 扫描剩余的属性,例如class=""
,直至右尖括号([^<]*)
- 捕获 link 文本.*$
- 扫描到文本末尾
通常没有理由从头开始手动构建 HTML 解析器,但通常会遇到麻烦;正则表达式很挑剔,对细节很敏感,即使是微小的输入变化也很脆弱,而需求往往会发生变化。为什么不使用几个很棒的 HTML 库之一?
带有HTML::TreeBuilder的示例(也提取了links,需要在评论中说明)
use warnings;
use strict;
use feature 'say';
use HTML::TreeBuilder;
my $links_string =
q(<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
<a href="/search/title/?vendornum=StaplesA17" >View More Stock</a> );
my $dom = HTML::TreeBuilder->new_from_content($links_string);
my @links_html;
foreach my $tag ( $dom->look_down(_tag => "a") ) {
push @links_html, $tag->as_HTML; # the whole link, as is
my $href = $tag->attr("href");
my ($name, $value) = $href =~ /\?([^=]+)=([^&]+)/; #/
say "$name = $value";
say $tag->as_trimmed_text; # or: ->as_text, keep some spaces
# Or:
# say for $tag->content_list; # all children, and/or text
};
#say for @links_html;
我在 links 之间使用换行符表示你的“many links in a large variable”,也许周围有一些空格以及。这不会影响库完成的解析。
一些意见
这里的主力是HTML::Elementclass,它具有强大而灵活的
look_down
方法。如果字符串确实只有 links 那么你可能可以直接使用那个 class,但是当像上面那样完成时,一个完整的 HTML 文档也可以解析获得 URL 后,我使用一个非常简单的正则表达式来提取单个名称-值对。调整是否可以有更多对,或者让我知道。最重要的是,如果还有更多内容,请使用 URI
元素子元素的
as_trimmed_text
returns 文本部分,在本例中可能只是 link 的文本。 content_list returns 所有子节点(此处相同)根据 RFC 3986
,如果有百分比编码的字符要转换,则使用 URI::Escape
这会打印
vendornum = StaplesA03 View Stock vendornum = StaplesA17 View More Stock
另一种选择是Mojo::DOM,它是整个生态系统的一部分
use warnings;
use strict;
use feature 'say';
use Mojo::DOM;
my $links_string = q( ... ); # as above
my $dom = Mojo::DOM->new($links_string);
my @links_html;
foreach my $node ( $dom->find('a')->each ) {
push @links_html, $node->to_string; # or $node, gets stringified to HTML
my $href = $node->attr('href');
my ($name, $value) = $href =~ /\?([^=]+)=([^&]+)/; #/
say "$name = $value";
say $node->text;
}
#say for @links_html;
我用的方法和上面一样,打印出来的也是一样的。但请注意 Mojolicious 提供了其他方便的方法。通常,使用一系列有用的方法链接调用,使用 CSS 选择器可以轻松完成 HTML 的非常精细的导航。
虽然像上面那样循环在这里可能很有用,但作为示例我们也可以这样做
my $v = $dom -> find('a')
-> map(
sub {
my ($name, $value) = $_->attr('href') =~ /\?(.+?)=([^&]+)/;
say "$name = $value";
say $_->text;
}
);
what 打印与上面相同。请参阅 Mojo::Collection 以更好地使用它。
URL里面的参数,如果你真的知道名字,可以用Mojo::URL解析
my $value = Mojo::URL->new($href)
-> query
-> param('vendornum');
如果这些问题没有解决,那么 Mojo::Parameters 很有用
my $param_names = Mojo::Parameters
-> new( Mojo::URL->new($href)->query )
-> names
其中 $param_names
是一个包含查询中所有参数名称的数组引用,或者使用
my $pairs = Mojo::Parameters->new( Mojo::URL->new($href)->query ) -> pairs;
# Or
# my %pairs = @{ Mojo::Parameters->new(Mojo::URL->new($href)->query) -> pairs };
其中 returns 一个包含所有名称、值对的 arrayref 连续列出(例如,什么可以直接分配给散列)。
HTML 文档也可以使用 XML::LibXML
很好地解析。
首先你应该考虑使用 HTML::TreeBuilder 这样的事情。一旦掌握了它,它就会比想出正则表达式更容易。然而,对于快速而肮脏的任务,正则表达式就可以了。
$text =
'<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>
<a x=y href="/search/product/?Vendornum=651687" foo=bar>View Stockings</A>';
$regex =
qr{<a\s[^>]*?href="(?<link>[^"]*?\?vendornum=(?<vendornum>\w+)[^"]*)"[^>]*?>(?<desc>(?:(?!</a>).)*)</a>}i;
while($text =~ m/$regex/g){ Data:Dump::pp1 %+; }
Returns
{
# tied Tie::Hash::NamedCapture
desc => "View Stock",
link => "/search/title/?vendornum=StaplesA03",
vendornum => "StaplesA03",
}
{
# tied Tie::Hash::NamedCapture
desc => "View Stockings",
link => "/search/product/?Vendornum=651687",
vendornum => 651687,
}
HTH