【问题标题】:regexp for finding everything between <a> and </a> tags用于查找 <a> 和 </a> 标记之间的所有内容的正则表达式
【发布时间】:2008-12-05 07:23:52
【问题描述】:

我正在尝试找到一种方法来列出&lt;a&gt;&lt;/a&gt; 标记之间的所有内容。所以我有一个链接列表,我想获取链接的名称(不是链接的位置,而是它们在页面上的名称)。对我真的很有帮助。

目前我有这个:

$lines = preg_split("/\r?\n|\r/", $content);  // content is the given page
foreach ($lines as $val) {
  if (preg_match("/(<A(.*)>)(<\/A>)/", $val, $alink)) {     
    $newurl = $alink[1];

    // put in array of found links
    $links[$index] = $newurl;
    $index++;
    $is_href = true;
  }
}

【问题讨论】:

  • 3? 4?几天之内的次数叹息
  • 我们应该关闭它,因为这是一个非常多余的问题吗?
  • 部分原因是“相关问题”功能不太好用。部分原因是人们在提出问题之前并没有调查谷歌(那些神秘地停止提出此类问题的人)。

标签: php regex


【解决方案1】:

标准免责声明适用:使用正则表达式解析 HTML 并不理想。成功取决于逐个字符级别的输入格式是否正确。如果你不能保证这一点,正则表达式将无法在某些时候做正确的事情。

话虽如此:

<a\b[^>]*>(.*?)</a>   // match group one will contain the link text

