PHP:如何使用Twitter API的数据将推文中的URL,提及和hastags转换为链接?

cor*_*ror 13 php api twitter

我真的很难理解Twitter如何期望其API用户将其发送的明文推文转换为正确链接的HTML.

这是交易:当您请求推文的详细数据时,Twitter的JSON API会发回这组信息:

{
    "created_at":"Wed Jul 18 01:03:31 +0000 2012",
    "id":225395341250412544,
    "id_str":"225395341250412544",
    "text":"This is a test tweet. #boring @nbc http://t.co/LUfDreY6 #skronk @crux http://t.co/VpuMlaDs @twitter",
    "source":"web",
    "truncated":false,
    "in_reply_to_status_id":null,
    "in_reply_to_status_id_str":null,
    "in_reply_to_user_id":null,
    "in_reply_to_user_id_str":null,
    "in_reply_to_screen_name":null,
    "user": <REDACTED>,
    "geo":null,
    "coordinates":null,
    "place":null,
    "contributors":null,
    "retweet_count":0,
    "entities":{
        "hashtags":[
            {
                "text":"boring",
                "indices":[22,29]
            },
            {
                "text":"skronk",
                "indices":[56,63]
            }
        ],
        "urls":[
            {
                "url":"http://t.co/LUfDreY6",
                "expanded_url":"http://www.twitter.com",
                "display_url":"twitter.com",
                "indices":[35,55]
            },
            {
                "url":"http://t.co/VpuMlaDs",
                "expanded_url":"http://www.example.com",
                "display_url":"example.com",
                "indices":[70,90]
            }
        ],
        "user_mentions":[
            {
                "screen_name":"nbc",
                "name":"NBC",
                "id":26585095,
                "id_str":"26585095",
                "indices":[30,34]
            },
            {
                "screen_name":"crux",
                "name":"Z. D. Smith",
                "id":407213,
                "id_str":"407213",
                "indices":[64,69]
            },
            {
                "screen_name":"twitter",
                "name":"Twitter",
                "id":783214,
                "id_str":"783214",
                "indices":[91,99]
            }
        ]
    },
    "favorited":false,
    "retweeted":false,
    "possibly_sensitive":false
}
Run Code Online (Sandbox Code Playgroud)

有趣的部分,对于这个问题,是text元素和条目hashtags,user_mentions以及urls阵列.Twitter正在告诉我们在text元素中,indices数组中出现了hastags,mentions和url ......所以这里是问题的症结所在:

你如何使用这些indices阵列?

你不能只是通过循环遍历每个链接元素来直接使用它们substr_replace,因为替换中的第一个链接元素text会使后续链接元素的所有索引值无效.你也不能使用substr_replace数组功能,因为只有当你为第一个arg而不是单个字符串给它一个字符串数组时它才有效(我已经测试了这个.结果是......奇怪的).

是否有一些函数可以在具有不同替换字符串的单个字符串中同时替换多个索引分隔的子字符串?

vit*_*0gy 16

所有你需要做的就是使用twitter提供直接的简单替换是收集你想要做的替换,然后向后排序.你可能会找到一种更聪明的方法来构建$实体,我还是希望它们是可选的,所以我尽可能地吻.

无论哪种方式,我的观点只是为了表明你不需要爆炸字符串和字符数等等.无论你如何做,你需要做的只是从最后开始并工作到字符串的开头,而twitter的索引仍然有效.

<?php 

