题
我正在尝试提取的属性的一个锚标记(<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>
我怎么可以修改我的regex使其工作与属性没有报价?或者是有更好的方式做到这一点吗?
谢谢!
更新: 感谢所有良好的意见和建议为止。有一件事我没有提及:可悲的是我必须补/修改的代码没有写我自己。没有时间/资金来改写这东西从底部。
解决方案
如果你有一个像元件
<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">
将不起作用。
其他提示
尽管不通过正则表达式解析 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
就在属性名称之前,不必担心捕获您想要跳过的所有内容。
令牌咒响应:你不应该调整/修改/收获/或以其他方式使用正则表达式产生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.
只是为了与其他人达成一致:用正则表达式不解析HTML
这是不可能创建将挑选出的属性即使正确片的HTML,没关系所有可能的变体格式错误的表达式。你的正则表达式已经是非常不可读,即使没有办法应付无效缺乏报价;进一步追成现实世界的HTML的恐怖,你会自己开车疯狂与不可靠的表达式的不可维护的斑点。
有现有的库要么读破HTML,或将其校正为有效的XHTML,然后可以很容易地用一个XML解析器吞食。使用它们。
您不能多次捕捉使用相同的名称。因此,不能使用在表达式量词与名为捕获。
所以,要么不使用命名捕获:
(?:(\b\w+\b)\s*=\s*("[^"]*"|'[^']*'|[^"'<>\s]+)\s+)+
或者不使用数量词上此表达式:
(?<name>\b\w+\b)\s*=\s*(?<value>"[^"]*"|'[^']*'|[^"'<>\s]+)
这的确也允许属性值等bar=' baz='quux
:
foo="bar=' baz='quux"
好缺点将是您必须之后剥离的前缘和后引号。
PHP(PCRE)和Python
简单属性提取(看看它的工作):
((?:(?!\s|=).)*)\s*?=\s*?["']?((?:(?<=")(?:(?<=\\)"|[^"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!"|')(?:(?!\/>|>|\s).)+))
或者与标签打开/关闭验证,标签名检索和评论逸出。这种表达预见未加引号/报价,单/双引号,躲过中的属性引号,空格周围等号,不同数量的属性,仅选中标签内的属性和属性值内管理不同的报价。 (看看它的工作):
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
(工作原理与 “gisx” 标志更好。)
的Javascript
由于Javascript
正则表达式不支持查询的屁股,它不支持以前的表现,我建议的大部分功能。但在情况下,它可能适合别人的需求,你可以试试这个版本。 (见它的工作)。
(\S+)=[\'"]?((?:(?!\/>|>|"|\'|\s).)+)
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 );
$毫秒随后将包含所述第二和第三元件上的键和值。
$keys = $ms[1];
$values = $ms[2];
这是我在 HTML 标签中提取属性的最佳正则表达式:
# 修剪引号内的匹配项(单引号或双引号)
(\S+)\s*=\s*([']|["])\s*([\W\w]*?)\s*\2
# 不带修剪
(\S+)\s*=\s*([']|["])([\W\w]*?)\2
优点:
- 您可以修剪引号内的内容。
- 匹配引号内的所有特殊 ASCII 字符。
- 如果你有 title="You're mine" 则正则表达式不会损坏
缺点:
- 它返回 3 组;首先是属性,然后是引号(“|”),最后是引号内的属性,即:
<div title="You're">
结果是第 1 组:标题,第 2 组:”,第 3 组:你是。
这是在线正则表达式示例:https://regex101.com/r/aVz4uG/13
我通常使用这个正则表达式来提取 HTML 标签:
如果您不使用类似的标签类型,我建议您这样做 <div
, <span
, , ETC。
<[^/]+?(?:\".*?\"|'.*?'|.*?)*?>
例如:
<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">
这是在线正则表达式示例:https://regex101.com/r/aVz4uG/15
该正则表达式中的错误是:
<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+)=|[>"']))?[^"']*)["']?
与
(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?
避免相等信号之间的空格:(\S+)\s*=\s*((?:...
更改最后一个 + 和 .为了:|[>"']))?[^"']*)["']?
这是在线正则表达式示例:https://regex101.com/r/aVz4uG/8
这样的事情可能会有所帮助。
'(\S+)\s*?=\s*([\'"])(.*?|)\2
我建议你使用 HTML整洁到HTML到XHTML转换,然后使用适当的XPath表达式提取的属性。
如果你想是一般性的,你必须看看确切的规格的标签,就像 在这里,.但是,即使有,如果你做你的完美regexp么,如果你有错误html?
我建议去图书馆分析html,取决于语言的工作:例如像蟒蛇的美丽的汤。
如果您选择在.NET我推荐的HTML敏捷包,甚至畸形的HTML非常强大的。
然后,可以使用XPath。
我考虑只使用一个正则表达式的策略。当然这是一个不错的游戏拿出一个单一的正则表达式,做这一切。但在maintainabilty方面您要拍摄自己的双脚。
标记和在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
or $3
或$4
之一。我用$2$3$4
提取价值。
最终的正则表达式是
\s+(\w+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^><"'\s]+)))?(?=(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^><"'\s]+))?)*\s*/?\s*>)
注:我除去先行所有不必要的基团,并且所有其余的基团的非捕获
。我已经创建了一个 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>';
}
这对我的作品。它也考虑到一些最终情况下,我也遇到过。
我使用此正则表达式对XML解析器
(?<=\s)[^><:\s]*=*(?=[>,\s])
提取元素:
var buttonMatcherRegExp=/<a[\s\S]*?>[\s\S]*?<\/a>/;
htmlStr=string.match( buttonMatcherRegExp )[0]
然后使用jQuery来解析,并提取所需的位:
$(htmlStr).attr('style')
看看这个 正则表达式和PHP - 从IMG标签隔离src属性一>
或许可以通过DOM走并获得所需的属性。它工作正常的我,正从身体标记属性