【问题标题】:PHP - Parse html to retrieve href from an "a" tag that is inside an other "a" tagPHP - 解析 html 以从另一个“a”标签内的“a”标签中检索 href
【发布时间】:2016-01-17 06:39:29
【问题描述】:

我已经搜索了几个小时(不应该有任何重复),并尝试了许多不同的方法,同时使用 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”,并且在使用正则表达式或 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>";
}

如您所料,不幸的是它不起作用,在这种情况下,我不会在这里寻求帮助......

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


更新

我忘记在我最初的问题中指定答案仍应允许捕获标准 href。我的目标是“扩展”或“改进”我的实际 html 解析器,以确保我也从任何 href 中检索 url。我的初始代码仅使用 RegEx,我无法从嵌套的“a”标签中捕获第二个 href。一个完美的答案将允许捕获嵌套和标准 href。 Brandon White 的解决方案仅适用于嵌套 href,但使用两个不同的 RegEx(嵌套/标准)两次解析整个 html 内容会耗费资源。如果可能的话,理想的解决方案是允许同时捕获两者的 RegEx。

【问题讨论】:

  • 为什么要嵌套锚标签?
  • 不幸的是,我完全无法控制我正在阅读的内容,我可以向您保证这不是我练习的一部分。谢谢你的提问。
  • 从技术上讲,您能不能不使用 REGEX 环视来捕获两个锚标记?进一步提取这两者并删除除了最终的href 值之外的所有值?
  • 我实际上尝试了一些非常相似的方法,例如: if (substr_count($html, "href") > 1) { use a regex here to get the url } 但我使用这种方法的问题是我不确定如何从我的 foreach 循环中获取 $link 变量 raw html。 (请参阅我的问题中的代码)如果我能帮上忙,您的想法可能会奏效。

标签: php html regex href domdocument


【解决方案1】:

我已经能够使用以下解决方案实现我的目标:

$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)."</pre>";

输出:

array
(
    [0] => SOME_URL_2
    [1] => SOME_URL_4
    [2] => SOME_URL_5
    [3] => SOME_URL_6
)

此解决方案适用于具有混合和/或嵌套内容的整个 html 页面。它允许根据需要捕获尽可能多的嵌套href,同时仍捕获标准href“a”标签。

【讨论】:

    【解决方案2】:

    您实际上可以使用一些非常漂亮的 RegEx 来完成您的要求。使用 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 在这些棘手的情况下非常方便。谢天谢地,不需要您在上面尝试的所有逻辑。您所需要的只是一些 RegEx 的逻辑和知识。我一直推荐的网站是RegExr。分析和构建有效的 RegEx 非常有帮助。其实这里有一个RegEx "Fiddle"的例子。

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

    代码输出

    【讨论】:

    • 嗨,布兰登,我刚刚发布了一个解决方案,同时我正在研究它。如您所见,我尚未接受任何答案。我没有测试你的,但似乎你对 RegEx 的经验比我更有经验。我看到您的解决方案比我的解决方案更轻,但可定制性较低。您认为您的解决方案在解析完整的 html 页面时可以同时使用嵌套和常规“a”标签吗?
    • 它绝对可以与非嵌套一起使用,尽管您需要以两种方式之一对其进行更改。为标准锚标记重写一个不同的 RegEx 方程并单独运行它,或者将这两种类型封装在一个组中。当然,这是假设您希望将它们分开。您可以完全使用我上面给您的内容来尝试您正在做的事情。我想强调 RegEx 的用处,并建议您借此机会学习。
    • 嗨,Brandon,是否有可能只使用一个修改过的 RegEx 版本来检索“a”标签内的所有href,要么有一个,要么有一个?如果这是可能的,我只需在之后应用某种逻辑来选择保留哪一个或全部保留。这将是完美的解决方案。请告诉我,您是 RegEx 艺术家。
    • 是的,可以只使用一个正则表达式来执行此操作。虽然,您最初的问题是针对嵌套锚点。再说一次,如果我是你,我会把它当作一种学习经验来创造你想要做的事情。查看RegExr,尤其是左侧的“参考”选项卡。上面解释了所有必要的信息。 提示:您还需要查看非捕获组,以免弄乱您的阵列。
    • 您好,Brandon,很遗憾,我正在进行一个项目,目前没有足够的时间来改进脚本的那部分。我知道仅使用 RegEx 可能会使代码更高效且消耗更少,但现在这不是我的第一要务。我仍然非常感谢您在此方面的努力和合作,非常感谢!如果我有足够的空闲时间,我应该有一天回来改进我的答案,并希望能拿出一个单行字。再次感谢,祝你愉快!
    猜你喜欢
    • 1970-01-01
    • 2012-12-11
    • 2021-03-03
    • 1970-01-01
    • 2016-08-14
    • 2021-09-27
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多