正则expression式提取标签属性

我试图提取锚标签( <a> )的属性。 到目前为止,我有这样的expression:

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

它适用于类似的string

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

和(单引号)

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

但不适用于不带引号的string:

 <a href=test.html class=xyz> 

我怎样才能修改我的正则expression式,使其与不带引号的属性一起工作? 还是有更好的方法来做到这一点?

谢谢!

更新: 感谢所有的好评和build议。 有一件事我没有提到:我可悲的是必须修补/修改不是由我自己写的代码。 而且没有时间/金钱从下往上重写这些东西。

如果你有一个像

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

这个正则expression式可以用来连续查找每个属性的名称和值

 (\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' 

虽然不通过正则expression式parsingHTML的build议是有效的,但是这里有一个expression式几乎可以完成你所问的:

 / \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下运行一个replace(我认为PCRE),你可以把\K放在属性名之前,而不必担心捕获所有你想跳过的东西。

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

有必要考虑“\”和“\”这样的angular落情况,您最好使用适当的DOMparsing器,XMLparsing器或其他许多经过testing的工具之一发明你自己的。

我不在乎你使用哪一个,只要它被认可,testing,并且你使用了一个。

 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. 

只是为了同意其他人:不要使用正则expression式parsingHTML。

不可能创build一个expression式来为即使是正确的HTML片断select属性,但不要介意所有可能的畸形变体。 你的正则expression式即使没有试图处理无效引用的缺失,也是几乎不可读的。 追逐真实世界的HTML的恐怖,你会发现自己疯狂与不可靠的expression不可维护的blob。

现有的库可以读取损坏的HTML,也可以将其更正为有效的XHTML,然后使用XMLparsing器轻松进行吞噬。 使用它们。

您不能使用相同的名称进行多个捕获。 因此,您不能在具有命名捕捉的expression式上使用量词。

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

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

或者不要在这个expression式上使用量词:

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

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

 foo="bar=' baz='quux" 

那么缺点是你必须在之后去掉前面和后面的引号。

PHP(PCRE)和Python

简单的属性提取( 查看工作 ):

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

或者通过标签开启/closuresvalidation,标签名称检索和评论转义。 这个expression式预见了未加引号/引用的单/双引号,在属性内部的引号内容,等号符号的空格,不同数量的属性,仅检查标签内的属性,以及在属性值内pipe理不同的引号。 ( 看它工作 ):

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

(用“gisx”标志更好地工作。)


使用Javascript

由于Javascript正则expression式不支持后视,它不会支持我build议的以前的expression式的大部分function。 但万一它可能适合某人的需要,你可以试试这个版本。 ( 看到它工作 )。

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

splattne,

@VonC解决scheme部分工作,但如果标签混合了未引用和引用,则存在一些问题

这个工作与混合属性

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

testing一下

 <?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]; 

像这样的东西可能会有所帮助

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

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

如果你在.NET中,我推荐HTML敏捷包,即使格式不正确的HTML也非常强大。

然后你可以使用XPath。

如果你想成为一般的,你必须看看一个标签的确切规格,就像这里 。 但即使如此,如果你做你完美的正则expression式,如果你有错误的HTML?

我build议去图书馆parsinghtml,这取决于你使用的语言:例如像Python的美丽的汤。

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

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

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

#没有修剪

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

优点:

  • 您可以修剪引号内的内容。
  • 匹配引号内的所有特殊ASCII字符。
  • 如果您有title =“您是我的”,RegEx不会中断

缺点:

  • 它返回3组; 首先是属性,然后是引号(“|'),最后是引号内的属性,即: <div title="You're">结果是第一组:title,第二组:第三组: '回覆。

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



我通常使用这个正则expression式来提取HTML标签:

如果你不使用像<div<span等标签types,我推荐这个

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

例如:

 <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 * ((?:…

改变最后的+和。 for:| [>“'])) ?[^”'] * )[“']?

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

我会重新考虑只使用一个正则expression式的策略。 当然这是一个很好的游戏,拿出一个单一的正则expression式来完成这一切。 但是从可维护性的angular度来看,你将要双脚毙命。

我也需要这个,并写了一个函数来parsing属性,你可以从这里得到它:

https://gist.github.com/4153580

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

我创build了一个PHP函数 ,可以提取任何HTML标记的属性。 它还可以处理像无效值这样的无值属性,还可以通过检查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; } 

testing代码

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

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

我正在使用这个正则expression式的XMLparsing器

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

提取元素:

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

然后使用jQueryparsing并提取你想要的位:

 $(htmlStr).attr('style') 

看看这个正则expression式和PHP – 从img标签隔离src属性

也许你可以通过DOM来获得所需的属性。 它适用于我,从身体标签获取属性