Exploring the Twitter trends API

In this notebook we'll take a quick look at the section of the Twitter Rest API that deals with trending terms:

We'll work with the Tweepy library's support for these, explore the calls, and sketch out some possibilities.

Setup

First some library imports and user key setup. This assumes you've arranged for the necessary app keys at apps.twitter.com.


In [74]:
from collections import Counter
import os
from pprint import pprint
import tweepy

In [58]:
c_key = os.environ['CONSUMER_KEY']
c_secret = os.environ['CONSUMER_SECRET']
a_token = os.environ['ACCESS_TOKEN']
a_token_secret = os.environ['ACCESS_TOKEN_SECRET']

auth = tweepy.OAuthHandler(c_key, c_secret)
auth.set_access_token(a_token, a_token_secret)

api = tweepy.API(auth)

Basic calls

The first call trends/available returns a set of locations, each with its own set of parameters.


In [59]:
trends_available = api.trends_available()

In [60]:
len(trends_available)


Out[60]:
467

In [61]:
trends_available[0:3]


Out[61]:
[{'country': '',
  'countryCode': None,
  'name': 'Worldwide',
  'parentid': 0,
  'placeType': {'code': 19, 'name': 'Supername'},
  'url': 'http://where.yahooapis.com/v1/place/1',
  'woeid': 1},
 {'country': 'Canada',
  'countryCode': 'CA',
  'name': 'Winnipeg',
  'parentid': 23424775,
  'placeType': {'code': 7, 'name': 'Town'},
  'url': 'http://where.yahooapis.com/v1/place/2972',
  'woeid': 2972},
 {'country': 'Canada',
  'countryCode': 'CA',
  'name': 'Ottawa',
  'parentid': 23424775,
  'placeType': {'code': 7, 'name': 'Town'},
  'url': 'http://where.yahooapis.com/v1/place/3369',
  'woeid': 3369}]

We see from this (and from their docs) that Twitter uses Yahoo WOEIDs to define place names. How they arrived at this list of 467 places is not clear. It's a mix of countries and cities, at least:


In [62]:
sorted([t['name'] for t in trends_available])[:25]


Out[62]:
['Abu Dhabi',
 'Acapulco',
 'Accra',
 'Adana',
 'Adelaide',
 'Aguascalientes',
 'Ahmedabad',
 'Ahsa',
 'Albuquerque',
 'Alexandria',
 'Algeria',
 'Algiers',
 'Amman',
 'Amritsar',
 'Amsterdam',
 'Ankara',
 'Ansan',
 'Antalya',
 'Antipolo',
 'Argentina',
 'Athens',
 'Atlanta',
 'Auckland',
 'Austin',
 'Australia']

Hmm, let's see exactly what classification levels they have in this set:


In [76]:
c = Counter([t['placeType']['name'] for t in trends_available])

In [77]:
c


Out[77]:
Counter({'Country': 62, 'Supername': 1, 'Town': 402, 'Unknown': 2})

'Unknown'??


In [78]:
unk = [t for t in trends_available if t['placeType']['name'] == 'Unknown']

In [79]:
unk


Out[79]:
[{'country': 'South Africa',
  'countryCode': 'ZA',
  'name': 'Soweto',
  'parentid': 23424942,
  'placeType': {'code': 22, 'name': 'Unknown'},
  'url': 'http://where.yahooapis.com/v1/place/1587677',
  'woeid': 1587677},
 {'country': 'Saudi Arabia',
  'countryCode': 'SA',
  'name': 'Ahsa',
  'parentid': 23424938,
  'placeType': {'code': 9, 'name': 'Unknown'},
  'url': 'http://where.yahooapis.com/v1/place/56120136',
  'woeid': 56120136}]

Ah, Soweto is a township, not a city, and Al Ahsa is a region.

Moving along, we can dig into specifc places one at a time:


In [8]:
ams = [t for t in trends_available if t['name'] == 'Amsterdam'][0]

In [64]:
ams


Out[64]:
{'country': 'Netherlands',
 'countryCode': 'NL',
 'name': 'Amsterdam',
 'parentid': 23424909,
 'placeType': {'code': 7, 'name': 'Town'},
 'url': 'http://where.yahooapis.com/v1/place/727232',
 'woeid': 727232}

In [63]:
trends_ams = api.trends_place(ams['woeid'])