function json_tweet_text_to_HTML($tweet, $links=true, $users=true, $hashtags=true)
{
    $return = $tweet->text;

    $entities = array();

    if($links && is_array($tweet->entities->urls))
    {
        foreach($tweet->entities->urls as $e)
        {
            $temp["start"] = $e->indices[0];
            $temp["end"] = $e->indices[1];
            $temp["replacement"] = "<a href='".$e->expanded_url."' target='_blank'>".$e->display_url."</a>";
            $entities[] = $temp;
        }
    }
    if($users && is_array($tweet->entities->user_mentions))
    {
        foreach($tweet->entities->user_mentions as $e)
        {
            $temp["start"] = $e->indices[0];
            $temp["end"] = $e->indices[1];
            $temp["replacement"] = "<a href='https://twitter.com/".$e->screen_name."' target='_blank'>@".$e->screen_name."</a>";
            $entities[] = $temp;
        }
    }
    if($hashtags && is_array($tweet->entities->hashtags))
    {
        foreach($tweet->entities->hashtags as $e)
        {
            $temp["start"] = $e->indices[0];
            $temp["end"] = $e->indices[1];
            $temp["replacement"] = "<a href='https://twitter.com/hashtag/".$e->text."?src=hash' target='_blank'>#".$e->text."</a>";
            $entities[] = $temp;
        }
    }

    usort($entities, function($a,$b){return($b["start"]-$a["start"]);});


    foreach($entities as $item)
    {
        $return = substr_replace($return, $item["replacement"], $item["start"], $item["end"] - $item["start"]);
    }

    return($return);
}


