【问题标题】:Javascript match() function returning full matched tagJavascript match() 函数返回完全匹配的标签
【发布时间】:2011-09-08 16:04:39
【问题描述】:
console.log( html.match( /<a href="(.*?)">[^<]+<\/a>/g ));

而不是只返回如下网址:

http://google, http://yahoo.com

它返回整个标签:

<a href="http://google.com">Google.com</a>, <a href="http://yahoo.com">Yahoo.com</a>

为什么会这样?

【问题讨论】:

  • 因为您使用正则表达式来解析 html。这也是因为第一个数组项总是包含整个匹配项,随后它具有括号内的匹配项。
  • @Zirak:这是整个回复,但在括号中。
  • 而且我理解不使用正则表达式解析html标签的原因,但这是在nodejs上,所以我没有任何选择。尝试过jsdom和杏子,但错误百出,尚未成熟。
  • “但这是在 nodejs 上,所以我没有任何选择” 你可以。 :-) 您可以创建自己的解析逻辑(可能会使用一些针对零碎的正则表达式),对于像这样的简单提取,它可以更加智能。

标签: javascript regex parsing screen-scraping


【解决方案1】:

您需要RegExp#exec 和一个循环访问匹配结果的1 索引处的元素,而不是String.match。当有 g 标志时,String.match 不返回捕获组,只是每个匹配项的索引 0 处的元素数组,这是整个匹配字符串。 (参见the spec 的第 15.5.4.10 节。)

所以本质上:

var re, match, html;

re = /<a href="(.*?)">[^<]+<\/a>/g;
html = 'Testing <a href="http://yahoo.com">one two three</a> <a href="http://google.com">one two three</a> foo';

re.lastIndex = 0; // Work around literal bug in some implementations
for (match = re.exec(html); match; match = re.exec()) {
  display(match[1]);
}

Live example

但这是用正则表达式解析 HTML。 这里有龙。


更新 re dragons,这里有一个快速列表,可以打败这个正则表达式,我想不到:

  1. a 和 href 之间除了一个空格之外的任何其他内容,例如 两个 空格而不是一个、换行符、class='foo' 等,等等。
  2. 在href 属性周围使用单引号而不是双引号。
  3. 根本没有在href 属性周围使用引号。
  4. href 属性之后也使用双引号的任何内容,例如:

    <a href="http://google.com" class="foo">
    

这不是你的正则表达式,它只是为了强调正则表达式不能可靠地单独使用来解析 HTML。它们可以构成解决方案的一部分,帮助您扫描令牌,但它们不能可靠地完成整个工作。

【讨论】:

  • +1 用于查找错误,修复它并详细说明我的正则表达式的所有问题。我也阅读了您对上述问题的评论。这些链接是我在此应用程序中所需要的,所以我认为只要我巩固我的正则表达式以涵盖链接的所有变体,例如你提到的缺失,我应该没问题。编写我自己的解析逻辑实际上是在尝试重新发明轮子,特别是对于这么简单的事情。我非常有信心 jsdom 和其他解析器会很快成熟。
【解决方案2】:

虽然您确实无法使用正则表达式可靠地 _解析_ HTML,但这不是 OP 所要求的。

相反,OP 需要一种从 HTML 文档中提取锚链接的方法,该方法可以使用正则表达式轻松且出色地处理。

上一个回复者列出的四个问题中:

  1. 锚的各个部分之间有多个空格
  2. 使用单引号而不是双引号
  3. 根本不使用引号来分隔 href 属性
  4. 具有除 href 之外的其他前导或尾随属性

只有数字 3 对 single 正则表达式解决方案提出了重大问题,但也恰好是完全不应该出现在 HTML 文档中的非标准 HTML。 (请注意,如果您发现 HTML 包含非分隔标记属性,则有一个正则表达式将匹配它们,但我认为它们不值得提取。YMMV - 您的里程可能会有所不同。)

要使用正则表达式从 HTML 中提取锚链接 (hrefs),您可以使用以下正则表达式(以注释形式):

<          # a literal '<'
a          # a literal 'a'
[^>]+?     # one or more chars which are not '>' (non-greedy)
href=      # literal 'href='
('|")      # either a single or double-quote captured into group #1
([^\1]+?)  # one or more chars that are not the group #1, captured into group #2
\1         # whatever capture group #1 matched

没有 cmets 是:

<a[^>]+?href=('|")([^\1]+?)\1

(请注意,我们不需要匹配任何超出最终分隔符的内容,包括标记的其余部分,因为我们只对锚链接感兴趣。)

在 JavaScript 中并假设“源”包含您希望从中提取锚链接的 HTML:

var source='<a href="double-quote test">\n'+
           '<a href=\'single-quote test\'>\n'+
           '<a class="foo" href="leading prop test">\n'+
           '<a    href="trailing prop test"   class="foo">\n'+
           '<a style="bar" link="baz" '+
                'name="quux" '+
                'href="multiple prop test" class="foo">\n'+
           '<a class="foo"\n href="inline newline test"\n style="bar"\n />';

当打印到控制台时,内容如下:

<a href="double-quote test">
<a href='single-quote test'>
<a class="foo" href="leading prop test">
<a    href="trailing prop test"   class="foo">
<a style="bar" link="baz" name="quux" href="multiple prop test" class="foo">
<a class="foo" 
 href="inline newline test"
 style="bar"
 />

你会写如下:

var RE=new RegExp(/<a[^>]+?href=('|")([^\1]+?)\1/gi),
    match;

while(match=RE.exec(source)) {
    console.log(match[2]);
}

它将以下行打印到控制台:

double-quote test
single-quote test
leading prop test
trailing prop test
multiple prop test
inline newline test

注意事项:

  1. 在 nodejs v0.5.0-pre 中测试的代码,但应该在任何现代 JavaScript 下运行。

  2. 由于正则表达式使用捕获组 #1 来标注前导分隔引号,因此生成的链接出现在捕获组 #2 中。

  3. 您可能希望使用以下方法验证匹配的存在、类型和长度:

    
    if(match && typeof match === 'object' && match.length > 1) {
        console.log(match[2]);
    }
    
    但它确实没有必要,因为 RegExp.exec() 在失败时返回“null”。另请注意,正确的 typeof 匹配项是“object”,而不是“Array”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2019-10-03
    • 1970-01-01
    相关资源
    最近更新 更多