In [65]:
trends_ams


Out[65]:
[{'as_of': '2016-10-17T18:47:54Z',
  'created_at': '2016-10-17T18:45:27Z',
  'locations': [{'name': 'Amsterdam', 'woeid': 727232}],
  'trends': [{'name': '#ShoutoutToMyEx',
    'promoted_content': None,
    'query': '%23ShoutoutToMyEx',
    'tweet_volume': 442950,
    'url': 'http://twitter.com/search?q=%23ShoutoutToMyEx'},
   {'name': '#singleparents',
    'promoted_content': None,
    'query': '%23singleparents',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23singleparents'},
   {'name': '#Mosul',
    'promoted_content': None,
    'query': '%23Mosul',
    'tweet_volume': 69119,
    'url': 'http://twitter.com/search?q=%23Mosul'},
   {'name': '#dating',
    'promoted_content': None,
    'query': '%23dating',
    'tweet_volume': 14363,
    'url': 'http://twitter.com/search?q=%23dating'},
   {'name': '#STOICON16',
    'promoted_content': None,
    'query': '%23STOICON16',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23STOICON16'},
   {'name': 'Find Her',
    'promoted_content': None,
    'query': '%22Find+Her%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22Find+Her%22'},
   {'name': 'Cristiano Ronaldo',
    'promoted_content': None,
    'query': '%22Cristiano+Ronaldo%22',
    'tweet_volume': 15817,
    'url': 'http://twitter.com/search?q=%22Cristiano+Ronaldo%22'},
   {'name': 'Hans Klok',
    'promoted_content': None,
    'query': '%22Hans+Klok%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22Hans+Klok%22'},
   {'name': 'Pierre Bokma',
    'promoted_content': None,
    'query': '%22Pierre+Bokma%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22Pierre+Bokma%22'},
   {'name': 'Felix Rottenberg',
    'promoted_content': None,
    'query': '%22Felix+Rottenberg%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22Felix+Rottenberg%22'},
   {'name': 'Renate Groenewold',
    'promoted_content': None,
    'query': '%22Renate+Groenewold%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22Renate+Groenewold%22'},
   {'name': 'Kookboek',
    'promoted_content': None,
    'query': 'Kookboek',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=Kookboek'},
   {'name': 'VfL Wolfsburg',
    'promoted_content': None,
    'query': '%22VfL+Wolfsburg%22',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%22VfL+Wolfsburg%22'},
   {'name': 'Artis',
    'promoted_content': None,
    'query': 'Artis',
    'tweet_volume': 10527,
    'url': 'http://twitter.com/search?q=Artis'},
   {'name': 'Staat',
    'promoted_content': None,
    'query': 'Staat',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=Staat'},
   {'name': 'Lubach',
    'promoted_content': None,
    'query': 'Lubach',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=Lubach'},
   {'name': 'West',
    'promoted_content': None,
    'query': 'West',
    'tweet_volume': 221194,
    'url': 'http://twitter.com/search?q=West'},
   {'name': 'Amsterdam',
    'promoted_content': None,
    'query': 'Amsterdam',
    'tweet_volume': 31724,
    'url': 'http://twitter.com/search?q=Amsterdam'},
   {'name': '#jajgra',
    'promoted_content': None,
    'query': '%23jajgra',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23jajgra'},
   {'name': '#Hunted',
    'promoted_content': None,
    'query': '%23Hunted',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23Hunted'},
   {'name': '#Asscher',
    'promoted_content': None,
    'query': '%23Asscher',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23Asscher'},
   {'name': '#feyado',
    'promoted_content': None,
    'query': '%23feyado',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23feyado'},
   {'name': '#JOANNE',
    'promoted_content': None,
    'query': '%23JOANNE',
    'tweet_volume': 119376,
    'url': 'http://twitter.com/search?q=%23JOANNE'},
   {'name': '#LIVMUN',
    'promoted_content': None,
    'query': '%23LIVMUN',
    'tweet_volume': 52370,
    'url': 'http://twitter.com/search?q=%23LIVMUN'},
   {'name': '#eenvandaag',
    'promoted_content': None,
    'query': '%23eenvandaag',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23eenvandaag'},
   {'name': '#didd',
    'promoted_content': None,
    'query': '%23didd',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23didd'},
   {'name': '#camhee',
    'promoted_content': None,
    'query': '%23camhee',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23camhee'},
   {'name': '#gtst',
    'promoted_content': None,
    'query': '%23gtst',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23gtst'},
   {'name': '#NACpraat',
    'promoted_content': None,
    'query': '%23NACpraat',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23NACpraat'},
   {'name': '#DPSEPSC',
    'promoted_content': None,
    'query': '%23DPSEPSC',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23DPSEPSC'},
   {'name': '#LFCvMUFC',
    'promoted_content': None,
    'query': '%23LFCvMUFC',
    'tweet_volume': 39619,
    'url': 'http://twitter.com/search?q=%23LFCvMUFC'},
   {'name': '#ThingsISayPrettyOften',
    'promoted_content': None,
    'query': '%23ThingsISayPrettyOften',
    'tweet_volume': 71493,
    'url': 'http://twitter.com/search?q=%23ThingsISayPrettyOften'},
   {'name': '#TEDxMaastricht',
    'promoted_content': None,
    'query': '%23TEDxMaastricht',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23TEDxMaastricht'},
   {'name': '#twezwo',
    'promoted_content': None,
    'query': '%23twezwo',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23twezwo'},
   {'name': '#StudioVoetbal',
    'promoted_content': None,
    'query': '%23StudioVoetbal',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23StudioVoetbal'},
   {'name': '#dumpertmeuk',
    'promoted_content': None,
    'query': '%23dumpertmeuk',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23dumpertmeuk'},
   {'name': '#wereldarmoededag',
    'promoted_content': None,
    'query': '%23wereldarmoededag',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23wereldarmoededag'},
   {'name': '#PerrieExposedZaynParty',
    'promoted_content': None,
    'query': '%23PerrieExposedZaynParty',
    'tweet_volume': 92932,
    'url': 'http://twitter.com/search?q=%23PerrieExposedZaynParty'},
   {'name': '#mondaymotivation',
    'promoted_content': None,
    'query': '%23mondaymotivation',
    'tweet_volume': 148775,
    'url': 'http://twitter.com/search?q=%23mondaymotivation'},
   {'name': '#TCSAM16',
    'promoted_content': None,
    'query': '%23TCSAM16',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23TCSAM16'},
   {'name': '#WorldFoodDay',
    'promoted_content': None,
    'query': '%23WorldFoodDay',
    'tweet_volume': 13674,
    'url': 'http://twitter.com/search?q=%23WorldFoodDay'},
   {'name': '#BASF',
    'promoted_content': None,
    'query': '%23BASF',
    'tweet_volume': 10802,
    'url': 'http://twitter.com/search?q=%23BASF'},
   {'name': '#WFD2016',
    'promoted_content': None,
    'query': '%23WFD2016',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23WFD2016'},
   {'name': '#Hoorn',
    'promoted_content': None,
    'query': '%23Hoorn',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23Hoorn'},
   {'name': '#droomlandamerika',
    'promoted_content': None,
    'query': '%23droomlandamerika',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23droomlandamerika'},
   {'name': '#omarmprijs',
    'promoted_content': None,
    'query': '%23omarmprijs',
    'tweet_volume': None,
    'url': 'http://twitter.com/search?q=%23omarmprijs'}]}]

In [68]:
trends_ams[0]['trends']


Out[68]:
[{'name': '#ShoutoutToMyEx',
  'promoted_content': None,
  'query': '%23ShoutoutToMyEx',
  'tweet_volume': 442950,
  'url': 'http://twitter.com/search?q=%23ShoutoutToMyEx'},
 {'name': '#singleparents',
  'promoted_content': None,
  'query': '%23singleparents',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23singleparents'},
 {'name': '#Mosul',
  'promoted_content': None,
  'query': '%23Mosul',
  'tweet_volume': 69119,
  'url': 'http://twitter.com/search?q=%23Mosul'},
 {'name': '#dating',
  'promoted_content': None,
  'query': '%23dating',
  'tweet_volume': 14363,
  'url': 'http://twitter.com/search?q=%23dating'},
 {'name': '#STOICON16',
  'promoted_content': None,
  'query': '%23STOICON16',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23STOICON16'},
 {'name': 'Find Her',
  'promoted_content': None,
  'query': '%22Find+Her%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22Find+Her%22'},
 {'name': 'Cristiano Ronaldo',
  'promoted_content': None,
  'query': '%22Cristiano+Ronaldo%22',
  'tweet_volume': 15817,
  'url': 'http://twitter.com/search?q=%22Cristiano+Ronaldo%22'},
 {'name': 'Hans Klok',
  'promoted_content': None,
  'query': '%22Hans+Klok%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22Hans+Klok%22'},
 {'name': 'Pierre Bokma',
  'promoted_content': None,
  'query': '%22Pierre+Bokma%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22Pierre+Bokma%22'},
 {'name': 'Felix Rottenberg',
  'promoted_content': None,
  'query': '%22Felix+Rottenberg%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22Felix+Rottenberg%22'},
 {'name': 'Renate Groenewold',
  'promoted_content': None,
  'query': '%22Renate+Groenewold%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22Renate+Groenewold%22'},
 {'name': 'Kookboek',
  'promoted_content': None,
  'query': 'Kookboek',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=Kookboek'},
 {'name': 'VfL Wolfsburg',
  'promoted_content': None,
  'query': '%22VfL+Wolfsburg%22',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%22VfL+Wolfsburg%22'},
 {'name': 'Artis',
  'promoted_content': None,
  'query': 'Artis',
  'tweet_volume': 10527,
  'url': 'http://twitter.com/search?q=Artis'},
 {'name': 'Staat',
  'promoted_content': None,
  'query': 'Staat',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=Staat'},
 {'name': 'Lubach',
  'promoted_content': None,
  'query': 'Lubach',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=Lubach'},
 {'name': 'West',
  'promoted_content': None,
  'query': 'West',
  'tweet_volume': 221194,
  'url': 'http://twitter.com/search?q=West'},
 {'name': 'Amsterdam',
  'promoted_content': None,
  'query': 'Amsterdam',
  'tweet_volume': 31724,
  'url': 'http://twitter.com/search?q=Amsterdam'},
 {'name': '#jajgra',
  'promoted_content': None,
  'query': '%23jajgra',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23jajgra'},
 {'name': '#Hunted',
  'promoted_content': None,
  'query': '%23Hunted',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23Hunted'},
 {'name': '#Asscher',
  'promoted_content': None,
  'query': '%23Asscher',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23Asscher'},
 {'name': '#feyado',
  'promoted_content': None,
  'query': '%23feyado',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23feyado'},
 {'name': '#JOANNE',
  'promoted_content': None,
  'query': '%23JOANNE',
  'tweet_volume': 119376,
  'url': 'http://twitter.com/search?q=%23JOANNE'},
 {'name': '#LIVMUN',
  'promoted_content': None,
  'query': '%23LIVMUN',
  'tweet_volume': 52370,
  'url': 'http://twitter.com/search?q=%23LIVMUN'},
 {'name': '#eenvandaag',
  'promoted_content': None,
  'query': '%23eenvandaag',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23eenvandaag'},
 {'name': '#didd',
  'promoted_content': None,
  'query': '%23didd',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23didd'},
 {'name': '#camhee',
  'promoted_content': None,
  'query': '%23camhee',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23camhee'},
 {'name': '#gtst',
  'promoted_content': None,
  'query': '%23gtst',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23gtst'},
 {'name': '#NACpraat',
  'promoted_content': None,
  'query': '%23NACpraat',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23NACpraat'},
 {'name': '#DPSEPSC',
  'promoted_content': None,
  'query': '%23DPSEPSC',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23DPSEPSC'},
 {'name': '#LFCvMUFC',
  'promoted_content': None,
  'query': '%23LFCvMUFC',
  'tweet_volume': 39619,
  'url': 'http://twitter.com/search?q=%23LFCvMUFC'},
 {'name': '#ThingsISayPrettyOften',
  'promoted_content': None,
  'query': '%23ThingsISayPrettyOften',
  'tweet_volume': 71493,
  'url': 'http://twitter.com/search?q=%23ThingsISayPrettyOften'},
 {'name': '#TEDxMaastricht',
  'promoted_content': None,
  'query': '%23TEDxMaastricht',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23TEDxMaastricht'},
 {'name': '#twezwo',
  'promoted_content': None,
  'query': '%23twezwo',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23twezwo'},
 {'name': '#StudioVoetbal',
  'promoted_content': None,
  'query': '%23StudioVoetbal',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23StudioVoetbal'},
 {'name': '#dumpertmeuk',
  'promoted_content': None,
  'query': '%23dumpertmeuk',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23dumpertmeuk'},
 {'name': '#wereldarmoededag',
  'promoted_content': None,
  'query': '%23wereldarmoededag',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23wereldarmoededag'},
 {'name': '#PerrieExposedZaynParty',
  'promoted_content': None,
  'query': '%23PerrieExposedZaynParty',
  'tweet_volume': 92932,
  'url': 'http://twitter.com/search?q=%23PerrieExposedZaynParty'},
 {'name': '#mondaymotivation',
  'promoted_content': None,
  'query': '%23mondaymotivation',
  'tweet_volume': 148775,
  'url': 'http://twitter.com/search?q=%23mondaymotivation'},
 {'name': '#TCSAM16',
  'promoted_content': None,
  'query': '%23TCSAM16',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23TCSAM16'},
 {'name': '#WorldFoodDay',
  'promoted_content': None,
  'query': '%23WorldFoodDay',
  'tweet_volume': 13674,
  'url': 'http://twitter.com/search?q=%23WorldFoodDay'},
 {'name': '#BASF',
  'promoted_content': None,
  'query': '%23BASF',
  'tweet_volume': 10802,
  'url': 'http://twitter.com/search?q=%23BASF'},
 {'name': '#WFD2016',
  'promoted_content': None,
  'query': '%23WFD2016',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23WFD2016'},
 {'name': '#Hoorn',
  'promoted_content': None,
  'query': '%23Hoorn',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23Hoorn'},
 {'name': '#droomlandamerika',
  'promoted_content': None,
  'query': '%23droomlandamerika',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23droomlandamerika'},
 {'name': '#omarmprijs',
  'promoted_content': None,
  'query': '%23omarmprijs',
  'tweet_volume': None,
  'url': 'http://twitter.com/search?q=%23omarmprijs'}]

In [71]:
[(t['name'], t['tweet_volume']) for t in trends_ams[0]['trends']]


Out[71]:
[('#ShoutoutToMyEx', 442950),
 ('#singleparents', None),
 ('#Mosul', 69119),
 ('#dating', 14363),
 ('#STOICON16', None),
 ('Find Her', None),
 ('Cristiano Ronaldo', 15817),
 ('Hans Klok', None),
 ('Pierre Bokma', None),
 ('Felix Rottenberg', None),
 ('Renate Groenewold', None),
 ('Kookboek', None),
 ('VfL Wolfsburg', None),
 ('Artis', 10527),
 ('Staat', None),
 ('Lubach', None),
 ('West', 221194),
 ('Amsterdam', 31724),
 ('#jajgra', None),
 ('#Hunted', None),
 ('#Asscher', None),
 ('#feyado', None),
 ('#JOANNE', 119376),
 ('#LIVMUN', 52370),
 ('#eenvandaag', None),
 ('#didd', None),
 ('#camhee', None),
 ('#gtst', None),
 ('#NACpraat', None),
 ('#DPSEPSC', None),
 ('#LFCvMUFC', 39619),
 ('#ThingsISayPrettyOften', 71493),
 ('#TEDxMaastricht', None),
 ('#twezwo', None),
 ('#StudioVoetbal', None),
 ('#dumpertmeuk', None),
 ('#wereldarmoededag', None),
 ('#PerrieExposedZaynParty', 92932),
 ('#mondaymotivation', 148775),
 ('#TCSAM16', None),
 ('#WorldFoodDay', 13674),
 ('#BASF', 10802),
 ('#WFD2016', None),
 ('#Hoorn', None),
 ('#droomlandamerika', None),
 ('#omarmprijs', None)]

There's also a way to fetch the list without hashtags:


In [72]:
trends_ams = api.trends_place(ams['woeid'], exclude='hashtags')

In [73]:
[(t['name'], t['tweet_volume']) for t in trends_ams[0]['trends']]


Out[73]:
[('Find Her', None),
 ('Cristiano Ronaldo', 15817),
 ('Hans Klok', None),
 ('Pierre Bokma', None),
 ('Felix Rottenberg', None),
 ('Renate Groenewold', None),
 ('Kookboek', None),
 ('VfL Wolfsburg', None),
 ('Artis', 10527),
 ('Staat', None),
 ('Lubach', None),
 ('West', 221194),
 ('Amsterdam', 31724)]

Simplifying a bit

Let's write a few functions so we don't have to write all that out each time.


In [36]:
def getloc(name):
    try:
        return [t for t in trends_available if t['name'].lower() == name.lower()][0]
    except:
        return None

In [42]:
def trends(loc, exclude=False):
    return api.trends_place(loc['woeid'], 'hashtags' if exclude else None)

In [46]:
def top(trends):
    return sorted([(t['name'], t['tweet_volume']) for t in trends[0]['trends']], key=lambda a: a[1] or 0, reverse=True)

In [80]:
tok = getloc('tokyo')

In [81]:
trends_tok = trends(tok, True)

In [82]:
top(trends_tok)


Out[82]:
[('センター', 95248),
 ('カインとアベル', 65728),
 ('山田くん', 34572),
 ('水曜日', 25301),
 ('雨の日', 23363),
 ('キスブサ', 16031),
 ('シュウさん', 11489),
 ('マリオンガーデン1300桑名店', None),
 ('人妻10連ガチャ', None),
 ('帯のフレーズ', None),
 ('労働時間の上限引き下げ', None),
 ('ディラン氏', None),
 ('野迫川村', None),
 ('水嶋ヒロ', None),
 ('ガッポイ', None),
 ('リトルなでしこ', None),
 ('星野源のオールナイトニッポン', None),
 ('弓弦誕生日', None),
 ('受動喫煙防止', None),
 ('鍵盤アタック', None),
 ('弓弦くん', None),
 ('黄金魂', None),
 ('法の下', None),
 ('入賞取り消し', None),
 ('最高益', None)]

In [83]:
alg = getloc('Algeria')

In [84]:
trends_alg = trends(alg)

In [90]:
top(trends_alg)


Out[90]:
[('#ShoutoutToMyEx', 444076),
 ('#افضل_مشروب_بالعالم', 26720),
 ('Julien', 21587),
 ('#تونس', 13246),
 ('#مجازر_17_اكتوبر_1961', None),
 ('Algériens', None),
 ('#امل_بوشوشه', None),
 ('Alger', None),
 ('#Ligue1', None),
 ('#JÉcoute', None),
 ('#IbtissamTiskat', None),
 ('#Syrie', None),
 ('#اكتشف_الجزاير', None),
 ('#مينو', None)]

Weird, there's an L-to-R issue rendering the output here:


In [92]:
top(trends_alg)[1][0]


Out[92]:
'#افضل_مشروب_بالعالم'

Just for fun let's compare Algiers with Algeria.


In [102]:
algs = getloc('Algiers')

In [103]:
trends_algs = trends(algs)

In [104]:
top(trends_algs)


Out[104]:
[('#ShoutoutToMyEx', 444634),
 ('#افضل_مشروب_بالعالم', 27576),
 ('Julien', 21806),
 ('#تونس', 13314),
 ('#مجازر_17_اكتوبر_1961', None),
 ('Algériens', None),
 ('#امل_بوشوشه', None),
 ('Alger', None),
 ('#Ligue1', None),
 ('#JÉcoute', None),
 ('#IbtissamTiskat', None),
 ('#Syrie', None),
 ('#اكتشف_الجزاير', None),
 ('#مينو', None)]

Very similar. How about Chicago vs Miami?


In [119]:
chi = getloc('Chicago')

In [112]:
trends_chi = trends(chi)

In [114]:
chi


Out[114]:
{'country': 'United States',
 'countryCode': 'US',
 'name': 'Chicago',
 'parentid': 23424977,
 'placeType': {'code': 7, 'name': 'Town'},
 'url': 'http://where.yahooapis.com/v1/place/2379574',
 'woeid': 2379574}

In [115]:
top(trends_chi)[:10]


Out[115]:
[('Liverpool', 249610),
 ('#FreeJulian', 188500),
 ('Joanne', 168900),
 ('#mondaymotivation', 151709),
 ('Manchester United', 90762),
 ('Nicki', 81258),
 ('Anfield', 68510),
 ('Bears', 58819),
 ('Trump TV', 36360),
 ('#HTTR', 32578)]

In [120]:
mia = getloc('Miami')

In [121]:
mia


Out[121]:
{'country': 'United States',
 'countryCode': 'US',
 'name': 'Miami',
 'parentid': 23424977,
 'placeType': {'code': 7, 'name': 'Town'},
 'url': 'http://where.yahooapis.com/v1/place/2450022',
 'woeid': 2450022}

In [122]:
trends_mia = trends(mia)

In [123]:
top(trends_mia)[:10]


Out[123]:
[('Halloween', 445189),
 ('Liverpool', 249610),
 ('#FreeJulian', 188500),
 ('Joanne', 168900),
 ('#mondaymotivation', 151709),
 ('iPhone 7', 97610),
 ('Manchester United', 90762),
 ('Anfield', 68510),
 ('Dolphins', 37166),
 ('Trump TV', 36360)]

In [124]:
world = getloc('Worldwide')

In [125]:
trends_world = trends(world)

In [127]:
top(trends_world)[:25]


Out[127]:
[('#Joanne', 124283),
 ('#تيم_القمه_والفولورز_بالحب', 121628),
 ('#زد_رصيدك2', 83068),
 ('#LIVMUN', 69867),
 ('BELIEBERS UNIDOS', 54083),
 ('#كم_عندك_مشاهده_في_السناب', 53986),
 ('#اوصف_السعودي_بكلمه', 50079),
 ('Trump TV', 36682),
 ('#اعفوا_وزير_الاتصالات', 36680),
 ('#OneDitection', 29790),
 ('#PuebloDignificado', 29260),
 ('#افضل_مشروب_بالعالم', 28907),
 ('#MovieTitleToDescribeElection', 26682),
 ('George Zimmerman', 25160),
 ('#DiaDeLaLealtadPeronista', 24999),
 ('#NationalPastaDay', 24037),
 ('#MakeMeHateYouInOnePhrase', 23068),
 ('#içerdeyim', 22923),
 ('#PAKvWI', 21320),
 ('#727TourParis', 20466),
 ('#PraVidaFicarBoaSóFalta', 18914),
 ('#كلنا_ضد_الحب', 18626),
 ('#EdirnedenMusulaYeniTürkiye', 16482),
 ('#ليفربول_مانشستر', 16291),
 ('#GBHeroesMCR', 16199)]

Looking nearby

There are specific javascript calls for fetching a user's geolocation through a browser (see Using Geolocation via MDN). With a tool like that in place, you could fetch the user's lat and long and send it to the trends/closest Twitter API call:


In [95]:
closeby = api.trends_closest(38.8860307, -76.9931073)

In [99]:
closeby[0]


Out[99]:
{'country': 'United States',
 'countryCode': 'US',
 'name': 'Washington',
 'parentid': 23424977,
 'placeType': {'code': 7, 'name': 'Town'},
 'url': 'http://where.yahooapis.com/v1/place/2514815',
 'woeid': 2514815}

In [101]:
top(trends(closeby[0]))


Out[101]:
[('#FreeJulian', 187560),
 ('Joanne', 170150),
 ('#mondaymotivation', 151088),
 ('Odell', 148579),
 ('Eagles', 92264),
 ('#CowboysNation', 76418),
 ('Anfield', 65707),
 ('#LIVMUN', 62369),
 ('Aaron Rodgers', 50620),
 ('Nicki Minaj', 48791),
 ('#HTTR', 36891),
 ('Trump TV', 35964),
 ('Amy Goodman', 30901),
 ('#MovieTitleToDescribeElection', 26327),
 ('#SurvivingCompton', 24847),
 ('George Zimmerman', 24318),
 ('#NationalPastaDay', 23299),
 ('#MakeMeHateYouInOnePhrase', 22344),
 ('Azealia Banks', 20716),
 ('#PAKvWI', 20481),
 ('Amy Schumer', 16811),
 ('#FreeGsuHomecomingParty', 15987),
 ('To the First Lady', None),
 ('Shepard Smith', None),
 ('Come to Mama', None),
 ('I Gotta Ask', None),
 ('Pray 4 Me', None),
 ('Leonard Fournette', None),
 ('Mauritania', None),
 ('Head of U.S.', None),
 ('Adam Powell', None),
 ('Bob Cousy Award', None),
 ('#MyFirstCarIn3Words', None),
 ('#Under30Summit', None),
 ('#MusicMonday', None),
 ('#LArain', None),
 ('#jbfconf2016', None),
 ('#BossDay', None),
 ('#NIKEMAG', None),
 ('#DPSEPSC', None),
 ('#congratsliam', None),
 ('#JunkinsFire', None),
 ('#heweb16', None),
 ('#hatersbackofflivestream', None),
 ('#JaneTheVirgin', None),
 ('#CookingSchmovies', None),
 ('#FintechU', None),
 ('#AmberAlert', None),
 ('#KevinCanWait', None),
 ('#My5WordHorrorNovel', None)]

Hmm, that parentid attribute might be useful.


In [135]:
[t['name'] for t in trends_available if t['parentid'] == 23424977]


Out[135]:
['Albuquerque',
 'Atlanta',
 'Austin',
 'Baltimore',
 'Baton Rouge',
 'Birmingham',
 'Boston',
 'Charlotte',
 'Chicago',
 'Cincinnati',
 'Cleveland',
 'Colorado Springs',
 'Columbus',
 'Dallas-Ft. Worth',
 'Denver',
 'Detroit',
 'El Paso',
 'Fresno',
 'Greensboro',
 'Harrisburg',
 'Honolulu',
 'Houston',
 'Indianapolis',
 'Jackson',
 'Jacksonville',
 'Kansas City',
 'Las Vegas',
 'Long Beach',
 'Los Angeles',
 'Louisville',
 'Memphis',
 'Mesa',
 'Miami',
 'Milwaukee',
 'Minneapolis',
 'Nashville',
 'New Haven',
 'New Orleans',
 'New York',
 'Norfolk',
 'Oklahoma City',
 'Omaha',
 'Orlando',
 'Philadelphia',
 'Phoenix',
 'Pittsburgh',
 'Portland',
 'Providence',
 'Raleigh',
 'Richmond',
 'Sacramento',
 'St. Louis',
 'Salt Lake City',
 'San Antonio',
 'San Diego',
 'San Francisco',
 'San Jose',
 'Seattle',
 'Tallahassee',
 'Tampa',
 'Tucson',
 'Virginia Beach',
 'Washington']

In [130]:
[t['name'] for t in trends_available if t['parentid'] == world['woeid']]


Out[130]:
['United Arab Emirates',
 'Algeria',
 'Argentina',
 'Australia',
 'Austria',
 'Bahrain',
 'Belgium',
 'Belarus',
 'Brazil',
 'Canada',
 'Chile',
 'Colombia',
 'Denmark',
 'Dominican Republic',
 'Ecuador',
 'Egypt',
 'Ireland',
 'France',
 'Ghana',
 'Germany',
 'Greece',
 'Guatemala',
 'Indonesia',
 'India',
 'Israel',
 'Italy',
 'Japan',
 'Jordan',
 'Kenya',
 'Korea',
 'Kuwait',
 'Lebanon',
 'Latvia',
 'Oman',
 'Mexico',
 'Malaysia',
 'Nigeria',
 'Netherlands',
 'Norway',
 'New Zealand',
 'Peru',
 'Pakistan',
 'Poland',
 'Panama',
 'Portugal',
 'Qatar',
 'Philippines',
 'Puerto Rico',
 'Russia',
 'Saudi Arabia',
 'South Africa',
 'Singapore',
 'Spain',
 'Sweden',
 'Switzerland',
 'Thailand',
 'Turkey',
 'United Kingdom',
 'Ukraine',
 'United States',
 'Venezuela',
 'Vietnam']

No Liechtenstein? Georgia? Belize? Hmm.

Thoughts on rate limits

The Twitter API calls have the following rate limits (as of October 17, 2016):

  • GET trends/available - 15/15 min
  • GET trends/place - 15/15 min
  • GET trends/closest - 15/15 min

The key issue here is trends/place. trends/available probably doesn't change very often. trends/closest would only vary if the user is on the move, and even then, only slowly. But to track trends at more than one level on a minute-over-minute basis would simply not be possible because we are limited to one call per minute on trends/place.

Practically speaking, we could do:

  • every minute for one place
  • every two-plus-a-fraction minutes for two places
  • every four minutes for three places

Given the practicality of setting things up and allowing a little buffer, it probably makes sense to check three places every five minutes. The default scenario could be check (worldwide, country, closest place). A config parameter could figure this out for you if set by default, or a little UI element could help choose places. For example, if a continuing event users wanted to track occurred in Omaha, you could have an option to switch one of the ongoing trend trackers to get Omaha every five minutes, or to turn off all the defaults and fetch trends for Omaha once every minute.