从所有<a>标签获取href值,包括嵌套的<a>标签

问题描述 投票:0回答:3

我已经搜索了几个小时(不应该有任何重复)并尝试了许多不同的方法,使用 RegEx(正则表达式)和 DOMdocument,但没有成功。

非标准 HTML 代码:

<a class="SOMECLASS" href="javascript:__FUNCTION(SOME_HREF_INSIDE)" onclick="SOME_JS_FUNCTION();" id="SOME_ID" style="SOME_STYLE">
    <a href="SOME_URL_3">SOME TEXT</a>
</a>

现在的问题是我正在尝试获取 url“SOME_URL_3”,并且在使用 RegEx 或 DOMdocument 进行解析时,一旦遇到第一个 href 就会停止。当然,由于第二个“a”标签是第一个标签的一部分,因此解析器仅将其视为一个。

我观察到浏览器在解析时似乎会自动分隔标签,如下所示。

之前:

<a href="SOME_URL">
    <a href="SOME_URL_2">
    </a>
</a>

之后:

<a href="SOME_URL">
</a>
<a href="SOME_URL_2">
</a>

我无法使用 php 复制此浏览器行为。

之前的尝试:

$dom = new DOMDocument();
@$dom->loadHTML($result);

foreach($dom->getElementsByTagName('a') as $link) { 
    $href_count = 0;
    $attrs = array();

    for ($i = 0; $i < $link->attributes->length; ++$i) {
        $node = $link->attributes->item($i);
        if ($node->nodeName == "href") {
            $attrs[$node->nodeName][$href_count] = $node->nodeValue;
            $href_count++;
            if ($href_count >= 2) {
                echo "A second href has been found";
            }
        }
    }

    echo "<pre>";
    var_dump($attrs);
    echo "</pre>";
}

正如你所料,不幸的是它不起作用,否则我不会在这里寻求帮助......

请随时分享您的知识,任何帮助或建议将不胜感激!


更新:

我忘记在我最初的问题中指定答案应该仍然允许从标准/非嵌套“a”标签捕获href。我的目标是扩展/改进现有的 HTML 解析器,以确保我还可以从任何 href 属性中检索 url。我的初始代码仅使用 RegEx,并且无法从嵌套的“a”标签中捕获附加的 href。我正在寻找的解决方案将允许从嵌套和标准/非嵌套“a”标签捕获href。 Brandon White 的解决方案仅适用于嵌套 href。然而,使用两个不同的正则表达式(嵌套/非嵌套)来解析整个 HTML 内容两次会消耗资源。如果可能的话,理想的解决方案是允许同时捕获两者的正则表达式。

php html regex href domdocument
3个回答
2
投票

以下代码提取所有

<a>
标签
href
值。 演示

$result = <<<HTML
<a href="SOME_URL">
    <a href="SOME_URL_2">
    </a>
</a>

<a href="SOME_URL3">
    <a href="SOME_URL_4">
    </a>
</a>

<a href="SOME_URL_5">
</a>
<a href="SOME_URL_6">
</a>

HTML;

$dom = new DOMDocument();
@$dom->loadHTML($result);
foreach($dom->getElementsByTagName('a') as $link) {
    $tag_html = $dom->saveHTML($link); //Get tag inner html
    
    if (substr_count($tag_html, "href") > 1) { //If tag contains more than one href attribute
        preg_match_all('/href="([^"]*)"/is', $tag_html, $link_output, PREG_SET_ORDER);
        $output[] = $link_output[1][1]; //Output second href
    } else { //Not nested tag
        $output[] = $link->getAttribute('href'); //Output first href
    }
}

echo "<pre>";
print_r($output);
echo "</pre>";

输出:

<pre>Array
(
    [0] => SOME_URL
    [1] => SOME_URL_2
    [2] => SOME_URL3
    [3] => SOME_URL_4
    [4] => SOME_URL_5
    [5] => SOME_URL_6
)
</pre>

1
投票

您实际上可以使用一些非常奇特的正则表达式来完成您所要求的事情。使用 Negative Lookahead 和一些逻辑,您实际上可以完全提取嵌套的 href 位置。

示例

$result = <<<HTML
<a href="SOME_URL">
    <a href="SOME_URL_2">
    </a>
</a>

<a href="SOME_URL3">
    <a href="SOME_URL_4">
    </a>
</a>

<a href="SOME_URL5">
</a>
<a href="SOME_URL_6">
</a>

HTML;

preg_match_all('/<a.*>(?!<\/a>)\s*<a.*href\s*=\s*"(.+)"/', $result, $matches);

var_dump($matches);

说明

RegEx 在这些棘手的情况下非常方便。值得庆幸的是,您上面尝试的所有逻辑都没有必要。您所需要的只是一些正则表达式的逻辑和知识。我一直推荐的网站是 RegExr。分析和构建有效的正则表达式非常有帮助。事实上,这是示例的 RegEx "Fiddle"

  • <a.*>
    这与任何第一个锚标记匹配
  • (?!<\/a>)
    这是一个否定前瞻 - 它检查以确保后面有结束锚标记。这确保了它是嵌套锚匹配。
  • \s*
    匹配两个锚点之间任何可能的空白。
  • <a.*href\s*=\s*"(.+)"
    这与在 href 属性和
    =
    和值之间使用任何可能的空格编写的第二个锚标记相匹配。此外,
    (.+)
    将 URL 放入捕获组。使用
    preg_match_all()
    函数,它将成为
    $match
    数组中的第二行。请参阅下面的示例输出。
  • 另请注意,它不会提取非嵌套 URL,如上面的代码示例所示。

代码输出


0
投票

将嵌套的

<a>
标签(无效的 HTML)提供给 DOMDocument 将导致结构自动解析为兄弟
<a>
标签(不再是父子)。此行为使得原始嵌套标签与原始同级标签无法区分。

作为在将文本输入 DOM 解析器之前清除父标签的绝望尝试,我编写了一个正则表达式,它将连续删除父

<a>
标签,直到没有剩余。然后 DOM 解析器就可以做它擅长的事情了。

代码:(演示

$html = <<<HTML
<a href="SOME_URL">
    <a href="SOME_URL_2">
        <a href="SOME_URL_DEEP">
        </a>
    </a>
</a>

<a href="SOME_URL3">
    <a href="SOME_URL_4">
    </a>
</a>

<a href="SOME_URL_5">
</a>
<a href="SOME_URL_6">
</a>

HTML;

do {
    $html = preg_replace(
        '#<a[^>]*>(?:(?!</?a).)*(<a[^>]*>(?:(?!</?a).)*</a>)(?:(?!</?a).)*</a>#si',
        '$1',
        $html,
        -1,
        $count
    );
} while ($count);

$dom = new DOMDocument();
$dom->loadHTML($html);
$result = [];
foreach($dom->getElementsByTagName('a') as $node) {
    $result[] = $node->getAttribute('href');
}
var_export($result);

输出:

array (
  0 => 'SOME_URL_DEEP',
  1 => 'SOME_URL_4',
  2 => 'SOME_URL_5',
  3 => 'SOME_URL_6',
)

毫无疑问,当使用正则表达式尝试解析 HTML(有效/无效)时,会出现异常和异常。

© www.soinside.com 2019 - 2024. All rights reserved.