【讨论】:

  • 这将匹配任何以“a”开头的标签,直到一个/a。 (.*) 将挑出一个标签
  • HTML 4.01 / XHTML 1.0 定义了 a、abbr、acronym、address、applet 和 area 标签,它们都将匹配
  • 没错。我添加了 \b 以避免这种情况。 @e-satis:这里不需要非贪婪匹配,“[^
  • 如果 regex 不是查找 之间所有内容的最佳方法,那是什么?
  • 感谢您的帮助。我意识到正则表达式不是最好的方法。我正在尝试 slim 建议的 PHP html 解析器。
【解决方案2】:

我是正则表达式的忠实粉丝,但这不是使用它们的正确地方。

使用真正的 HTML 解析器。

  • 你的代码会更清晰
  • 它更有可能奏效

我在 Google 上搜索了一个 PHP HTML 解析器,并找到了 this one

如果您知道您正在使用 XHTML,那么您可以使用 PHP 的标准 XML 解析器。

【讨论】:

    【解决方案3】:
    <a\s*(.*)\>(.*)</a>
    
    <a href="http://www.stackoverflow.com">Go to stackoverflow.com</a>
    

    $1 = href="www.stackoverflow.com"

    $2 = 访问 stackoverflow.com

    我回答了一个类似的问题,除了标签 here 之外的所有内容都被删除

    【讨论】:

    • 我更改了答案以解决这种情况,感谢您的提示。尽管如此,由于贪婪的星星,您的“(。*)”是错误的。
    • 这个答案在教研究人员坏事。观看此技术失败@3v4l.org/qMd7v == 不好的建议
    【解决方案4】:

    正则表达式,黑魔法,再次:)

    我发现了一个关于常见正则表达式的nice question。有一些有趣的链接,您可以在其中找到非常常见的正则表达式。

    抓取 HTML 标签

    ]>(.?) 用 RegexBuddy 分析这个正则表达式,匹配特定 HTML 标记的开始和结束对。标签之间的任何内容都被捕获到第一个反向引用中。正则表达式中的问号使星号变得懒惰,以确保它在第一个结束标记之前而不是在最后一个结束标记之前停止,就像贪婪的星号一样。此正则表达式将无法正确匹配嵌套在自身内部的标签,例如 onetwoone。

    )\b[^>]>(.*?) 用 RegexBuddy 分析这个正则表达式将匹配任何的开始和结束对HTML 标记。请务必关闭区分大小写。此解决方案的关键是在正则表达式中使用反向引用 \1。标签之间的任何内容都被捕获到第二个反向引用中。此解决方案也不会匹配嵌套在自身中的标签。

    否则:浏览此链接:keyword "link"。过滤链接有一些有趣的方法。

    我希望这会有所帮助:)

    祝你好运!

    【讨论】:

      【解决方案5】:

      嗯..使用正则表达式并不完美,但是在perl regexp中,

      m!<a .*?>(.*?)</a>!i
      

      应该给你匹配组一中该行的第一个链接的名称,忽略大小写。

      限制:

      • 不处理一行中的多个链接
      • 不处理跨越多行的链接。
      • 也将匹配锚标记。

      您可以通过将所有行合并为一行,然后使用链接开始作为分隔符将其拆分为一个数组(或多行)来解决此问题。

      【讨论】:

      • 这不是 Perl 问题。
      • 不,但这是一个关于正则表达式的问题,您会发现许多语言都支持“perl 兼容的正则表达式”。我建议今天不要使用正则表达式进行这种文档解析。陷阱太多了。
      • 我同意;这就是为什么我在这里揭发了所有正则表达式的答案,并发布了两种 dom 解析技术来展示稳定/可靠的做法,以造福于不知情的研究人员。
      【解决方案6】:

      搭配图案

      '<a.*?>(.*?)</a>'
      

      你会得到

      ['sign up', 'log in', 'careers 2.0']
      

      在此标记中搜索:

      <span id="hlinks-nav"><a href="/users/login?returnurl=%2fquestions%2f343115%2fregexp-for-finding-everything-between-a-and-a-tags">sign up</a><span class="lsep">|</span><a href="/users/login?returnurl=%2fquestions%2f343115%2fregexp-for-finding-everything-between-a-and-a-tags">log in</a><span class="lsep">|</span><a href="http://careers.stackoverflow.com">careers 2.0</a><span class="lsep">|</span></span>
      

      【讨论】:

      • 此建议模式缺少分隔符并且容易损坏。看这里失败了:3v4l.org/rJvhR == 不好的建议
      【解决方案7】:

      如果存在一些假想或无效的边缘情况,带有is 标志的["'] 边界的表达式也是一个选项,例如:

      <a\s.*?['"]\s*>((?:(?!<\/a>).)*)<\/a>
      

      测试

      $re = '/<a\s.*?[\'"]\s*>((?:(?!<\/a>).)*)<\/a>/si';
      $str = '<a href="https://google.com"
      title="some title"
      data-key="{\'key\':\'adf0a8dfq<>*1$4%\' >
      
      some context in here <>
      
      some context in there <>
      
      </a>
      
      <A href="https://google.com"
      title="some title"
      data-key="{\'key\':\'adf0a8dfq<>*1$4%\'>
      
      some context in here
      
      some context in there
      
      </A>';
      
      preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
      
      var_dump($matches);
      

      如果您希望简化/修改/探索表达式,在regex101.com 的右上角面板中已对此进行了说明。如果您愿意,您还可以在this link 中观看它如何与一些示例输入匹配。


      正则表达式电路

      jex.im 可视化正则表达式:

      【讨论】:

      • 使用PREG_SET_ORDER 实际上使提取所需值更像是一项任务。我觉得这也是个坏建议。 3v4l.org/rOZCZ 与其发布更多,请专注于发布更好/更周到
      【解决方案8】:

      如果我要抱怨所有的正则表达式解决方案,我想我需要实际演示如何使用正确的 HTML 解析器(OP 没有表明要解析的 HTML 以任何方式无效——所以合法的解析器绝对适合脚本的稳定性和质量)。

      现在,我的建议确实要求您熟悉 DOMDocument(以及可选的 DOMXPath)的基础知识,但是一旦您了解所涉及的组件,您就会发现其语法远没有正则表达式那么神秘。出于这个原因,我还认为这种技术将提高脚本的整体可读性(对于您和您的代码的未来读者)。

      代码:(Demos)

      $html = <<<HTML
      <a href="#">hello</a> <abbr href="#">FYI</abbr> <a title="goodbye">later</a>
      <a href=https://example.com>no quoted attributes</a>
      <A href="https://example.com"
      title="some title"
      data-key="{\'key\':\'adf0a8dfq<>*1$4%\'">a link with data attribute</A>
      and
      this is <a title="hello">not a hyperlink</a> but simply an anchor tag
      HTML;
      
      $dom = new DOMDocument; 
      $dom->loadHTML($html);
      $xpath = new DOMXPath($dom);
      $linkText = [];
      foreach ($xpath->evaluate("//a[@href]") as $node) {
          $linkText[] = $node->nodeValue;
      }
      var_export($linkText);
      

      输出:

      array (
        0 => 'hello',
        1 => 'no quoted attributes',
        2 => 'a link with data attribute',
      )    
      

      如果您不关心现有的href 属性:

      代码:

      $doc = new DOMDocument();
      $doc->loadHTML($html);
      $aTags = [];
      foreach ($doc->getElementsByTagName('a') as $a) {
          $aTags[] = $a->nodeValue;
      }
      var_export($aTags);
      

      输出:

      array (
        0 => 'hello',
        1 => 'later',
        2 => 'no quoted attributes',
        3 => 'a link with data attribute',
        4 => 'not a hyperlink',
      )
      

      【讨论】:

        【解决方案9】:

        使用 preg_match_all 创建一个列表的最佳和最快的方法是使用 preg_match_all。

        例子:

        $pattern = '#<a[^>]*>([^<]*)<\/a>#';
        $subject = '<a href="#">Link 1</a> <a href="#">Link 3</a> <a href="#">Link 3</a>';
        preg_match_all($pattern, $subject, $matches);
        print_r($matches[1]);
        

        $pattern = '#<a[^>]*>(.*?)<\/a>#';
        $subject = '<a href="#">2 > 1</a> <a href="#">1 < 2</a>';
        preg_match_all($pattern, $subject, $matches);
        

        结果将是:

        Array (
         [0] => Link 1
         [1] => Link 3
         [2] => Link 3
        )
        

        【讨论】:

        • 这种技术不可靠。来自$subject = '&lt;a href="#"&gt;2 &gt; 1&lt;/a&gt; &lt;a href="#"&gt;1 &lt; 2&lt;/a&gt;'; 我希望有 2 个链接,但您的技术只提供了一个。 3v4l.org/1atja == 不好的建议
        • 尝试以下操作:$pattern = '#&lt;a[^&gt;]*&gt;(.*?)&lt;\/a&gt;#'; $subject = '&lt;a href="#"&gt;2 &gt; 1&lt;/a&gt; &lt;a href="#"&gt;1 &lt; 2&lt;/a&gt;'; preg_match_all($pattern, $subject, $matches);
        • 感谢您的回复,但我对用这个问题玩无休止的break&patch游戏不感兴趣。请参阅我最近添加的答案中的技术应该如何解析有效的 html。
        猜你喜欢
        • 2015-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-19
        • 2013-04-09
        • 1970-01-01
        • 2016-06-03
        • 2014-02-17
        相关资源
        最近更新 更多