如何从一个字符串中提取所有的锚点标签、它们的 href 属性和锚点文本?

10
我需要以几种不同的方式处理html字符串中的链接。
$str = 'My long <a href="http://example.com/abc" rel="link">string</a> has any
        <a href="/local/path" title="with attributes">number</a> of
        <a href="#anchor" data-attr="lots">links</a>.'
$links = extractLinks($str);
foreach ($links as $link) {
    $pattern = "#((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)#ie";
    if (preg_match($pattern,$str)) {
        // Process Remote links
        //   For example, replace url with short url,
        //   or replace long anchor text with truncated
    } else {
        // Process Local Links, Anchors

    }
}
function extractLinks($str) {
    // First, I tried DomDocument
    $dom = new DomDocument();
    $dom->loadHTML($str);
    return $dom->getElementsByTagName('a');
    // But this just returns:
    //   DOMNodeList Object
    //   (
    //       [length] => 3
    //   )

    // Then I tried Regex
    if(preg_match_all("|<a.*(?=href=\"([^\"]*)\")[^>]*>([^<]*)</a>|i", $str, $matches)) {
        print_r($matches);
    }
    // But this didn't work either.
}

extractLinks($str)的期望结果:

[0] => Array(
           'str' = '<a href="http://example.com/abc" rel="link">string</a>',
           'href' = 'http://example.com/abc';
           'anchorText' = 'string'
       ),
[1] => Array(
           'str' = '<a href="/local/path" title="with attributes">number</a>',
           'href' = '/local/path';
           'anchorText' = 'number'
       ),
[2] => Array(
           'str' = '<a href="#anchor" data-attr="lots">links</a>',
           'href' = '#anchor';
           'anchorText' = 'links'
       );

我需要这些东西,这样我就可以做一些像编辑href(添加跟踪、缩短等)或用其他内容替换整个标签(<a href="/u/username">username</a> 可以变成 username)的事情。

这里是我尝试做的一个演示


3
小马,它来了!https://dev59.com/X3I-5IYBdhLWcg3wq6do (这是一个需要匹配除XHTML自包含标签之外的开放标签的正则表达式问题) - Palpatim
1
很奇怪,仍然有很多人试图使用正则表达式解析HTML。 - Mike H-R
1
@Palpatim 哈哈!我感觉自己好像踏进了S.O.的传说中。 - Ryan
1
是否必须使用正则表达式?如果不是必须使用正则表达式,我有另一种解决方案。 - Javad
3
我建议使用DomDocument和 $dom->loadHTML($str);,然后您可以通过 $dom->getElementsByTagName('a') 轻松搜索所有 <a> 并获取它们的任何属性。 - Javad
显示剩余7条评论
2个回答

18

你只需要将它改为:

$str = 'My long <a href="http://example.com/abc" rel="link">string</a> has any
    <a href="/local/path" title="with attributes">number</a> of
    <a href="#anchor" data-attr="lots">links</a>.';

$dom = new DomDocument();
$dom->loadHTML($str);
$output = array();
foreach ($dom->getElementsByTagName('a') as $item) {
   $output[] = array (
      'str' => $dom->saveHTML($item),
      'href' => $item->getAttribute('href'),
      'anchorText' => $item->nodeValue
   );
}

通过将其放入循环中并使用getAttributenodeValuesaveHTML(THE_NODE),您将获得所需的输出。


2
@Ryan 我更新了我的回答,应该是 $dom->saveHTML($item) 而不是 $item->saveHTML($item) - Javad
如果HTML字符串中有特殊字符,例如&,则此方法将无法正常工作。 - Tahir Raza

6

像这样

<a\s*href="([^"]+)"[^>]+>([^<]+)</a>
  1. 对于第0个数组元素,整体匹配是您想要的
  2. 对于第1个数组元素,第1组捕获是您想要的
  3. 对于第2个数组元素,第2组捕获是您想要的

使用preg_match($pattern,$string,$m)

数组元素将在$m[0] $m[1] $m[3]

这里有一个PHP演示

$string = 'My long <a href="http://example.com/abc" rel="link">string</a> has any
        <a href="/local/path" title="with attributes">number</a> of
        <a href="#anchor" data-attr="lots">links</a>. ';
$regex='|<a\s*href="([^"]+)"[^>]+>([^<]+)</a>|';
$howmany = preg_match_all($regex,$string,$res,PREG_SET_ORDER);
print_r($res);

1
为什么在工作的正则表达式上投反对票? - Hans Schindler
@GordonM <a>标签是自包含的。这个正则表达式不会吞噬其他标签。 - Hans Schindler
1
@Ryan,请看一下我在我的问题中添加的完整PHP演示:http://ideone.com/A59IpP - Hans Schindler
它将匹配注释和CDATA中的字符串(JavaScript)。 - ThW
1
@HansSchindler 为什么有人会对你的“有效正则表达式”回答进行了负评?这很简单。除了diks are here之外,你的正则表达式对我来说不起作用。这个可以:'#\<a href="([^"]*)".*?\>(.+?)\<\/a\>#s'。请更新你的回答。 - Jasom Dotnet
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接