Análise de conteúdo tabnews (fevereiro de 2023)
Salve povo!
Fiz uma análise das postagens do mês de fevereiro do tabnews, pra me familiarizar com a API e entender melhor alguns padrões de postagem aqui do fórum.
Todo o código desenvolvido nessa postagem está nesse notebook do kaggle: link.
Eu também fiz um post no meu blog no qual eu explico o passo a passo pra reproduzir os gráficos e valores que eu vou expor aqui.
Vamos abordar os seguintes tópicos:
- Buscando dados na API
- Automatizando as requisições com python
- Análise inicial dos dados
- Distribuição dos comentários
- Encontrando tags nas postagens
- Distribuição temporal das postagens
- Distribuição por dias da semana ao longo do mês novo!
- Distribuição por horário do dia
- Conclusão
Pra coletar os dados, busquei uma forma de evitar fazer um consumo excesivo da API, e me baseei no cabeçalho x-pagination-total-rows
que a API do tabnews retorna em todas as requisições.
No meu caso, utilizei o endpoint /api/v1/contents
, pra pegar apenas as postagens, sem os comentários.
$ curl -I "https://www.tabnews.com.br/api/v1/contents?strategy=new"
HTTP/2 200
...
..
link: <https://www.tabnews.com.br/api/v1/contents?strategy=new&page=1&per_page=1>; rel="first", <https://www.tabnews.com.br/api/v1/contents?strategy=new&page=2&per_page=1>; rel="next", <https://www.tabnews.com.br/api/v1/contents?strategy=new&page=8153&per_page=1>; rel="last"
x-pagination-total-rows: 8153
O total de resultados (no dia que eu rodei esse script) eram 8153!
Esse cabeçalho sempre retorna o total de resultados, dado que os endpoints da API são todos paginados, com um máximo de 100 resultados por página.
Combinei essa quantidade de resultados com o retorno do endpoint de analytics, que dá a quantidade de posts feitos por dia num intervalo de dois meses.
$ curl https://www.tabnews.com.br/api/v1/analytics/root-content-published
[
{"date":"13/01","conteudos":38},
{"date":"14/01","conteudos":29},
{"date":"15/01","conteudos":30},
{"date":"16/01","conteudos":50},
{"date":"17/01","conteudos":37},
{"date":"18/01","conteudos":44},
..
]
Com isso, selecionei apenas os posts do início de fevereiro até o dia atual, e fiz descobri "a quantos posts atrás" foi feito o primeiro post de fevereiro.
$ echo '[{"date":"01/02","conteudos":46},{"date":"02/02","conteudos":66},{"date":"03/02","conteudos":43},{"date":"04/02","conteudos":43},{"date":"05/02","conteudos":31},{"date":"06/02","conteudos":55},{"date":"07/02","conteudos":51},{"date":"08/02","conteudos":46},{"date":"09/02","conteudos":47},{"date":"10/02","conteudos":39},{"date":"11/02","conteudos":18},{"date":"12/02","conteudos":19},{"date":"13/02","conteudos":40},{"date":"14/02","conteudos":29},{"date":"15/02","conteudos":45},{"date":"16/02","conteudos":54},{"date":"17/02","conteudos":48},{"date":"18/02","conteudos":21},{"date":"19/02","conteudos":18},{"date":"20/02","conteudos":27},{"date":"21/02","conteudos":27},{"date":"22/02","conteudos":37},{"date":"23/02","conteudos":38},{"date":"24/02","conteudos":41},{"date":"25/02","conteudos":10},{"date":"26/02","conteudos":13},{"date":"27/02","conteudos":51},{"date":"28/02","conteudos":38},{"date":"01/03","conteudos":21},{"date":"02/03","conteudos":35},{"date":"03/03","conteudos":35},{"date":"04/03","conteudos":21},{"date":"05/03","conteudos":15},{"date":"06/03","conteudos":28},{"date":"07/03","conteudos":27},{"date":"08/03","conteudos":40},{"date":"09/03","conteudos":37},{"date":"10/03","conteudos":31},{"date":"11/03","conteudos":29},{"date":"12/03","conteudos":23},{"date":"13/03","conteudos":34}]' \
| jq "[.[].conteudos] | add"
1417
1417! Esse foi o número de postagens criadas des do início de fevereiro, até o dia atual. Isso dá mais ou menos 15 páginas (1417 / 100 ≈ 14).
Como o total de resultados do endpoint eram 8153, eles estavam distribuidos em 82 páginas (8153 / 100 ≈ 82).
Com isso, sei que precisava começar minhas requisições ali pela página 68 (82 - 14).
A lógica pra recuperar as postagens dentro do intervalo é simples:
- Começo buscando na página 68, e iterando sobre os resultados de cada página;
- Se um resultado for anterior a fevereiro, eu pulo pro próximo;
- Se o resultado for de fevereiro, salvo em uma lista;
- Vou passando de página em página, sempre adicionando +1 à página atual;
- Quando eu chegar no primeiro post de março, posso parar o algoritmo;
A implementação dessa lógica é extremamente simples se usarmos as libs requests
e json
do python.
import json
import requests
from datetime import datetime as dt
from time import sleep
def published_before_february(request_body):
"""Returns whether the given post was `published_at` before 01/02/2023"""
published_at = dt.strptime(request_body["published_at"], "%Y-%m-%dT%H:%M:%S.%fZ")
return dt.strptime("2023-02-01", "%Y-%m-%d") > published_at
def published_after_february(request_body):
"""Returns whether the given post was `published_at` after 28/02/2023"""
published_at = dt.strptime(request_body["published_at"], "%Y-%m-%dT%H:%M:%S.%fZ")
return published_at > dt.strptime("2023-02-28T23:59:59", "%Y-%m-%dT%H:%M:%S")
base_url = "https://www.tabnews.com.br/api/v1"
current_page = 68
request_loop = True
february_posts = []
while request_loop:
request_url = f"{base_url}/contents?strategy=old&page={current_page}&per_page=100"
r = requests.get(request_url)
posts = r.json()
for post in posts:
if published_before_february(post):
continue
if published_after_february(post):
request_loop = False
break
february_posts.append(post)
current_page = current_page + 1
sleep(1) # prevent abuse :')
with open("february.json", "w") as outfile:
json.dump(february_posts, outfile)
Foram criados 935 posts em fevereiro.
Eu optei por remover os posts da newsletter do conjunto de dados, o que me deixou com 795 postagens criadas por usuários do fórum.
tabcoins | comments | |
---|---|---|
count | 795.000000 | 795.000000 |
mean | 3.373585 | 5.596226 |
std | 7.522933 | 13.093704 |
min | -12.000000 | 0.000000 |
25% | 1.000000 | 1.000000 |
50% | 1.000000 | 3.000000 |
75% | 3.000000 | 6.000000 |
max | 82.000000 | 301.000000 |
- A metade das postagens (50%) não atinge mais de três comentários.
- Três quartos das postagens (75%) não atinge 3 tabcoins!
- Total de tabcoins recebidos: 2682
- Total de comentários realizados: 4449
80% das postagens receberam ao menos um comentário.
Eu queria entender como ocorre essa distribuição, porque a média de comentários é relativamente alta (~5.6 por post), mas o desvio padrão é bem alto (~13).
Como não podemos ter posts negativos, isso indica que poucos posts recebem muitos comentários.
A forma que eu achei de entender essa relação foi agrupando os posts por número de comentários. Depois, multiplicando o número de posts agrupado pelo número de comentários, conseguimos ver quantos porcento do total esses posts representam.
Exemplo:
comments | num_posts | total_comments | percent_posts | percent_comments |
---|---|---|---|---|
301 | 1 | 301 | 0.13 | 6.77 |
84 | 1 | 84 | 0.13 | 1.89 |
76 | 1 | 76 | 0.13 | 1.71 |
71 | 1 | 71 | 0.13 | 1.60 |
44 | 1 | 44 | 0.13 | 0.99 |
43 | 1 | 43 | 0.13 | 0.97 |
Aqui podemos ver que posts com 301 comentários ocorreram 1 única vez, e representam 6.7% do total de comentários realizados no mês todo.
Posts com muitos comentários ocorrem poucas vezes, por exemplo, apenas 1 post teve 84, 76, 71, 44, 43 comentários.
Vamos agrupar pela ocorrência pra entender como os posts populares impactam o conteúdo do site.
num_posts | total_posts | percent_posts | percent_comments | comments | total_comments |
---|---|---|---|---|---|
1 | 12 | 1.56 | 18.20 | 809 | 809 |
2 | 16 | 2.00 | 9.61 | 214 | 428 |
3 | 6 | 0.76 | 3.37 | 50 | 150 |
4 | 8 | 1.00 | 3.60 | 40 | 160 |
5 | 10 | 1.26 | 4.27 | 38 | 190 |
18 | 36 | 4.52 | 7.69 | 19 | 342 |
6 | 6 | 0.75 | 2.16 | 16 | 96 |
Podemos interpretar essa tabela da seguinte forma: um total de doze posts teve um numero de comentários que ocorreu uma única vez (301 comentários, 84 comentários, 76 comentários..), o que representa 1.56% de todos os posts criados no mês.
Esses posts juntos somaram 18.20% dos comentários do mês, num total de 809 comentários.
Ou seja, 18.2% dos comentários ficaram acumulados em menos de 2% das postagens.
Esse acumulo de comentários pode ser visualizado plotando o percentual de posts pelo percentual de comentários.
O Tabnews utiliza um sistema onde o pessoal taggeia seus posts manualmente, usando colchetes ou categoria: título do post
.
Quais foram as tags mais utilizadas?
Pra responder essa pergunta, usei dois parâmetros.
-
Tags com até duas palavras.
Nesse caso, achei melhor usar um gráfico de barras para a visualização.
-
Tags com apenas uma palavra
Aqui foi interessante usar uma nuvem de palavras.
Primeiro, podemos visualizar a distribuição dos posts em função do dia da semana.
Temos um aumento nas postagens no meio da semana, e uma acalmada no fds... é como se a galera gostasse de usar o tabnews em horário de trabalho. Será?
O user rafael comentou que uma boa forma de visualizar a interação na plataforma ao longo dos dias da semana é pela página de status.
Como ficaria esse gráfico sem as contribuições da Newsletter?
No gráfico abaixo, eu separei apenas posts realizados em dia de semana, e fiz a distribuição em função da hora da postagem.
O post acabou ficando meio comprido :P Mas eu queria mostrar como uma quantidade limitada de dados (apenas um mês, contendo só títulos e horários) pode nos dar bastante informação sobre o perfil de postagem de uma comunidade.
Uma análise mais profunda pode estimar valores como:
- o melhor horário para postagens (em termos de comentários recebidos)
- as tags com maior receptividade pelos usuários
- os assuntos com maior engajamento na plataforma
que são métricas poderosas e podem qualificar a audiência pra quem busca expandir seu público através do fórum.
Muito obrigado!
Que trabalho legal. Essa distribuição que apresentou aqui não tem nada de anormal, na verdade é o esperado nesse tipo de plataforma, aonde o comportamento dos atores é independente (power-law). É o que o Taleb chama de "extremistão". Inclusive recomendo a leitura da "Lógica do Cisne Negro". Se você fizer uma análise parecida no YouTube, ou em qualquer rede social, vai notar que os gráficos de distribuição serão bem parecidos com os que você apresentou aqui.
O horário do dia é interessante, mas pessoas que moram fora do pais postam e comentam em horários diferentes. Ajuda, duvida e discussão sendo palavras que mostram existir uma série de juniors. Também um público alvo. Ja o dia da semana me lembra os dados do LinkedIn. De qqr forma espero que continue a nos mostrar essas análises, pois vai ser interessante ver im comparativo entres os meses.
Incrível! Antes de abrir tabnews hoje eu estava com o pensamento "como será que está indo o tabnews?" e por incrível que parece é o primeiro post que me aparece como se estivesse respondendo a pergunta que eu tinha me feito!
Muito boa análise, uma questão sou novo aqui e não encontrei onde realizar uma busco dentro do próprio site de conteúdos ?
Não sei se não tem ou ainda não foi implementado, ou esse não é o objetivo.