用于提取标记属性的正则表达式

时间:2008-11-25 11:22:54

标签: html regex

我正在尝试提取锚标记(<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>

如何修改我的正则表达式,使其适用于没有引号的属性?或者有更好的方法吗?

谢谢!

更新: 感谢您提供的所有好评和建议。有一件事我没有提到:遗憾的是我必须修补/修改我自己编写的代码。并且没有时间/金钱从下往上重写这些东西。

19 个答案:

答案 0 :(得分:84)

如果你有像

这样的元素
<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">无效。

答案 1 :(得分:22)

虽然不通过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;

“但是等等,”你可能会说。 “那么*评论呢?!?!”好的,那么你可以用以下代码替换非回溯部分中的. :(它还处理CDATA部分。)

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

答案 2 :(得分:13)

Token Mantra响应:您不应该使用正则表达式调整/修改/收获/或以其他方式生成html / xml。

还有一些必须考虑的问题,例如\'和\“等极端情况条件。你最好使用正确的DOM Parser,XML Parser或许多其他许多经过试验和测试的工具之一这项工作,而不是发明自己的工作。

我并不关心你使用哪一个,只要它被识别,测试,你使用一个。

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. 

答案 3 :(得分:10)

仅与其他人一致:不要使用regexp解析HTML。

不可能创建一个表达式来为即使是正确的HTML片段选择属性,也不要介意所有可能的格式错误的变体。你的正则表达式已经非常难以理解,即使没有尝试应对无效的引号缺失;进一步追逐现实世界HTML的恐怖,你会因为不可靠的不可靠表达而让自己疯狂。

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

答案 4 :(得分:10)

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

所以要么不使用命名捕获:

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

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

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

这也允许使用bar=' baz='quux

等属性值
foo="bar=' baz='quux"

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

答案 5 :(得分:7)

PHP(PCRE)和Python

简单属性提取(See it working):

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

或者使用标记打开/关闭验证,标记名称检索和注释转义。此表达式预见到未加引号/引号,单引号或双引号,属性内的转义引号,等号周围的空格,不同的属性数,仅检查标记内的属性,以及管理属性值中的不同引号。 (See it working):

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

(使用&#34; gisx&#34;标志更好地工作。)

的Javascript

由于Javascript正则表达式不支持后视,因此它不支持我建议的先前表达式的大部分功能。但是如果它可能符合某人的需要,你可以尝试这个版本。 (See it working)。

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

答案 6 :(得分:4)

这是我在HTML标签中提取属性的最好的RegEx:

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

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

#without trim

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

优点:

  • 您可以修改引号内的内容。
  • 匹配引号内的所有特殊ASCII字符。
  • 如果你有title =“你是我的”,RegEx没有破坏

缺点:

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

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

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

如果您不使用<div<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>

返回<div '>但不应返回任何匹配项:

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+)=|[>"']))?[^"']*)["']?

VS

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

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

更改最后一个+和。对于: | [&GT;? “']))的 [^” '] * )[“']

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

答案 7 :(得分:4)

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将包含第2和第3个元素的键和值。

$keys = $ms[1];
$values = $ms[2];

答案 8 :(得分:3)

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

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

答案 9 :(得分:2)

如果您想成为一般人,则必须查看标签的精确规格,例如here。但即便如此,如果你做了完美的正则表达式,如果你的html格式不正确怎么办?

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

答案 10 :(得分:2)

如果您使用.NET,我建议使用HTML敏捷包,即使格式错误的HTML也非常强大。

然后你可以使用XPath。

答案 11 :(得分:2)

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

答案 12 :(得分:1)

我会重新考虑只使用一个正则表达式的策略。当然,这是一个很好的游戏,可以提出一个单一的正则表达式。但就可维护性而言,你将要双脚射击自己。

答案 13 :(得分:1)

HTML中的标签和属性的格式为

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

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

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

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

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

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

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

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

答案 14 :(得分:0)

我还需要这个并编写了一个解析属性的函数,你可以从这里得到它:

https://gist.github.com/4153580

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

答案 15 :(得分:0)

我创建了一个PHP function,可以提取任何HTML标记的属性。它还可以处理没有值的disabled等属性,并且还可以通过检查{{1}来确定标记是否是独立标记(没有结束标记)或不具有结束标记结果:

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;
}

答案 16 :(得分:0)

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

我正在使用此Regex for XML解析器

(?<=\s)[^><:\s]*=*(?=[>,\s])

答案 17 :(得分:-1)

提取元素:

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

然后使用jQuery解析并提取你想要的位:

$(htmlStr).attr('style') 

答案 18 :(得分:-1)

看看这个 Regex & PHP - isolate src attribute from img tag

也许你可以浏览DOM并获得所需的属性。它适用于我,从body-tag

获取属性