html-用于提取标签属性的正则表达式

我正在尝试提取锚标记的属性(<a>)。 到目前为止,我有这个表达:

(?<name>\b\w+\b)\s*=\s*("(?<value>[^"]*)"|'(?<value>[^']*)'|(?<value>[^"'<> \s]+)\s*)+

适用于像

<a href="test.html" class="xyz">

和(单引号)

<a href='test.html' class="xyz">

但不适用于不带引号的字符串:

<a href=test.html class=xyz>

如何修改我的正则表达式,使其与不带引号的属性一起使用? 还是有更好的方法做到这一点?

更新:到目前为止,感谢您的所有宝贵意见和建议。 我没有提到一件事:可悲的是我必须修补/修改不是由我编写的代码。 而且没有时间/金钱从头开始重写这些东西。

splattne asked 2020-02-22T08:21:37Z
19个解决方案
85 votes

如果你有一个像

<name attribute=value attribute="value" attribute='value'>

此正则表达式可用于依次查找每个属性名称和值

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

应用于:

<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href='test.html' class="xyz">

它会产生:

'href' => 'test.html'
'class' => 'xyz'

注意:这不适用于数字属性值,例如 <div id="1">无法正常工作。

VonC answered 2020-02-22T08:22:04Z
22 votes

尽管不建议通过regexp解析HTML的建议是有效的,但是以下表达式可以执行您所要求的操作:

/
   \G                     # start where the last match left off
   (?>                    # begin non-backtracking expression
       .*?                # *anything* until...
       <[Aa]\b            # an anchor tag
    )??                   # but look ahead to see that the rest of the expression
                          #    does not match.
    \s+                   # at least one space
    ( \p{Alpha}           # Our first capture, starting with one alpha
      \p{Alnum}*          # followed by any number of alphanumeric characters
    )                     # end capture #1
    (?: \s* = \s*         # a group starting with a '=', possibly surrounded by spaces.
        (?: (['"])        # capture a single quote character
            (.*?)         # anything else
             \2           # which ever quote character we captured before
        |   ( [^>\s'"]+ ) # any number of non-( '>', space, quote ) chars
        )                 # end group
     )?                   # attribute value was optional
/msx;

“但是等等,”您可能会说。 “ *评论呢?!?!” 好的,那么您可以将非回溯部分中的\K替换为:(它也处理CDATA部分。)

(?:[^<]|<[^!]|<![^-\[]|<!\[(?!CDATA)|<!\[CDATA\[.*?\]\]>|<!--(?:[^-]|-[^-])*-->)
  • 另外,如果您想在Perl 5.10下运行替换(我认为是PCRE),则可以将\K放在属性名称的前面,而不必担心捕获要跳过的所有内容。
Axeman answered 2020-02-22T08:22:33Z
13 votes

令牌咒语响应:您不应使用正则表达式进行调整/修改/收获/否则产生html / xml。

也可能需要考虑诸如\'和\“之类的特殊情况。您最好使用适当的DOM解析器,XML解析器或用于此工作的许多其他经过测试的工具之一 发明自己的东西。

我并不在乎您使用哪一个,只要它得到认可,测试并使用。

my $foo  = Someclass->parse( $xmlstring ); 
my @links = $foo->getChildrenByTagName("a"); 
my @srcs = map { $_->getAttribute("src") } @links; 
# @srcs now contains an array of src attributes extracted from the page. 
Kent Fredric answered 2020-02-22T08:23:03Z
11 votes

只是为了与其他所有人达成共识:请勿使用正则表达式解析HTML。

无法创建一个表达式,即使是正确的HTML代码也不会挑出属性,不要管所有可能的格式错误的变体。 即使不尝试解决引号无效的问题,您的正则表达式已经非常难以理解。 进一步研究现实世界中的HTML恐怖,您将以不可维护的表达式不可靠的斑点使自己发疯。

现有的库可以读取损坏的HTML或将其更正为有效的XHTML,然后可以使用XML解析器轻松地将其吞噬。 使用它们。

bobince answered 2020-02-22T08:23:32Z
11 votes

您不能对多个捕获使用相同的名称。 因此,您不能对带有命名捕获的表达式使用量词。

因此,要么不要使用命名捕获:

(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+

或不要在此表达式上使用量词:

(?<name>\b\w+\b)\s*=\s*(?<value>"[^"]*"|'[^']*'|[^"'<>\s]+)

这也允许属性值像bar=' baz='quux

foo="bar=' baz='quux"

缺点是您必须在之后删除前导和尾随引号。

Gumbo answered 2020-02-22T08:24:10Z
7 votes

PHP(PCRE)和Python

简单的属性提取(请参见工作原理):

((?:(?!\s|=).)*)\s*?=\s*?["']?((?:(?<=")(?:(?<=\\)"|[^"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!"|')(?:(?!\/>|>|\s).)+))

或通过标签打开/关闭验证,标签名称检索和注释转义。 该表达式预见了无引号/带引号,单/双引号,属性内的转义引号,等号周围的空格,不同数量的属性,仅检查标记内的属性以及管理属性值内的不同引号。 (看到它的工作):

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

(与“ gisx”标志一起使用效果更好。)


Java脚本

由于Javascript正则表达式不支持后向查找,因此它不支持我建议的先前表达式的大多数功能。 但是,如果它可能适合某人的需求,则可以尝试此版本。 (看到它正常工作)。

(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)
Ivan Chaer answered 2020-02-22T08:24:53Z
5 votes

这是提取HTML Tag中属性的最佳方式RegEx:

#在引号内修剪匹配项(单引号或双引号)

(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2

#不带修剪

(\S+)\s*=\s*([']|["])([\W\w]*?)\2

优点:

  • 您可以在引号内修剪内容。
  • 匹配引号内的所有特殊ASCII字符。
  • 如果您拥有title =“ You're mine”,则RegEx不会损坏

缺点:

  • 返回3组; 首先是属性,然后是引号(|),最后是引号内的属性,即:<div title="You're">,结果是第1组:标题,第2组:“,第3组:您是。

这是在线RegEx示例:[https://regex101.com/r/aVz4uG/13]



我通常使用此RegEx提取HTML标签:

如果您不使用[^/]+?<span等标签类型,则建议您这样做。

<[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

例如:

<div title="a>b=c<d" data-type='a>b=c<d'>Hello</div>
<span style="color: >=<red">Nothing</span>
# Returns 
# <div title="a>b=c<d" data-type='a>b=c<d'>
# <span style="color: >=<red">

这是在线RegEx示例:[https://regex101.com/r/aVz4uG/15]

此RegEx中的错误是:

<div[^/]+?(?:\".*?\"|'.*?'|.*?)*?>

在这个标签中:

<article title="a>b=c<d" data-type='a>b=c<div '>Hello</article>

返回[^/]+?,但不应返回任何匹配项:

Match:  <div '>

要“解决”此问题,请删除[^/]+?模式:

<div(?:\".*?\"|'.*?'|.*?)*?>


答案#317081很好,但与这些情况不正确匹配:

<div id="a"> # It returns "a instead of a
<div style=""> # It doesn't match instead of return only an empty property
<div title = "c"> # It not recognize the space between the equal (=)

这是改进:

(\S+)\s*=\s*["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))?[^"']*)["']?

(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

避免相等信号之间的空格:(\ S +)\ s * = \ s *((?:...

更改最后的+和。 对于:| [>“']))?[^”'] *)[“']?

这是在线RegEx示例:[https://regex101.com/r/aVz4uG/8]

Israel Alberto RV answered 2020-02-22T08:26:55Z
4 votes

splattne,

@VonC解决方案部分起作用,但是如果标记包含未加引号和引号的混合,则存在一些问题

这与混合属性一起使用

$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

测试一下

<?php
$pat_attributes = "(\S+)=(\"|'| |)(.*)(\"|'| |>)"

$code = '    <IMG title=09.jpg alt=09.jpg src="http://example.com.jpg?v=185579" border=0 mce_src="example.com.jpg?v=185579"
    ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);
var_dump( $ms );

$code = '
<a href=test.html class=xyz>
<a href="test.html" class="xyz">
<a href=\'test.html\' class="xyz">
<img src="http://"/>      ';

preg_match_all( "@$pat_attributes@isU", $code, $ms);

var_dump( $ms );

然后,$ ms将在第二和第三元素上包含键和值。

$keys = $ms[1];
$values = $ms[2];
fedmich answered 2020-02-22T08:27:34Z
3 votes

这样的事情可能会有所帮助

'(\S+)\s*?=\s*([\'"])(.*?|)\2
user273314 answered 2020-02-22T08:27:54Z
2 votes

我建议您使用HTML Tidy将HTML转换为XHTML,然后使用合适的XPath表达式提取属性。

activout.se answered 2020-02-22T08:28:14Z
2 votes

如果要通用,则必须查看标签的精确规范,例如此处。 但是即使这样,如果您执行了完美的正则表达式,那么如果html格式不正确怎么办?

我建议去图书馆解析html,具体取决于您使用的语言:例如 像python的Beautiful Soup。

Piotr Lesnicki answered 2020-02-22T08:28:39Z
2 votes

如果您使用.NET,我建议您使用HTML敏捷性包,即使在HTML格式不正确的情况下也非常健壮。

然后,您可以使用XPath。

Andrew Bullock answered 2020-02-22T08:29:03Z
2 votes

HTML中的标签和属性具有以下形式

<tag 
   attrnovalue 
   attrnoquote=bli 
   attrdoublequote="blah 'blah'"
   attrsinglequote='bloob "bloob"' >

要匹配属性,您需要找到四种形式之一的正则表达式$1。 然后,您需要确保HTML标记内仅报告匹配项。 假设您使用正确的正则表达式,则总的正则表达式为:

attr(?=(attr)*\s*/?\s*>)

前瞻确保仅其他属性和结束标记跟随该属性。 我对$1使用以下正则表达式:

\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?

使不重要的组无法捕获。 第一个匹配组$1为您提供属性的名称,值是$2$3$4中的一个。我使用2758551126437004004292来提取值。最终的正则表达式是

\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?(?=(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^><"'\s]+))?)*\s*/?\s*>)

注意:我删除了前瞻中的所有不必要组,并使所有其余组都无法捕获。

Dietrich Baumgarten answered 2020-02-22T08:29:43Z
1 votes

我会重新考虑只使用一个正则表达式的策略。 当然,拿出一个完整的正则表达式来完成这是一个不错的游戏。 但就可维护性而言,您将双脚开枪。

innaM answered 2020-02-22T08:30:04Z
0 votes

我还需要这个,并编写了一个用于解析属性的函数,您可以从这里获取它:

[HTTPS://gist.GitHub.com/4153580]

(注意:它不使用正则表达式)

Furkan Mustafa answered 2020-02-22T08:30:33Z
0 votes

我创建了一个PHP函数,可以提取任何HTML标记的属性。 它还可以处理诸如disabled之类的没有值的属性,并且还可以通过检查content结果来确定该标签是否为独立标签(无结束标签)或没有(具有结束标签):

/*! Based on <https://github.com/mecha-cms/cms/blob/master/system/kernel/converter.php> */
function extract_html_attributes($input) {
    if( ! preg_match('#^(<)([a-z0-9\-._:]+)((\s)+(.*?))?((>)([\s\S]*?)((<)\/\2(>))|(\s)*\/?(>))$#im', $input, $matches)) return false;
    $matches[5] = preg_replace('#(^|(\s)+)([a-z0-9\-]+)(=)(")(")#i', '$1$2$3$4$5<attr:value>$6', $matches[5]);
    $results = array(
        'element' => $matches[2],
        'attributes' => null,
        'content' => isset($matches[8]) && $matches[9] == '</' . $matches[2] . '>' ? $matches[8] : null
    );
    if(preg_match_all('#([a-z0-9\-]+)((=)(")(.*?)("))?(?:(\s)|$)#i', $matches[5], $attrs)) {
        $results['attributes'] = array();
        foreach($attrs[1] as $i => $attr) {
            $results['attributes'][$attr] = isset($attrs[5][$i]) && ! empty($attrs[5][$i]) ? ($attrs[5][$i] != '<attr:value>' ? $attrs[5][$i] : "") : $attr;
        }
    }
    return $results;
}

测试代码

$test = array(
    '<div class="foo" id="bar" data-test="1000">',
    '<div>',
    '<div class="foo" id="bar" data-test="1000">test content</div>',
    '<div>test content</div>',
    '<div>test content</span>',
    '<div>test content',
    '<div></div>',
    '<div class="foo" id="bar" data-test="1000"/>',
    '<div class="foo" id="bar" data-test="1000" />',
    '< div  class="foo"     id="bar"   data-test="1000"       />',
    '<div class id data-test>',
    '<id="foo" data-test="1000">',
    '<id data-test>',
    '<select name="foo" id="bar" empty-value-test="" selected disabled><option value="1">Option 1</option></select>'
);

foreach($test as $t) {
    var_dump($t, extract_html_attributes($t));
    echo '<hr>';
}
Taufik Nurrohman answered 2020-02-22T08:30:58Z
0 votes

这对我有用。 它还考虑到了我遇到的一些最终情况。

我正在将此正则表达式用于XML解析器

(?<=\s)[^><:\s]*=*(?=[>,\s])
Roei Sabag answered 2020-02-22T08:31:23Z
-1 votes

提取元素:

var buttonMatcherRegExp=/<a[\s\S]*?>[\s\S]*?<\/a>/;
htmlStr=string.match( buttonMatcherRegExp )[0]

然后使用jQuery解析并提取所需的位:

$(htmlStr).attr('style') 
Tom Chiverton answered 2020-02-22T08:31:47Z
-1 votes

看看这个正则表达式和PHP-从img标签中隔离src属性

也许您可以遍历DOM并获取所需的属性。 它对我来说很好用,从身体标签获取属性

Stefan answered 2020-02-22T08:32:12Z
translate from https://stackoverflow.com:/questions/317053/regular-expression-for-extracting-tag-attributes