?>
Run Code Online (Sandbox Code Playgroud)

  • **警告:**`substr_replace`不是多字节(utf-8)兼容的.带有(在我的情况下)重音字符的字符串将导致错误的索引! (5认同)
  • @MbengueAssane尝试[`utf8_substr_replace`](https://shkspr.mobi/blog/2012/09/a-utf-8-aware-substr_replace-for-use-in-app-net/). (3认同)

Sty*_*dev 13

好的,所以我需要做到这一点,我解决了它.这是我写的功能.https://gist.github.com/3337428

function parse_message( &$tweet ) {
    if ( !empty($tweet['entities']) ) {
        $replace_index = array();
        $append = array();
        $text = $tweet['text'];
        foreach ($tweet['entities'] as $area => $items) {
            $prefix = false;
            $display = false;
            switch ( $area ) {
                case 'hashtags':
                    $find   = 'text';
                    $prefix = '#';
                    $url    = 'https://twitter.com/search/?src=hash&q=%23';
                    break;
                case 'user_mentions':
                    $find   = 'screen_name';
                    $prefix = '@';
                    $url    = 'https://twitter.com/';
                    break;
                case 'media':
                    $display = 'media_url_https';
                    $href    = 'media_url_https';
                    $size    = 'small';
                    break;
                case 'urls':
                    $find    = 'url';
                    $display = 'display_url';
                    $url     = "expanded_url";
                    break;
                default: break;
            }
            foreach ($items as $item) {
                if ( $area == 'media' ) {
                    // We can display images at the end of the tweet but sizing needs to added all the way to the top.
                    // $append[$item->$display] = "<img src=\"{$item->$href}:$size\" />";
                }else{
                    $msg     = $display ? $prefix.$item->$display : $prefix.$item->$find;
                    $replace = $prefix.$item->$find;
                    $href    = isset($item->$url) ? $item->$url : $url;
                    if (!(strpos($href, 'http') === 0)) $href = "http://".$href;
                    if ( $prefix ) $href .= $item->$find;
                    $with = "<a href=\"$href\">$msg</a>";
                    $replace_index[$replace] = $with;
                }
            }
        }
        foreach ($replace_index as $replace => $with) $tweet['text'] = str_replace($replace,$with,$tweet['text']);
        foreach ($append as $add) $tweet['text'] .= $add;
    }
}
Run Code Online (Sandbox Code Playgroud)


Jef*_*ens 7

这是一个边缘情况,但如果一个实体包含在另一个实体中,则在Styledev的答案中使用str_replace()可能会导致问题.例如,"我是天才!#me #mensa"可能会成为"我是天才!#me #me nsa"如果首先替换较短的实体.

该解决方案避免了这个问题:

<?php
/**
 * Hyperlinks hashtags, twitter names, and urls within the text of a tweet
 * 
 * @param object $apiResponseTweetObject A json_decoded() one of these: https://dev.twitter.com/docs/platform-objects/tweets
 * @return string The tweet's text with hyperlinks added
 */
function linkEntitiesWithinText($apiResponseTweetObject) {

    // Convert tweet text to array of one-character strings
    // $characters = str_split($apiResponseTweetObject->text);
    $characters = preg_split('//u', $apiResponseTweetObject->text, null, PREG_SPLIT_NO_EMPTY);

    // Insert starting and closing link tags at indices...

    // ... for @user_mentions
    foreach ($apiResponseTweetObject->entities->user_mentions as $entity) {
        $link = "https://twitter.com/" . $entity->screen_name;          
        $characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
        $characters[$entity->indices[1] - 1] .= "</a>";         
    }               

    // ... for #hashtags
    foreach ($apiResponseTweetObject->entities->hashtags as $entity) {
        $link = "https://twitter.com/search?q=%23" . $entity->text;         
        $characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
        $characters[$entity->indices[1] - 1] .= "</a>";         
    }

    // ... for http://urls
    foreach ($apiResponseTweetObject->entities->urls as $entity) {
        $link = $entity->expanded_url;          
        $characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
        $characters[$entity->indices[1] - 1] .= "</a>";         
    }

    // ... for media
    foreach ($apiResponseTweetObject->entities->media as $entity) {
        $link = $entity->expanded_url;          
        $characters[$entity->indices[0]] = "<a href=\"$link\">" . $characters[$entity->indices[0]];
        $characters[$entity->indices[1] - 1] .= "</a>";         
    }

    // Convert array back to string
    return implode('', $characters);

}
?>  
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚在foreach循环之前添加了一个if(is_array($ apiResponseTweetObject-> entities-> media)){ (3认同)

vit*_*oft 6

Jeff的解决方案与英文文本配合得很好,但是当推文包含非ASCII字符时,它就被打破了.该解决方案避免了这个问题:

mb_internal_encoding("UTF-8");

// Return hyperlinked tweet text from json_decoded status object:
function MakeStatusLinks($status) 
{$TextLength=mb_strlen($status['text']); // Number of UTF-8 characters in plain tweet.
 for ($i=0;$i<$TextLength;$i++)
 {$ch=mb_substr($status['text'],$i,1); if ($ch<>"\n") $ChAr[]=$ch; else $ChAr[]="\n<br/>"; // Keep new lines in HTML tweet.
 }
if (isset($status['entities']['user_mentions']))
 foreach ($status['entities']['user_mentions'] as $entity)
 {$ChAr[$entity['indices'][0]] = "<a href='https://twitter.com/".$entity['screen_name']."'>".$ChAr[$entity['indices'][0]];
  $ChAr[$entity['indices'][1]-1].="</a>";
 }
if (isset($status['entities']['hashtags']))
 foreach ($status['entities']['hashtags'] as $entity)
 {$ChAr[$entity['indices'][0]] = "<a href='https://twitter.com/search?q=%23".$entity['text']."'>".$ChAr[$entity['indices'][0]];
  $ChAr[$entity['indices'][1]-1] .= "</a>";
 }
if (isset($status['entities']['urls']))
 foreach ($status['entities']['urls'] as $entity)
 {$ChAr[$entity['indices'][0]] = "<a href='".$entity['expanded_url']."'>".$entity['display_url']."</a>";
  for ($i=$entity['indices'][0]+1;$i<$entity['indices'][1];$i++) $ChAr[$i]='';
 }
if (isset($status['entities']['media']))
 foreach ($status['entities']['media'] as $entity)
 {$ChAr[$entity['indices'][0]] = "<a href='".$entity['expanded_url']."'>".$entity['display_url']."</a>";
  for ($i=$entity['indices'][0]+1;$i<$entity['indices'][1];$i++) $ChAr[$i]='';
 }
return implode('', $ChAr); // HTML tweet.
}
Run Code Online (Sandbox Code Playgroud)