2012-07-18 13 views
12

Sono davvero incerto su come Twitter si aspetta che gli utenti della sua API convertano i tweet in chiaro che invia all'HTML correttamente collegato.PHP: Come utilizzare i dati dell'API di Twitter per convertire URL, menzioni e hastags nei tweet ai collegamenti?

Ecco il punto: JSON API di Twitter invia questo insieme di informazioni di nuovo quando si richiedono i dati dettagliati per un tweet:

{ 
    "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 
} 

Le parti più interessanti, per questa domanda, sono l'elemento text e le voci gli array hashtags, user_mentions e urls. Twitter sta dicendo noi dove nell'elemento text i hastags, menzioni, e gli URL vengono visualizzati con le indices array ... quindi ecco il nocciolo della questione:

Come usa quei indices array?

Non è possibile utilizzarli semplicemente eseguendo il ciclo su ciascun elemento di collegamento con qualcosa come substr_replace, poiché la sostituzione del primo elemento di collegamento nello text invaliderà tutti i valori di indice per gli elementi di collegamento successivi. Non è inoltre possibile utilizzare la funzionalità dell'array di substr_replace, poiché funziona solo quando gli si assegna una serie di stringhe per il primo arg, piuttosto che una singola stringa (l'ho provato. I risultati sono ... strani).

Esiste qualche funzione che possa sostituire simultaneamente più sottostringhe delimitate da un indice in una singola stringa con stringhe di sostituzione diverse?

risposta

15

Tutto quello che dovete fare per utilizzare gli indici Twitter fornisce verso l'alto con una semplice sostituzione è raccogliere le sostituzioni che si desidera fare e poi ordinarli all'indietro. Probabilmente puoi trovare un modo più intelligente per costruire $ entità, volevo che fossero opzionali in ogni caso, quindi mi azzardai a farlo.

In entrambi i casi, il mio punto qui era solo per mostrare che non è necessario esplodere la stringa e il numero di caratteri e quant'altro. Indipendentemente da come lo fai, tutto ciò che devi fare è iniziare alla fine e lavorare all'inizio della stringa, e l'indice twitter è ancora valido.

<?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); 
} 


?> 
+1

* facepalm * ... Perché non ho pensato che *** *** – CoreDumpError

+0

Funziona per me ... Ho aggiunto un ulteriore bit di codice in alto, come quando si recuperano i retweet che a volte contengono ellissi. Twitter ti consiglia di utilizzare il retweet_status e le sue entità in tali circostanze: https://dev.twitter.com/overview/api/entities-in-twitter-objects#retweets – diggersworld

+0

Wow, ottimo approccio, non ci avrei pensato. .. –

13

Ok, quindi ho dovuto fare esattamente questo e l'ho risolto. Ecco la funzione che ho scritto. 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; 
    } 
} 
+1

Questa è una buona implementazione di base, ma ha bug. Mette display_url invece di URL espanso come href (collegamenti interrotti con "...") e fa alcune strane sostituzioni per hashtag e @screen_names. – Noam

+0

L'ho modificato per funzionare in modo più coerente con l'API corrente. – Styledev

+0

roba utile .. :) – mithunsatheesh

7

Si tratta di un caso limite, ma l'uso di str_replace() in risposta di Styledev potrebbe causare problemi se un'entità è contenuto all'interno di un altro. Ad esempio, "I'm a genius! #me #mensa" potrebbe diventare "Sono un genio! #me#me nsa" se l'entità più breve viene sostituita per prima.

Questa soluzione evita questo problema:

<?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); 

} 
?> 
+0

Ho ricevuto questo errore: Avviso: Proprietà non definita: stdClass :: $ text in /Users/#/Sites/twitter/test.php on line 9 – intelis

+0

@intelis: hai json_decode() il Tweet oggetto? –

+0

Sì, e stava funzionando, ma stava lasciando fuori parte delle twitts :( – intelis

6

soluzione di Jeff lavorato bene con il testo inglese, ma si è rotto quando il tweet contiene caratteri non ASCII. Questa soluzione evita questo problema:

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. 
} 
+0

Buona cattura. Ho riscontrato questo problema anche. Ho aggiornato la mia risposta con la soluzione che sto usando.Specifica la stringa correttamente quando sono coinvolti caratteri multibyte –

0

Ecco una versione JavaScript (utilizzando jQuery) di soluzione vita10gy s'

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; 
} 

È quindi possibile utilizzare questa funzione in questo modo ...

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

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

Per quanto riguarda di utile json_tweet_text_to_HTML(), ho trovato un tweet che non poteva formattare correttamente vita10gy: 626125868247552000.

questo tweet ha uno spazio unificatore in esso. La mia soluzione era quella di sostituire la prima riga della funzione con il seguente:

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

Esecuzione di un str_replace() su &nbsp; è coperto here.

1

Ecco una risposta aggiornata che funziona con la nuova modalità estesa di Twitter. Combina la risposta di @ vita10gy e il commento di @Hugo (per renderlo utf8 compatibile), con alcune piccole modifiche da utilizzare con i nuovi valori 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); 
} 
Problemi correlati