Já pensou se você conseguisse identificar as respostas emocionais que usuários apresentam sobre uma determinada entidade de interesse? Saber se as pessoas ficaram felizes com a venda do GitHub para a Microsoft, ou mais tristes do que o Tony Stark no final de Guerra Infinita? Bom, você veio ao lugar certo, nesse tutorial vamos aprender da forma mais simples possível como criar um analisador de sentimentos com dados obtido pelo Twitter. Bora?
- Python3.7
- Pipenv
- Git
- Uma conta no Twitter
Digite o seguinte comando no seu terminal:
make setup
Isso irá criar e ativar um ambiente virtual com tudo o que você precisa para trabalhar
Digite os seguintes comando no seu terminal:
pipenv install
pipenv shell
Isso irá criar e ativar um ambiente virtual com tudo o que você precisa para trabalhar
Vamos inciar com a parte mais chata e trabalhosa, criar um perfil de desenvolvedor e uma APP no twitter. Essa parte é simples, porém o Twitter agora pede altas explicações e descrições que podem ser um pouco chatas de preencher, então vou deixar um preenchimento padrão para quem quiser colar perder menos tempo.
1. Vamos começar acessando https://developer.twitter.com/ e clicando em Apply
:
2. Na próxima página, clique em Apply for a developer account
e em Continue
na página seguinte para seguir com a sua conta:
3. Informe o tipo de conta que você está criando (Personal Use
provavelmente) e preencha as informações solicitadas:
4. Na próxima página terão algumas informações a serem preenchidas, preencha o campo What use case(s) are you interested in?
conforme a imagem e em Describe in your own words what you are building
coloque o seguinte texto:
1. I’m using Twitter’s APIs to run a Python Brasil Tutorial about Sentiment Analysis;
2. I plan to analyse Tweets to understand how people are feeling regarding some subject.
3. The solution does not involve tweeting, retweeting, neither liking content on twitter. It is just for analysis;
4. The solution does not involve displaying twitter explicitly, but its polarity and subjectivity
Em Will your product, service, or analysis make Twitter content or derived information available to a government entity?
selecione não
5. Finja ler leia os Termos de Serviço
e clique em concordar
6. Agora basta confirmar o seu email e seguir para a próxima parte :)
1. Primeiramente Fora Temer acesse o site de desenvolvimento do Twitter e clique em Create an APP
.
2. Digite os dados obrigatórios solicitados. No campo Tell us how this app will be used
você pode usar o seguinte texto:
This app will be used to develop a simple Sentiment Analysis App for a tutorial at Python Brasil 2018
Quanto ao website
, você pode colcar qualquer URL, incluisive a do seu perfil do twitter;
3. Acesse Keys and Tokens
para visualizar seus dados de autenticação.
Obs.: Para o Access Token e o Access Token Secret aparecerem é preciso clicar em Create Tokens
(Ou algo muito próximo disso) que se encontra mais abaixo na página. A Apressadinha já saiu apertando e não conseguiu tirar o print
4. Agora que você está com seu APP criado e tem todas as permissões, podemos seguir para a parte divertida.
1. Finalmente vamos dar inicio a parte interessante! Vamos iniciar criando um arquivo .py no editor de sua preferencia e importar as bibliotecas que vamos utilizar
import tweepy
import numpy as np
from textblob import TextBlob
2. Agora vamos setar as variáveis que vão receber as chaves da sua API do Twitter
consumer_key='your_consumer_key'
consumer_secret='consumer_secret'
access_token='access_token'
access_token_secret='access_token_secret'
Obs.: Se você preferir, pode colocar as chaves em um arquivo separado
Obs2.: Se você for commitar esse código am algum lugar, recomendo settar as chaves como variáveis de ambiente e importá-las
3. Tendo setado as variáveis, vamos fazer a autenticação do nosso script na API do Twitter:
auth = tweepy.OAuthHandler(consumer_key,consumer_secret)
auth.set_access_token(access_token,access_token_secret)
api = tweepy.API(auth)
Obs.: Se você quiser saber se a autenticação está funcionando, tweepy.API(auth)
deve retornar algo parecido com <tweepy.api.API object at 0x10d3c3240>
Obs2.: Caso vocês estejam tendo o erro SyntaxError: invalid syntax
em def _start(self, async):
, execute $pip3 install --upgrade git+https://github.com/tweepy/tweepy.git
4. Agora vamos buscar pelos nosso tweets, para isso, vamos utilizar a busca do tweepy. Como a coisa mais importante essa semana certamente é a Python Brasil
, podemos buscar os tweetes que façam referência a esse tópico.
tweets = api.search('Python Brasil')
ou para ignorar RTs e ter um resultado um pouco mais direcionado
tweets = api.search('Python Brasil -filter:retweets')
5. E iterar em cima dos resultados, pegando nossos tweets tweet.text
e colocando no TextBlob
for tweet in tweets:
phrase = TextBlob(tweet.text)
6. Uma vez que temos os tweets, podemos fazer a análise de sentimento contido em seu texto. Entretanto, temos um problema: O algoritmo da TextBlob foi treinado para fazer análise de textos em língua inglesa. Por isso, vamos ter que utilizar um recurso que faz a tradução para a inglês dos textos que estiverem em um idioma diferente (O TextBlob utiliza o google tradutor para isso). Para isso, vamos criar uma função para verificar o idioma do tweet:
def is_english(text):
if text.detect_language() == 'en':
return True
return False
Obs.: Vale lembrar que isso pode prejudicar um pouco a análise, pois alguns significado podem se perder durante a tradução. Existem outras formas de realizar análise de sentimento diretamente na língua portuguesa, porém, o objetivo desse tutorial é aprendermos primeiramente a maneira mais simples antes de recorrer a técnicas mais avançadas.
7. Dentro do for
, vamos verificar se o idioma do tweet é diferente do inglês com a função que acabamos de criar. Caso seja, vamos traduzir antes de realizar a análise de sentimento
polarities = []
for tweet in tweets:
phrase = TextBlob(tweet.text)
if not is_english(phrase):
phrase = TextBlob(str(phrase.translate(to='en')))
print('Tweet: ' + tweet.text)
print('Polarity: ' + str(phrase.sentiment.polarity) + " \ " + str(phrase.sentiment.subjectivity))
print('.....................')
Antes de continuarmos, vamos entender um pouco do que estamos vendo:
POLARITY (POLARIDADE): Um valor entre -1.0 e 1.0, onde -1.0 se refere a uma polaridade 100% negativa, 1.0 uma polaridade positiva
SUBJECTIVITY (SUBJETIVIDADE): Um valor variante entre 0.0 e 1.0, onde 0 se refere a um valor 100% objetivo e 1.0 um 100% subjetivo
SUBJETIVIDADE x OBJETIVIDADE: Sentenças objetivas normalmente possuem fatos ou informações, enquanto sentenças subjetivas expressam sentimentos pessoais e opiniões
8. Bom, agora que nós sabemos a polaridade e a subjetividade, devemos ignorar valores cuja a polaridade é neutra (0.0) e são objetivas (subjetividade 0.0), uma vez que temos interesse somente em sentenças que expressam sentimentos.
if (phrase.sentiment.polarity != 0.0 and phrase.sentiment.subjectivity != 0.0):
polarities.append(phrase.sentiment.polarity)
Agora que está parte está finalizada, podemos colocar isso dentro de uma função para deixar tudo mais bonito e organizado:
def tweet_analysis():
polarities = []
for tweet in tweets:
phrase = TextBlob(tweet.text)
if not is_english(phrase):
phrase = TextBlob(str(phrase.translate(to='en')))
if (phrase.sentiment.polarity != 0.0 and phrase.sentiment.subjectivity != 0.0):
polarities.append(phrase.sentiment.polarity)
print('Tweet: ' + tweet.text)
print('Polarity: ' + str(phrase.sentiment.polarity) + " \ " + str(phrase.sentiment.subjectivity))
print('.....................')
return polarities
9. E usar o numpy para calcular a média das polaridades e descobrir se a média da opinião é positiva (mais próxima de 1) ou negativa (mais próxima de -1)
polarity_mean = np.mean(polarities)
print('Média: ' + str(polarity_mean))
if(polarity_mean > 0.0):
print('POSITIVE')
else:
print('NEGATIVE')
10. Pronto! Agora temos um simples analisador de tweets... Porém ainda podemos ir mais adiante.
11. Vamos modificar um pouco nosso método de busca: ao invés de tweets = api.search('Python Brasil -filter:retweets')
vamos usar:
tweets = tweepy.Cursor(api.search, q="Python Brasil -filter:retweets").items(20)
Dessa forma, iremos filtrar somente os tweets que não forem retweets e pegar o numero determinado de tweets ;
12. Outra coisa que podemos fazer, é passar o parâmetro result_type='recent' para pegarmos os tweets mais recentes:
tweets = tweepy.Cursor(api.search, q="Python Brasil -filter:retweets", result_type="recent").items(20)
13. Antes de seguirmos para os próximos passos, podemos refatorar um pouco nosso código para deixar as coisas mais claras. Como por exemplo, podemos armazenar nossas polaridades e subjetividades em um dicionário e retorná-lo, assim podemos utilizar esses dados da forma que preferirmos:
tweets = tweepy.Cursor(api.search, q=query + " -filter:retweets").items(20)
subjectivities = []
polarities = []
for tweet in tweets:
phrase = TextBlob(tweet.text)
if not is_english(phrase):
phrase = TextBlob(str(phrase.translate(to='en')))
if phrase.sentiment.polarity != 0.0 and phrase.sentiment.subjectivity != 0.0:
polarities.append(phrase.sentiment.polarity)
subjectivities.append(phrase.sentiment.subjectivity)
print('Tweet: ' + tweet.text)
print('Polarity: ' + str(phrase.sentiment.polarity) + " \ " + str(phrase.sentiment.subjectivity))
print('.....................')
return {'polarity':polarities, 'subjectivity':subjectivities}
14. Agora que temos um dicionário com todas informações que precisamos, uma abordagem que podemos tentar, é aplicar uma média ponderada ao invés de uma média simples...
Calma... a média ponderada nada mais é do que o calculo de média onde alguns valores tem um peso maior do que o outro, assim podemos calcular a média de polaridade utilizando a subjetividade como peso. Desta forma, tweetes mais subjetivos (mais carregados de emoção) terão um peso maior.
def get_weighted_polarity_mean(valid_tweets):
return np.average(valid_tweets['polarity'],weights=valid_tweets['subjectivity'])
15 Aproveitando o embalo, podemos colocar a média normal também em uma função para manter um padrão:
def get_polarity_mean(valid_tweets):
return np.mean(valid_tweets['polarity'])
16. Também podemos transformar a query utilizada (q
) em um parâmetro recebido pela função que irá realizar a análise:
def tweet_analysis(query):
tweets = tweepy.Cursor(api.search, q=query + " -filter:retweets").items(20)
17. Para conseguirmos visualizar melhor nossos resultados podemos criar a seguinte função que se responsabiliza por mostrá-los na tela:
def print_result(mean):
if mean > 0.0:
print('POSITIVE')
elif mean == 0.0:
print('NEUTRO')
else:
print('NEGATIVE')
18. E criar uma if __name__ == "__main__"
para executar o nosso script com facilidade:
if __name__ == "__main__":
query = input("Entre a query de analise: ")
analysis = tweet_analysis(query)
print('MÉDIA PONDERADA: ' + str(get_weighted_polarity_mean(analysis)))
print_result(get_weighted_polarity_mean(analysis))
print('MÉDIA: ' + str(get_polarity_mean(analysis)))
print_result(get_polarity_mean(analysis))
16. O Código final ficaria assim:
import tweepy
import numpy as np
from textblob import TextBlob
consumer_key='your_consumer_key'
consumer_secret='consumer_secret'
access_token='access_token'
access_token_secret='access_token_secret'
auth = tweepy.OAuthHandler(consumer_key,consumer_secret)
auth.set_access_token(access_token,access_token_secret)
api = tweepy.API(auth)
def is_english(text):
if text.detect_language() == 'en':
return True
return False
def tweet_analysis(query):
tweets = tweepy.Cursor(api.search, q=query + " -filter:retweets").items(20)
subjectivities = []
polarities = []
for tweet in tweets:
phrase = TextBlob(tweet.text)
if not is_english(phrase):
phrase = TextBlob(str(phrase.translate(to='en')))
if phrase.sentiment.polarity != 0.0 and phrase.sentiment.subjectivity != 0.0:
polarities.append(phrase.sentiment.polarity)
subjectivities.append(phrase.sentiment.subjectivity)
print('Tweet: ' + tweet.text)
print('Polarity: ' + str(phrase.sentiment.polarity) + " \ " + str(phrase.sentiment.subjectivity))
print('.....................')
return {'polarity':polarities, 'subjectivity':subjectivities}
def get_weighted_polarity_mean(valid_tweets):
return np.average(valid_tweets['polarity'],weights=valid_tweets['subjectivity'])
def get_polarity_mean(valid_tweets):
return np.mean(valid_tweets['polarity'])
def print_result(mean):
if mean > 0.0:
print('POSITIVE')
elif mean == 0.0:
print('NEUTRO')
else:
print('NEGATIVE')
if __name__ == "__main__":
query = input("Entre a query de analise: ")
analysis = tweet_analysis(query)
print('MÉDIA PONDERADA: ' + str(get_weighted_polarity_mean(analysis)))
print_result(get_weighted_polarity_mean(analysis))
print('MÉDIA: ' + str(get_polarity_mean(analysis)))
print_result(get_polarity_mean(analysis))
Obs.: Fiquem a vontade para testar entidades de interesse diferentes, aumentar ou diminuir o numero de resultados;
Agora que já conseguimos gerar alguns resultados, vale a pena parar um pouco e analisá-los, assim poderemos observar algumas das dificuldades da Análise de sentimento.
Enquanto eu estava ajeitando os últimos detalhes desse tutorial no domingo com vários meses de antecedência, me deparei com um resultado curioso ao pesquisar por um dos Trending Topics do Brasil: Fantastico
:
Na noite de domingo uma galera no Twitter estava furiosa com alguma matéria que saiu no programa Fantástico:
Tweet: #Fantástico Mestre na arte da manipulação das massas! Admira Hitler #fantastico #pas
Polarity: 0.45 \ 0.9
.....................
Tweet: #fantásticolixo #Fantástico #globolixo
Polarity: 0.4 \ 0.9
.....................
Tweet: #Faustão na militância, e quando acaba vc se lembra que vem logo à seguir o #Fantástico. Esse ano não assisto mais. #BoicoteAGlobo
Polarity: 0.2 \ 0.5
.....................
Como é possível observar, não são tweets muito amigáveis, entretanto, se observarmos a polaridade deles, ela está tendendo mais para o positivo... Por quê? A resposta é simples: o nome do programa. "Fantástico" nesse caso está sendo interpretado como uma característica muito positiva, quando na verdade é somente o nome de um programa.
Outra coisa que pode gerar dificuldade é o numero de entradas com que temos que trabalhar. Quanto mais dados nós temos (tweets no contexto desse tutorial) melhor e mais precisa será a nossa análise, porém iremos precisar de mais capacidade de processamento. Experimente aumentar o numero de tweets buscados para 300
(que não é nem próximo de uma quantidade satisfatória de análise) e veja quanto tempo demora para o nosso programinha terminar de executar;
def tweet_analysis(query):
tweets = tweepy.Cursor(api.search, q=query + " -filter:retweets").items(300)
Demora não é? Capacidade de processamento é um desafio recorrente quando se trata de análise textual;
Como já havia mencionado anteriormente no tutorial, a maioria dos processadores textuais livres estão setados para a língua inglesa, então quando se trata de realizar análises para outros idiomas, é necessário que haja um processo de tradução. Veja o que acontece quando a frase Choque de cultura é irado!
(que tem uma conotação positiva em português) é traduzida pelo google tradutor:
angry
(irritado, irado) em inglês tem uma conotação negativa, fazendo com que um tweet com esse texto, seja interpretado como tendo uma polaridade negativa por causa da tradução, quando na verdade o seu sentido era exatamente o oposto.
Chegamos ao fim do nosso tutorial! Espero que tenha sido possível aprender um pouco sobre análise de sentimentos e manipulação textual. Quaisquer dúvidas, podem entrar em contato:
- E-mail: [email protected]
- Facebook: fb.com/error404not
- Twitter: @ngasonicunicorn
Também gostaria de lembrar que sexta feira dia 19 as 13h40, eu vou apresentar a palestra Mas afinal, pra que serve Análise de Sentimentos, onde vocês vão poder saber mais sobre o assunto.
Eu utilizei de alguns artigos maravilhosos sobre o assunto para conseguir montar esse tutorial, são eles: