perl 如何在正则表达式中的下一个特定字符处停止

q9rjltbz  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(158)

我在一个大的变量中有很多链接,我使用regex来提取链接。

<a href="/search/product/?vendornum=StaplesA03">View Stock</a>

我的正则表达式可以很好地查找两个匹配项:完整链接和供应商编号。

/<a href="\/search\/\product/(.*?)\/.*?>(.*?)<\/a>/igm

但有时候,链接会包含其他信息,比如一个类,它有自己的引号

<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>

我不知道第一场比赛是哪一场,哪一场是前两场

<a href="([^"]+)".*[^>].*?>View Stock</a>

我知道regex可能非常具有挑战性,我正在使用RegEx101.com,一个真实的的救生圈。
但我似乎就是想不出如何匹配第一个模式,即完整的href链接,但在我到达结尾>之前,排除任何其他具有自己的类
有正则表达式方面的Maven可以指导我吗?

ax6ht2ek

ax6ht2ek1#

通常没有理由从头开始手工构建HTML解析器,因为这样做通常会遇到麻烦;正则表达式很挑剔,对细节很敏感,即使是很小的输入变化也很脆弱,而需求却在不断变化。为什么不使用一些很棒的HTML库呢?
HTML::TreeBuilder示例(也提取链接,需要在注解中说明)

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;

我使用一个字符串,在链接之间有一个换行符,用于表示“* 在一个大变量中有许多链接 *",可能还带有一些空格。这不会影响库所做的解析。
几点意见

  • 这里的主要工具是HTML::Element类,它有强大而灵活的look_down方法。
  • 一旦我得到了URL,我就使用一个非常简单的正则表达式来提取一个名称-值对。如果可以有更多的对,请调整,或者让我知道。最重要的是,如果有更多的对,请使用URI
  • as_trimmed_text返回元素子元素的文本部分,在本例中可能只是链接的文本。content_list返回所有子节点(此处相同)
  • 根据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;
        }
    );

打印的内容与上面的相同。请参阅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是包含查询中所有参数名称的arrayref,或使用

my $pairs = Mojo::Parameters->new( Mojo::URL->new($href)->query ) -> pairs;
# Or
# my %pairs = @{ Mojo::Parameters->new(Mojo::URL->new($href)->query) -> pairs };

该函数返回一个arrayref,其中所有名称、值对都连续列出(例如,可以直接分配给hash)。
使用XML::LibXML也可以很好地解析HTML文档。

atmip9wb

atmip9wb2#

如果我没看错的话,你应该从URL和链接文本中提取vendornum值。最好使用html解析器。
如果你想在危险的代码中生活,你可以使用正则表达式来解析html:

my $html = '<a href="/search/title/?vendornum=StaplesA03" class="product-lister" >View Stock</a>';
if($html =~ /<a href="[^\?]*\?vendornum=([^"]*)[^>]*>([^<]*).*$/) {
    print "vendornum: $1, link text: $2\n";
} else {
    print "no match";
}

输出量:

vendornum: StaplesA03, link text: View Stock

说明:

  • vendornum=([^"]*)-扫描vendornum=,并捕获vendornum=之后直到"之前的所有内容
  • [^>]*>-扫描其余属性,如class="",直到右尖括号
  • ([^<]*)-捕获链接文本
  • .*$-扫描到文本结尾
t30tvxxf

t30tvxxf3#

首先,你应该考虑使用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 %+; }

退货

{
  # 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,
}

高温

相关问题