正则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度来看,你将要双脚毙命。
我创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来获得所需的属性。 它适用于我,从身体标签获取属性