【问题标题】:(PHP) Regex for finding specific href tag(PHP) 用于查找特定 href 标记的正则表达式
【发布时间】:2011-10-07 08:25:23
【问题描述】:

我有一个带有 n 个“a href”标签的 html 文档,这些标签具有不同的目标 url 和标签之间的不同文本。

例如:

<a href="http://www.example.com/d?12345abc" name="example"><span ....>lorem ipsum</span></a>
<a href="http://www.example.com/d/d?abc1234" name="example2"><span ....>example</span></a>
<a href="http://www.example.com/d.1234" name="example3">example3</a>
<a href="http://www.example.com/d/d.1234" name="example4"><img ...>test</img></a>
<a href="http://www.example.com/without_d/1234" name="example3">without a d as target url</a>

如您所见,目标网址在“d?, d., d/d?, d/d.”之间切换。在“a 标签”之间可以有 w3c 允许的任何类型的 html。

我需要一个正则表达式,它为我提供在目标 url 中具有以下组合之一的所有链接: “d?,d.,d/d?,d/d。”并且在任何位置的“a标签”之间有“Lorem”或“test”,包括子html标签。

到目前为止我的正则表达式:

href=[\"\']([^>]*?/[d]+[.|\?][^"]*?[\"\'][^>]*[/]?>.*?</a>)

我尝试如下包含 lorem / 测试:

href=[\"\']([^>]*?/[d]+[.|\?][^"]*?[\"\'][^>]*[/]?>(lorem|test)+</a>)

但这只有在我输入“。*?”时才有效。在 (lorem|test) 之前和之后,这将是贪婪的。

如果 SimpleXml 或任何其他 DOM 解析器有更简单的方法,请告诉我。否则,我将不胜感激任何有关正则表达式的帮助。

谢谢!

【问题讨论】:

    标签: php html regex href


    【解决方案1】:

    给你:

    $html = array
    (
        '<a href="http://www.example.com/d?12345abc" name="example"><span ....>lorem ipsum</span></a>',
        '<a href="http://www.example.com/d/d?abc1234" name="example2"><span ....>example</span></a>',
        '<a href="http://www.example.com/d.1234" name="example3">example3</a>',
        '<a href="http://www.example.com/d/d.1234" name="example4"><img ...>test</img></a>',
        '<a href="http://www.example.com/without_d/1234" name="example3">without a d as target url</a>',
    );
    
    $html = implode("\n", $html);
    $result = array();
    $anchors = phXML($html, '//a[contains(., "lorem") or contains(., "test")]');
    
    foreach ($anchors as $anchor)
    {
        if (preg_match('~d[.?]~', strval($anchor['href'])) > 0)
        {
            $result[] = strval($anchor['href']);
        }
    }
    
    echo '<pre>';
    print_r($result);
    echo '</pre>';
    

    输出:

    Array
    (
        [0] => http://www.example.com/d?12345abc
        [1] => http://www.example.com/d/d.1234
    )
    

    phXML() 函数是based on my DOMDocument / SimpleXML wrapper,如下:

    function phXML($xml, $xpath = null)
    {
        if (extension_loaded('libxml') === true)
        {
            libxml_use_internal_errors(true);
    
            if ((extension_loaded('dom') === true) && (extension_loaded('SimpleXML') === true))
            {
                if (is_string($xml) === true)
                {
                    $dom = new DOMDocument();
    
                    if (@$dom->loadHTML($xml) === true)
                    {
                        return phXML(@simplexml_import_dom($dom), $xpath);
                    }
                }
    
                else if ((is_object($xml) === true) && (strcmp('SimpleXMLElement', get_class($xml)) === 0))
                {
                    if (isset($xpath) === true)
                    {
                        $xml = $xml->xpath($xpath);
                    }
    
                    return $xml;
                }
            }
        }
    
        return false;
    }
    

    我现在懒得不使用这个功能了,但如果你需要,我相信你可以摆脱它。

    【讨论】:

    • 谢谢!使用 DOMDocument() 似乎是最好的方法,我对 Xpath 很熟悉,所以这根本不是问题。正如这里有人所说:“HTML 加正则表达式会在你观察时液化有情的神经,在恐怖的冲击下你的心灵会枯萎”
    • @Talisin:天哪,谁说的? (我只是希望你不知道这一点!)xD
    【解决方案2】:

    这是一个有效的正则表达式:

    $search = '/<a\s[^>]*href=["\'](?:http:\/\/)?(?:[a-z0-9-]+(?:\.[a-z0-9-]+)*)\/(?:d\/)?d[?.].*?>.*?(?:lorem|test)+.*?<\/a>/i';
    $matches = array();
    preg_match_all($search, $html, $matches);
    

    唯一的问题是它依赖于每个`标签之间有一个换行符。否则它将匹配如下内容:

    <a href="http://www.example.com/d.1234" name="example3">example3</a><a href="http://www.example.com/d/d.1234" name="example4"><img ...>test</img></a>
    

    【讨论】:

    • 感谢您提供正则表达式。越来越多地理解正则表达式对我有很大帮助。但正如你所说,它依赖于新行并且有一些错误空间,所以我将使用 DOMDocument() 代替。不过谢谢,我真的很感激。
    • 没问题 :) DOMDocument 在处理 HTML 时总是更好 :)
    【解决方案3】:

    使用 HTML 解析器。 Regex 绝对不是解析 HTML 的解决方案有很多原因。

    这里有一个很好的列表: Robust and Mature HTML Parser for PHP

    【讨论】:

      【解决方案4】:

      将只打印第一个和第四个链接,因为满足两个条件。

      preg_match_all('#href="(.*?)"(.*?)>(.*?)</a>#is', $string, $matches);
      $count = count($matches[0]);
      unset($matches[0], $matches[2]);
      
      for($i = 0; $i < $count; $i++){
      
          if(
              strpos($matches[1][$i], '/d') !== false 
              &&
              preg_match('#(lorem|test)#is', $matches[3][$i]) == true
          )
          {
              echo $matches[1][$i];    
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多