Подтвердить что ты не робот

PHP: Как использовать данные API Twitter для преобразования URL-адресов, упоминаний и хастагов в твитах на ссылки?

Я очень зациклен на том, как 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
}

Интересными частями для этого вопроса являются элемент text и записи в массивах hashtags, user_mentions и urls. Twitter сообщает нам, где в элементе text появляются гастаги, упоминания и URL-адреса с массивами indices... так вот суть вопроса:

Как вы используете те indices массивы?

Вы не можете просто использовать их прямо, перебирая каждый элемент ссылки с чем-то вроде substr_replace, так как замена первого элемента ссылки в text приведет к аннулированию всех значений индекса для последующих элементов ссылки. Вы также не можете использовать substr_replace массив, поскольку он работает только тогда, когда вы даете ему массив строк для первого arg, а не одну строку (I 've проверил это. Результаты... странные).

Есть ли какая-нибудь функция, которая может одновременно заменить несколько подстрочных индексированных подстрок в одной строке с разными строками замены?

4b9b3361

Ответ 1

Все, что вам нужно сделать, чтобы использовать индексы 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);
}


?>

Ответ 2

Хорошо, поэтому мне нужно было сделать именно это, и я решил это. Вот функция, которую я написал. 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;
    }
}

Ответ 3

Это краевой случай, но использование str_replace() в ответе Styledev может вызвать проблемы, если один объект содержится внутри другого. Например, "Я гений! #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 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);

}
?>  

Ответ 4

Решение 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.
}

Ответ 5

Вот обновленный ответ, который работает с новым расширенным режимом Twitter. Он сочетает в себе ответ на @vita10gy и комментарий @Hugo (чтобы сделать его совместимым с utf8), с небольшими небольшими изменениями для работы с новыми значениями api.

function utf8_substr_replace($original, $replacement, $position, $length) {
    $startString = mb_substr($original, 0, $position, "UTF-8");
    $endString = mb_substr($original, $position + $length, mb_strlen($original), "UTF-8");
    $out = $startString . $replacement . $endString;
    return $out;
}

function json_tweet_text_to_HTML($tweet, $links=true, $users=true, $hashtags=true) {
    // Media urls can show up on the end of the full_text tweet, but twitter doesn't index that url. 
    // The display_text_range indexes show the actual tweet text length.
    // Cut the string off at the end to get rid of this unindexed url.
    $return = mb_substr($tweet->full_text, $tweet->display_text_range[0],$tweet->display_text_range[1]);
    $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 =  utf8_substr_replace($return, $item["replacement"], $item["start"], $item["end"] - $item["start"]);
    }

    return($return);
}

Ответ 6

Вот версия JavaScript (с использованием jQuery) решения vita10gy

function tweetTextToHtml(tweet, links, users, hashtags) {

    if (typeof(links)==='undefined') { links = true; }
    if (typeof(users)==='undefined') { users = true; }
    if (typeof(hashtags)==='undefined') { hashtags = true; }

    var returnStr = tweet.text;
    var entitiesArray = [];

    if(links && tweet.entities.urls.length > 0) {
        jQuery.each(tweet.entities.urls, function() {
            var temp1 = {};
            temp1.start = this.indices[0];
            temp1.end = this.indices[1];
            temp1.replacement = '<a href="' + this.expanded_url + '" target="_blank">' + this.display_url + '</a>';
            entitiesArray.push(temp1);
        });
    }

    if(users && tweet.entities.user_mentions.length > 0) {
        jQuery.each(tweet.entities.user_mentions, function() {
            var temp2 = {};
            temp2.start = this.indices[0];
            temp2.end = this.indices[1];
            temp2.replacement = '<a href="https://twitter.com/' + this.screen_name + '" target="_blank">@' + this.screen_name + '</a>';
            entitiesArray.push(temp2);
        });
    }

    if(hashtags && tweet.entities.hashtags.length > 0) {
        jQuery.each(tweet.entities.hashtags, function() {
            var temp3 = {};
            temp3.start = this.indices[0];
            temp3.end = this.indices[1];
            temp3.replacement = '<a href="https://twitter.com/hashtag/' + this.text + '?src=hash" target="_blank">#' + this.text + '</a>';
            entitiesArray.push(temp3);
        });
    }

    entitiesArray.sort(function(a, b) {return b.start - a.start;});

    jQuery.each(entitiesArray, function() {
        returnStr = substrReplace(returnStr, this.replacement, this.start, this.end - this.start);
    });

    return returnStr;
}

Затем вы можете использовать эту функцию так...

for(var i in tweetsJsonObj) {
    var tweet = tweetsJsonObj[i];
    var htmlTweetText = tweetTextToHtml(tweet);

    // Do something with the formatted tweet here ...
}

Ответ 7

Что касается vita10gy help json_tweet_text_to_HTML(), я нашел твит, что он не мог правильно отформатировать: 626125868247552000.

В этом твит есть неразрывное пространство. Моим решением было заменить первую строку функции следующим:

$return = str_replace("\xC2\xA0", ' ', $tweet->text);

Выполнение str_replace() на &nbsp; покрывается здесь.