我正在尝试提取的属性的一个锚标记(<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给你的属性的名称,值是$2or $3$4之一。我用$2$3$4提取价值。 最终的正则表达式是

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

注:我除去先行所有不必要的基团,并且所有其余的基团的非捕获

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

https://gist.github.com/4153580

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

我已经创建了一个 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') 
scroll top