0

我当然遗漏了一些明显的东西,因为这非常简单,但这是我遇到的问题。

在抓取网站时,我正在提取一些格式类似于:

<a href="/test.php?var1=123&var2=456&var3=789">SomeString</a>

虽然有时一些链接看起来像:

<a href="/test.php?var1=123&var2=456&var3=789" title="sometitle">SomeString</a>

使用像下面这样的正则表达式可以工作,但它会添加一个我不想要的匹配项:

'/<a href=\"/test.php\?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\"(^.*?)>(^.*?)<\/a>'

所以很明显我在这里得到了一个我不想要的匹配,但它确实考虑了“标题”变量出现的可能性。有没有办法告诉 preg_match_all() “这里可能有东西,但你应该忽略它”或者如果它找到它,它匹配它?

谢谢。

编辑,因为不清楚:

在我的原始示例中,我想找出 var1、var2、var3 等于什么,以及和之间的文本(在我的示例中为 SomeString)。如果我正在浏览一个有很多结果的页面,有时字符串中会包含“title=”,这会使我的正则表达式失效。所以我想告诉它“这可能在这里,但不匹配,忽略它”。

4

4 回答 4

3

请记住,使用正则表达式解析 html 并不是最好的方法,您可以使用这个更便携的解决方案:

$pattern = <<<'LOD'
~
(?:                       # open a non-capturing group
    <a\s                  # begining of the a tag
    (?:                   # open a non capturing group
        [^h>]+            # all characters but "h" and "<" one or more times
      |                   # OR
        \Bh+              # one or more "h" not preceded by a word bundary
      |                   # OR
        h(?!ref\b)        # "h" not followed by "ref"
    )*+                   # repeat the group zero or more times
    href\s*=\s*"[^?]+\?   # href with the begining of the link until the "?"
    \K                    # reset all the match (this part is not needed)
  |                       # OR
    \G(?!\A)              # a contiguous match
)                         # close the non-capturing group
(?:                       # open a non capturing group
    (?<key>[^=&]++)       # take the key
    =                     # until the "="
    (?<value>[^&"]++)     # take the value
    (?: & | (?=") )       # a "&" or followed by a double quote
  |                       # OR
    "[^>]*>               # a double quote and the end of the opening tag
    (?<content>           # open the content named capturing group
        (?:               # open a non capturing group
            [^<]+         # all characters but "<" one or more times
          |               # OR
            <(?!/a\b)     # a "<" not followed by "/a" (the closing a tag)
        )*+               # repeat the group zero or more times
    )                     # close the named capturing group
    </a>                  # the closing tag (can be removed)
)                         # close the non-capturing group
~xi
LOD;

这种模式允许做几件事:

  • 它不关心 a 标签中属性的顺序或数量

  • 它不关心键/值对的数量(它需要全部)

  • 它会忽略 url 内没有键/值的标签

  • 它允许在此处使用空格 ( href = ")

  • 它支持内容部分内的 html 标签

但是提取结果有点困难:

preg_match_all($pattern, $subject, $matches);

foreach($matches['key'] as $k => $v) {
    if (empty($v)) {
        $result[] = array('values'  => $keyval,
                          'content' => $matches['content'][$k]);
        unset($keyval);
    } else {
        $keyval[] = array($v => $matches['value'][$k]);
    }
}
print_r($result);

DOM 方式

这种方式的主要兴趣在于 DOM 解析器具有与浏览器(也是解析器)相似的行为,因为它不关心属性的数量或位置,简单,双引号或无引号,并且标签之间的内容类型。

$doc = new DOMDocument();
@$doc->loadHTML($yourhtml);
$linkNodeList = $doc->getElementsByTagName("a");

foreach($linkNodeList as $linkNode) {
    if (preg_match('~var1=(?<var1>\d+)&var2=(?<var2>\d+)&var3=(?<var3>\d+)~i',
                   $linkNode->getAttribute('href'), $match)) {
       foreach($match as $k => &$v) {
           if (is_numeric($k)) unset($v);
       }
       // take the content between "a" tags
       $content= ''; 
       $children = $linkNode->childNodes; 
       foreach ($children as $child) { 
           $content .= $child->ownerDocument->saveXML( $child ); 
       }

       $result[] = array('values' => $match, 'content' => $content);
    }
}

print_r($result);   
于 2013-07-31T14:50:06.550 回答
1

描述

这个正则表达式将:

  • 找到合适的 href 值
  • 允许 href 值被单引号或双引号,或者根本不被引用
  • 提取 var1、var2 和 var3 的查询字符串值
  • 避免锚标记中的任何其他属性
  • 允许属性以任何顺序出现
  • 避免困难的边缘情况,这使得使用正则表达式匹配 html 文本变得困难

<a\b(?=\s)(?=(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*?\shref=(['"]?)\/test.php\?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\1(?:\s|\/>|>))(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>(.*?)<\/a>

在此处输入图像描述

例子

现场演示

示例文本

请注意鼠标悬停属性中相当困难的边缘情况

<a onmouseover=' href="/test.php?var1=666&var2=666&var3=666" ; if ( 6 > a ) { funRotate(href) } ; ' href="/test.php?var1=123&var2=456&var3=789" title="sometitle">SomeString</a>

火柴

第 0 组获取从打开到关闭的整个标签
第 1 组获取引号,然后在内部使用以确保使用正确的引号来关闭 href 值
第 2-4 组从 var1、var2 和 var3 中获取值
第 5 组获取<a...>...之间的内部字符串</a>

[0][0] = <a onmouseover=' href="/test.php?var1=666&var2=666&var3=666" ; if ( 6 > a ) { funRotate(href) } ; ' href="/test.php?var1=123&var2=456&var3=789" title="sometitle">SomeString</a>
[0][1] = "
[0][2] = 123
[0][3] = 456
[0][4] = 789
[0][5] = SomeString
于 2013-07-31T16:12:37.307 回答
0

您可以使用?*字符。?被称为非贪婪,但我认为它只是“可选”。*匹配“零个或多个”。

您的正则表达式应更改为

'/<a href=\"/test.php?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\"(^.*?)?>(^.*?)<\/a>'
                                                                       ^

或者

'/<a href=\"/test.php?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\"(^.*?)*>(^.*?)<\/a>'
                                                                       ^

而且,如果您不想将关于 的位分组title="something",那么您可以通过(?:)在您的正则表达式中使用来避免捕获。所以

'/<a href=\"/test.php?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\"(?:^.*?)*>(^.*?)<\/a>'
                                                                 ^^^^^^^^^
于 2013-07-31T13:16:37.530 回答
0

我认为这应该有效:

^<a\shref=\"\/test.php\?var1=([0-9]+)&var2=([0-9]+)&var3=([0-9]+)\"(?:.*?)>(.*)?<\/a>$

问号应该在正则表达式中转义......否则它匹配

 <a href="/test.phvar1=123&var2=456&var3=789">SomeString</a>
于 2013-07-31T13:24:39.937 回答