在文本中查找所有链接的 PHP 方法

9

我希望能够查找文本中所有类似于这样的链接:

Test text http://hello.world Test text 
http://google.com/file.jpg Test text https://hell.o.wor.ld/test?qwe=qwe Test text 
test text http://test.test/test

我知道我需要使用preg_match_all,但脑海中只有一个想法:从http|https|ftp开始搜索,并在空格或文本末尾结束搜索,这就是我真正需要的,所以所有的链接都将被正确地找到。
有人能帮我写php正则表达式吗?
我认为我需要在模式结尾处使用断言,但现在还不能理解它们的正确用法。
有什么想法?谢谢!

我之前提供的这个正则表达式是否符合你的要求? - Matt
1
如果你说它需要以http、https或ftp开头并以空格结尾,那么你可以简单地使用(?:https?|ftp)://\S+,注意\S+表示匹配一个或多个非空白字符。 - HamZa
@HamZa,在你的模式中,?: 是什么意思? - swamprunner7
1
@swamprunner7 (?:) 是一个非捕获组,查看这里 并将 这个参考资料 添加到书签或收藏夹中。 - HamZa
10个回答

31

我建议使用简单的正则表达式:~[a-z]+://\S+~i

  • 以协议 [a-z]+:// 开头
  • \S+ 后面跟着一个或多个非空格字符,其中 \S 是一个简写,表示 [^ \t\r\n\f]
  • 使用了修饰符 i (PCRE_CASELESS)(可能不是必须的)

因此,它可以看起来像这样:

$pattern = '~[a-z]+://\S+~';

$str = 'Test text http://hello.world Test text 
http://google.com/file.jpg Test text https://hell.o.wor.ld/test?qwe=qwe Test text 
test text http://test.test/test';

if($num_found = preg_match_all($pattern, $str, $out))
{
  echo "FOUND ".$num_found." LINKS:\n";
  print_r($out[0]);
}

输出:

FOUND 4 LINKS:
Array
(
    [0] => http://hello.world
    [1] => http://google.com/file.jpg
    [2] => https://hell.o.wor.ld/test?qwe=qwe
    [3] => http://test.test/test
)

Test on eval.in


1
需要进一步测试,但似乎已经可以工作了!非常感谢! :) 现在我将使用你的模式来查找所有链接并检查它们是否为文件,整个想法是找到所有文件链接,但现在有些网站喜欢使用漂亮的链接,如test.com/superfile,没有扩展名,所以这段代码可以帮助我很多 :) - swamprunner7
1
欢迎,很高兴能帮到您 @swamprunner7 - Jonny 5
太好了,但如果我需要在<a>标签中查找链接,该怎么办? - Luis Alfredo Serrano Díaz
1
这对我来说比 https://dev59.com/cXNA5IYBdhLWcg3wkuzO#5690614 更有效。 - aubreypwd

7
function turnUrlIntoHyperlink($string){
    //The Regular Expression filter
    $reg_exUrl = "/(?i)\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/";

    // Check if there is a url in the text
    if(preg_match_all($reg_exUrl, $string, $url)) {

        // Loop through all matches
        foreach($url[0] as $newLinks){
            if(strstr( $newLinks, ":" ) === false){
                $link = 'http://'.$newLinks;
            }else{
                $link = $newLinks;
            }

            // Create Search and Replace strings
            $search  = $newLinks;
            $replace = '<a href="'.$link.'" title="'.$newLinks.'" target="_blank">'.$link.'</a>';
            $string = str_replace($search, $replace, $string);
        }
    }

    //Return result
    return $string;
}

1
很好,这个方法可以捕获没有http/https开头的URL,但我认为如果同一个URL在文本中出现多次,这个方法将无法正常工作。 - smoyth

2

转换URL为超链接的函数

function turnUrlIntoHyperlink($string) { // 正则表达式过滤器 $reg_exUrl = "/(http|https|ftp|ftps)://[a-zA-Z0-9-.]+.[a-zA-Z]{2,3}(/\S*)?/";

// Check if there is a url in the text
if (preg_match($reg_exUrl, $string, $url)) {
    // make the urls hyper links
    echo preg_replace($reg_exUrl, "<a target='_blank' href='{$url[0]}'>{$url[0]}</a>", $string);
} else {
    // if no urls in the text just return the text
    echo $string;
}

}


2
<?php

// The Regular Expression filter
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";

// The Text you want to filter for urls
$text = "The text you want to filter goes here. http://google.com";

// Check if there is a url in the text
if(preg_match($reg_exUrl, $text, $url)) {

       // make the urls hyper links
       echo preg_replace($reg_exUrl, "<a href="{$url[0]}">{$url[0]}</a> ", $text);

} else {

       // if no urls in the text just return the text
       echo $text;

}
?>

Reference:http://css-tricks.com/snippets/php/find-urls-in-text-make-links/


这篇文章有一条评论显示了上述代码中的一些错误,这些错误很容易修复。只要文本中只有一个URL,代码就能够完美运行。但是如果你添加另一个URL,它会简单地重复第一个URL。 - wordman

2

非常好用,建议使用。

