【问题标题】:PHP - find all hyperlinks in a post, add target and rel=nofollow attributePHP - 查找帖子中的所有超链接,添加目标和 rel=nofollow 属性
【发布时间】:2015-09-21 01:21:52
【问题描述】:

我需要找到一种方法来阅读用户发布的内容,以查找可能包含的任何超链接、创建锚标记、为所有这些链接添加 target 和 rel=nofollow 属性。

我遇到过一些这样的 REGEX 解决方案:

 (?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

但在 SO 上关于同一问题的其他问题上,强烈建议 NOT 使用 REGEX 而不是使用 PHP 的 DOMDocument

无论是最好的方法,我都需要添加一些上面提到的属性来强化网站上的所有外部链接。

【问题讨论】:

    标签: php hyperlink attributes nofollow


    【解决方案1】:

    首先,您提到的指南建议不要使用正则表达式解析 HTML。据我了解,您要做的是解析来自用户的纯文本并将其转换为 HTML。为此,正则表达式通常就可以了。

    (请注意,我假设您自己将文本解析为链接,并且没有为此使用外部库。在后一种情况下,您需要修复库输出的 HTML,为此您应该 使用DOMDocument 遍历所有&lt;a&gt; 标签并为其添加适当的属性。)

    现在,您可以通过两种方式解析它:服务器端或客户端。

    服务器端

    优点:

    • 它输出准备好使用的 HTML。
    • 它不需要用户启用 Javascript。

    缺点:

    • 您需要添加rel="nofollow" 属性以使机器人不跟随链接。

    客户端

    优点:

    • 您不需要为机器人添加 rel="nofollow" 属性,因为它们首先看不到链接 - 它们是使用 Javascript 生成的,而机器人通常不解析 Javascript。

    缺点:

    • 以这种方式创建链接需要用户启用 Javascript。
    • 在 Javascript 中实现类似的东西会让人觉得网站速度很慢,尤其是在需要解析大量文本的情况下。
    • 这使得缓存解析的文本变得困难。

    我将专注于在服务器端实现它。

    服务器端实现

    所以,为了从用户输入中解析链接并添加任何你想要的属性,你可以使用这样的东西:

    <?php
    function replaceLinks($text)
    {
        $regex = '/'
          . '(?<!\S)'
          . '(((ftp|https?)?:?)\/\/|www\.)'
          . '(\S+?)'
          . '(?=$|\s|[,]|\.\W|\.$)'
          . '/m';
    
        return preg_replace_callback($regex, function($match)
        {
            return '<a'
              . ' target=""'
              . ' rel="nofollow"'
              . ' href="' . $match[0] . '">'
              . $match[0]
              . '</a>';
        }, $text);
    }
    

    解释:

    • (?&lt;!\S): 前面没有非空白字符。
    • (((ftp|https?)?:?)\/\/|www\.):接受 ftp://http://https://:////www. 作为 URL 的开头。
    • (\S+?) 以非贪婪方式匹配所有非空格。
    • (?=$|\s|[,]|\.\W|\.$) 每个 URL 必须跟在行尾、空格、逗号、点后跟 \w 以外的字符(这是为了让 .com.co.jp 等匹配)或点然后是行尾。
    • m 标志 - 匹配多行文本。

    测试

    现在,为了支持我的说法,我添加了一些测试用例:

    $tests = [];
    $tests []= ['http://example.com', '<a target="" rel="nofollow" href="http://example.com">http://example.com</a>'];
    $tests []= ['https://example.com', '<a target="" rel="nofollow" href="https://example.com">https://example.com</a>'];
    $tests []= ['ftp://example.com', '<a target="" rel="nofollow" href="ftp://example.com">ftp://example.com</a>'];
    $tests []= ['://example.com', '<a target="" rel="nofollow" href="://example.com">://example.com</a>'];
    $tests []= ['//example.com', '<a target="" rel="nofollow" href="//example.com">//example.com</a>'];
    $tests []= ['www.example.com', '<a target="" rel="nofollow" href="www.example.com">www.example.com</a>'];
    $tests []= ['user@www.example.com', 'user@www.example.com'];
    $tests []= ['testhttp://example.com', 'testhttp://example.com'];
    $tests []= ['example.com', 'example.com'];
    $tests []= [
        'test http://example.com',
        'test <a target="" rel="nofollow" href="http://example.com">http://example.com</a>'];
    $tests []= [
        'multiline' . PHP_EOL . 'blah http://example.com' . PHP_EOL . 'test',
        'multiline' . PHP_EOL . 'blah <a target="" rel="nofollow" href="http://example.com">http://example.com</a>' . PHP_EOL . 'test'];
    $tests []= [
        'text //example.com/slashes.php?parameters#fragment, some other text',
        'text <a target="" rel="nofollow" href="//example.com/slashes.php?parameters#fragment">//example.com/slashes.php?parameters#fragment</a>, some other text'];
    $tests []= [
        'text //example.com. new sentence',
        'text <a target="" rel="nofollow" href="//example.com">//example.com</a>. new sentence'];
    

    每个测试用例由两部分组成:源输入和预期输出。我使用以下代码来确定函数是否通过上述测试:

    foreach ($tests as $test)
    {
        list ($source, $expected) = $test;
        $actual = replaceLinks($source);
        if ($actual != $expected)
        {
            echo 'Test ' . $source . ' failed.' . PHP_EOL;
            echo 'Expected: ' . $expected . PHP_EOL;
            echo 'Actual:   ' . $actual . PHP_EOL;
            die;
        }
    }
    echo 'All tests passed' . PHP_EOL;
    

    我认为这可以让您了解如何解决问题。随意添加更多测试并试验正则表达式本身,使其适合您的特定需求。

    【讨论】:

      【解决方案2】:

      您可能对Goutte 感兴趣 您可以定义自己的过滤器等。

      【讨论】:

        【解决方案3】:

        使用 jquery 获取要发布的内容并在将其发布到 PHP 之前对其进行处理。

        $('#idof_content').val(
          $('#idof_content').val().replace(/\b(http(s|):\/\/|)(www\.\S+)/ig,
            "<a href='http\$2://\$3' target='_blank' rel='nofollow'>\$3</a>"));
        

        【讨论】:

        • 这将如何排除我网站内部链接的请求属性?请问这是否会提取所有类型的链接,http 或 https,www 或非 www
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 2012-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多