$str= "Test text http://hello.world";
preg_match_all('/\b(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)[-A-Z0-9+&@#\/%=~_|$?!:,.]*[A-Z0-9+&@#\/%=~_|$]/i', $str, $result, PREG_PATTERN_ORDER);
print_r($result[0]);

你为什么选择不捕获HTTP? - Johnny
它被用作可选项 - https? - s? 可以设置或不设置。 - Vladimir

2
建议的答案很好,但其中一个遗漏了www。的情况,另一个则遗漏了http://
因此,让我们将它们结合起来:
$text = Test text http://hello.world Test text 
http://google.com/file.jpg Test text https://hell.o.wor.ld/test?qwe=qwe Test text 
test text http://test.test/test

preg_match_all('/(((http|https|ftp|ftps)\:\/\/)|(www\.))[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\:[0-9]+)?(\/\S*)?/', $text, $results, PREG_PATTERN_ORDER);

print_r($results[0]);

PREG_PATTERN_ORDER 的返回值是数组的数组 (results),因此 $results[0] 是包含完整匹配模式的数组,$results[1] 是由第一个括号分组子模式匹配的字符串数组,以此类推。


1

如果要将URL转换为标签,并识别没有http/https的URL,请尝试以下方法。它使用preg_replace_callback来避免其他答案中出现同一URL多次的问题:

  private function convertUrls($string) {
    $url_pattern = '/(((http|https)\:\/\/)|(www\.))[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(\:[0-9]+)?(\/\S*)?/';
    return preg_replace_callback($url_pattern,
      function($matches) {
        $match = $matches[0];
        if (strstr($match, ":") === false) {
          $url = "https://$match";
        } else {
          $url = $match;
        }
        return '<a href="' . $url .'" target="_blank">' . $url . '</a>';
      },
      $string);
  }

非常完美地工作,无论链接是以 http 还是 www 开头,而且无论文本中链接出现了多少次。非常感谢! - Kida

0

正则表达式的替代方案是使用

它的效果非常好,但对于非常复杂的代码可能不太适用。

foreach($html->find('a') as $element) 
       echo $element->href . '<br>';

易于使用。无需正则表达式技能:-)


2
没有HTML代码,因此没有要解析的a标签。 - HamZa

0
不是正则表达式,但它可以找到所有内容,并确保它们尚未包含在标记中。它还检查链接是否被括在(),[],“”或任何其他具有开放和关闭的内容中。
$txt = "Test text http://hello.world Test text 
http://google.com/file.jpg Test text https://hell.o.wor.ld/test?qwe=qwe Test text 
test text http://test.test/test <a href=\"http://example.com\">I am already linked up</a>
It was also done in 1927 (http://test.com/reference) Also check this out:http://test/index&t=27";
$holder = explode("http",$txt);
for($i = 1; $i < (count($holder));$i++) {
    if (substr($holder[$i-1],-6) != 'href="') { // this means that the link is not alread in an a tag.
        if (strpos($holder[$i]," ")!==false) //if the link is not the last item in the text block, stop at the first space
            $href = substr($holder[$i],0,strpos($holder[$i]," "));
        else                                //else it is the last item, take it
            $href = $holder[$i];
        if (ctype_punct(substr($holder[$i-1],strlen($holder[$i-1])-1)) && ctype_punct(substr($holder[$i],strlen($holder[$i])-1)))
            $href = substr($href,0,-1);     //if both the fron and back of the link are encapsulated in punctuation, truncate the link by one
        $holder[$i] = implode("$href\" target=\"_blank\" class=\"link\">http$href</a>",explode($href,$holder[$i]));
        $holder[$i-1] .= "<a href=\"";
    }
}
$txt = implode("http",$holder);

echo $txt;

输出:

Test text <a href="http://hello.world" target="_blank" class="link">http://hello.world</a> Test text 
<a href="http://google.com/file.jpg" target="_blank" class="link">http://google.com/file.jpg</a> Test text <a href="https://hell.o.wor.ld/test?qwe=qwe" target="_blank" class="link">https://hell.o.wor.ld/test?qwe=qwe</a> Test text 
test text <a href="http://test.test/test" target="_blank" class="link">http://test.test/test</a> <a href="http://example.com">I am already linked up</a>
It was also done in 1927 (<a href="http://test.com/reference" target="_blank" class="link">http://test.com/reference</a>) Also check this out:<a href="http://test/index&amp;t=27" target="_blank" class="link">http://test/index&amp;t=27</a>

-1
我使用这个函数。
  <?php
    function deteli($string){
        $pos  = strpos($string, 'http');
        $spos = strpos($string, ' ', $pos);
        $lst  = $spos - $pos;
        $bef  = substr($string, 0, $pos);
        $aft  = substr($string, $spos);
        if ($pos == true || $pos == 0) {
            $link = substr($string, $pos, $lst);
            $res  =  $bef . "<a href='" . $link . "' class='link' target='_blank'>link</a>" . $aft . ""; 
            return  $res;
        }
        else{
            return $string;
        }
    }?>

1
欢迎来到 Stack Overflow!回答应该包含更多的内容,而不仅仅是代码。如果您认为问题提出得不好,可以标记它或发表评论。 - RaminS

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