Open Orion-Hunter opened 1 year ago
Eu trocaria a ordem aí, o Youtube deve ser o primeiro. O Twitter o segundo, depois o Tiktok e só depois as outras (facebook, threads, telegram)
Acredito que podemos aprender alguma coisa com a forma como o Social Blade resolve a questão. Ele faz um monitoramento muito mais complexo do que o monitoramento que iremos executar.
Olha, estou terminando o curso de analista de dados e seria legal ajudar na questão de visualização, pelo menos comparativa entre canais de direita e esquerda.
Mas envolveria uma ampla discussão sobre quais os canais/personalidades que seriam avaliadas, pois querendo ou não, a onda comunista/esquerdista está há anos na internet, enquanto poucos pessoas mais a direita se encontram de fato posicionadas e fazendo sucesso nesse aspecto.
Por exemplo, ian neves/gaiofato se posicionam como intelectuais da esquerda e eles estão faz uns anos já presentes. Da direita, com tanto tempo de canal, seria o Thiago Braga talvez? O MBL? O ideias radicais? Kkkk
Teria que ver a fundamentação do estudo em primazia
Eu e Malboro atualmente trabalhamos com inteligência política e já disponibilizamos uma planilha pro Smith dos canais de esquerda, posso disponibilizar aqui também.
O primeiro passo é definir quais indicadores iremos coletar. Sugiro a seguinte lista de métricas, usando o Youtube como ponto de partida:
Eu e Malboro atualmente trabalhamos com inteligência política e já disponibilizamos uma planilha pro Smith dos canais de esquerda, posso disponibilizar aqui também.
Pfv. Dependendo dos dados que vc possuir, já posso iniciar algumas análises aqui.
Eu cheguei a fuçar isso tudo com alguns códigos.
import os import pandas as pd from googleapiclient.discovery import build from googleapiclient.errors import HttpError from datetime import datetime, timedelta import logging import json
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)
try: with open("api_keys.json", 'r') as f: data = json.load(f) chaves_api_youtube = data['api_keys'] except FileNotFoundError: logging.error("Arquivo de chaves da API não encontrado.") chaves_api_youtube = [] except json.JSONDecodeError: logging.error("Falha ao decodificar o arquivo de chaves da API.") chaves_api_youtube = []
youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])
with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']
arquivo_excel = 'youtube_canais.xlsx' arquivo_existe = os.path.isfile(arquivo_excel)
dados_canais = []
if arquivo_existe: dados_excel = pd.read_excel(arquivo_excel, sheet_name='Canais') dados_canais.extend(dados_excel.to_dict('records'))
for id_canal in ids_canais: for chave_api_youtube in chaves_api_youtube: logging.info(f"Usando chave de API {chave_api_youtube}...")
try:
youtube = build('youtube', 'v3', developerKey=chave_api_youtube)
requisicao_canal = youtube.channels().list(
part="snippet,statistics,contentDetails",
id=id_canal
)
resposta_canal = requisicao_canal.execute()
if resposta_canal['items']:
canal = resposta_canal['items'][0]
nome_canal = canal['snippet']['title']
inscritos = int(canal['statistics']['subscriberCount'])
visualizacoes = int(canal['statistics']['viewCount'])
id_uploads = canal['contentDetails']['relatedPlaylists']['uploads']
data_atual = datetime.now()
data_6meses = data_atual - timedelta(days=6 * 30)
data_30dias = datetime.now() - timedelta(days=30)
data_3meses = datetime.now() - timedelta(days=3 * 30)
requisicao_videos = youtube.playlistItems().list(
part="contentDetails",
playlistId=id_uploads,
maxResults=50
)
resposta_videos = requisicao_videos.execute()
videos = resposta_videos['items']
while 'nextPageToken' in resposta_videos:
proxima_pagina_token = resposta_videos['nextPageToken']
requisicao_videos = youtube.playlistItems().list(
part="contentDetails",
playlistId=id_uploads,
maxResults=50,
pageToken=proxima_pagina_token
)
resposta_videos = requisicao_videos.execute()
videos.extend(resposta_videos['items'])
visualizacoes_30dias = 0
visualizacoes_3meses = 0
visualizacoes_6meses = 0
for video in videos:
id_video = video['contentDetails']['videoId']
data_publicacao = datetime.strptime(video['contentDetails']['videoPublishedAt'], '%Y-%m-%dT%H:%M:%SZ')
if data_publicacao > data_6meses:
requisicao_video = youtube.videos().list(
part="statistics",
id=id_video
)
resposta_video = requisicao_video.execute()
if resposta_video['items']:
estatisticas_video = resposta_video['items'][0]['statistics']
visualizacoes_video = int(estatisticas_video.get('viewCount', 0))
if data_publicacao > data_30dias:
visualizacoes_30dias += visualizacoes_video
if data_publicacao > data_3meses:
visualizacoes_3meses += visualizacoes_video
visualizacoes_6meses += visualizacoes_video
info_canal = {
'Nome do Canal': nome_canal,
'Inscritos': inscritos,
'Visualizações': visualizacoes,
'Visualizações 30 dias': visualizacoes_30dias,
'Visualizações 3 meses': visualizacoes_3meses,
'Visualizações 6 meses': visualizacoes_6meses
}
dados_canais.append(info_canal)
logging.info(f"Canal {nome_canal} processado.")
break
except HttpError:
logging.error("A cota da API foi excedida. Tentando com a próxima chave...")
chaves_api_youtube.remove(chave_api_youtube)
if not chaves_api_youtube:
logging.error("Todas as chaves de API foram usadas. Parando execução.")
sys.exit(1)
continue
logging.info("Processamento concluído.")
df = pd.DataFrame(dados_canais)
df.to_excel(arquivo_excel, sheet_name='Canais', index=False)
import os import pandas as pd from googleapiclient.discovery import build from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta from googleapiclient.errors import HttpError from docx import Document from docx.enum.text import WD_PARAGRAPH_ALIGNMENT import isodate import re import logging
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)
try: with open("api_keys.json", 'r') as f: data = json.load(f) chaves_api_youtube = data['api_keys'] except FileNotFoundError: logging.error("Arquivo de chaves da API não encontrado.") chaves_api_youtube = [] except json.JSONDecodeError: logging.error("Falha ao decodificar o arquivo de chaves da API.") chaves_api_youtube = []
youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])
with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']
time_limit = datetime.utcnow() - timedelta(hours=64)
time_limit_rfc3339 = time_limit.isoformat("T") + "Z"
filename = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") + ".docx"
doc = Document()
title = doc.add_paragraph() title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER run = title.add_run(f"Relatório do Dia {(datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')}") run.bold = True
channels = []
for id_canal in ids_canais: for chave_api_youtube in chaves_api_youtube: logging.info(f"Usando chave de API {chave_api_youtube}...") try: youtube = build('youtube', 'v3', developerKey=chave_api_youtube) # [NOVO] Constrói a API com a chave atual requisicao_canal = youtube.channels().list(part="snippet,statistics,contentDetails",id=id_canal) resposta_canal = requisicao_canal.execute() item_canal = resposta_canal["items"][0]
if not item_canal:
doc.add_paragraph(f"Nenhum vídeo retornado para o canal {channel_title}")
doc.add_paragraph("\n") # adiciona uma linha em branco entre os vídeos
continue
titulo_canal = item_canal["snippet"]["title]" #obtém o nome do canal
logging.info(f"Processando canal {titulo_canal}...")
inscritos = int(item_canal["statistics"]["subscriber_count"])# obtém a contagem de inscritos
# Add a title for the channel
channel_title_paragraph = doc.add_paragraph()
channel_title_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
run = channel_title_paragraph.add_run(titulo_canal)
run.bold = True
run.underline = True
channel_title_paragraph.style = doc.styles['Heading 2']
requisicao_video = youtube.search().list(part="id,snippet",channelId=channel_id,publishedAfter=time_limit_rfc3339,order="viewCount")
resposta = requisicao_video.execute()
logging.info("Fazendo a requisição para os vídeos do canal {titulo_canal}")
items = resposta.get("items", [])
if not items:
doc.add_paragraph("\n") # adiciona uma linha em branco entre os vídeos
doc.add_paragraph(f"Nenhum vídeo retornado para o canal {channel_title}")
doc.add_paragraph("\n") # adiciona uma linha em branco entre os vídeos
continue
videos = []
for item in resposta[itens]:
id_do_video = item["id"]["videoId"]
url_do_video = f"https://www.youtube.com/watch?v={id_do_video}"
titulo_do_video = item["snippet"]["title"] #obtém o título do vídeo
# Faz uma solicitação adicional para obter a contagem de visualizações, a descrição e a duração do vídeo
requisicao_videos = youtube.videos().list( part="snippet,statistics,contentDetails",id=id_do_video)
resposta_video = requisicao_videos.execute()
item_video = resposta_video["items"][0] # assume que sempre haverá um item na resposta
logging.info("Fazendo a requisição para o vídeo {titulo_do_video}")
visualizacoes_video = int(item_video["statistics"]["viewCount"]) # obtém a contagem de visualizações
description = item_video["snippet"]["description"] # obtém a descrição do vídeo
# Limita a descrição até o primeiro link
descricao = re.split(r'http[s]?://', description)[0]
# Converte a duração do vídeo de ISO 8601 para um timedelta e formata como HH:MM:SS
duracao = isodate.parse_duration(video_item["contentDetails"]["duration"])
duracaostr = str(duracao)
doc.add_paragraph(f"{titulo_do_video} ({titulo_canal}): {url_do_video}")
doc.add_paragraph(f"Duração: {duracaostr}")
doc.add_paragraph(f"Visualizações: {visualizacoes_video}")
doc.add_paragraph(f"Descrição: {description}")
doc.add_paragraph("_" * 105) # adiciona uma linha separadora de 40 caracteres
doc.add_paragraph("\n") # adiciona uma linha em branco entre os vídeos
except HttpError:
logging.error("A cota da API foi excedida. Tentando com a próxima chave...") # substituiu print() por logging.error()
chaves_api_youtube.remove(chave_api_youtube)
if not chaves_api_youtube:
logging.error("Todas as chaves de API foram usadas. Parando execução.")
sys.exit(1)
continue
except HttpError:
logging.exception("A cota da API foi excedida. Tentando com a próxima chave...")
break
doc.save(filename)
import json from googleapiclient.discovery import build from googleapiclient.errors import HttpError
with open('api_keys.json') as file: api_keys = json.load(file)
def get_channel_username(channel_id, youtube, api_key): try:
response = youtube.channels().list(
part='snippet',
id=channel_id
).execute()
# Verifica se a resposta contém resultados
if 'items' in response and len(response['items']) > 0:
channel = response['items'][0]
username = channel['snippet']['title']
return username
else:
return None
except HttpError as e:
error_message = e.content.decode("utf-8")
if 'quotaExceeded' in error_message:
print('A cota da chave de API foi excedida. Alternando para a próxima chave...')
return None
else:
print('Ocorreu um erro ao fazer a solicitação:', e)
return None
with open('ids_canais.json') as file: data = json.load(file)
output_data = []
for channel_id in data['ids_canais']: for api_key in api_keys:
youtube = build('youtube', 'v3', developerKey=api_key)
username = get_channel_username(channel_id, youtube, api_key)
if username:
output_data.append({
'id': channel_id,
'username': username
})
break # Sai do loop interno para passar para o próximo canal
with open('output.json', 'w') as outfile: json.dump(output_data, outfile, indent=4)
print("Processo concluído. Os nomes de usuários foram salvos em output.json.")
import os import pandas as pd from googleapiclient.discovery import build from googleapiclient.errors import HttpError from dateutil.relativedelta import relativedelta from datetime import datetime from openpyxl import load_workbook import logging import json
try: with open("checkpoints.json", 'r') as f: checkpoints = json.load(f) except FileNotFoundError: checkpoints = {}
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)
try: with open("api_keys.json", 'r') as f: data = json.load(f) chaves_api_youtube = data['api_keys'] except FileNotFoundError: logging.error("Arquivo de chaves da API não encontrado.") chaves_api_youtube = [] except json.JSONDecodeError: logging.error("Falha ao decodificar o arquivo de chaves da API.") chaves_api_youtube = []
youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])
with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']
try: dados_canais = pd.read_excel('youtube_definitivos.xlsx', sheet_name='Canais') except FileNotFoundError: dados_canais = pd.DataFrame(columns=['Nome do Canal', 'Inscritos', 'Visualizações', 'Visualizações 30 dias', 'Visualizações 3 meses', 'Visualizações 6 meses'])
escritor = pd.ExcelWriter('youtube_definitivos.xlsx', engine='openpyxl')
for id_canal in ids_canais: if id_canal in checkpoints: logging.info(f"O canal {id_canal} já foi processado, pulando...") continue for chave_api_youtube in chaves_api_youtube: logging.info(f"Usando chave de API {chave_api_youtube}...") # substituiu print() por logging.info() try: youtube = build('youtube', 'v3', developerKey=chave_api_youtube) # [NOVO] Constrói a API com a chave atual requisicao_canal = youtube.channels().list( part="snippet,statistics,contentDetails", id=id_canal ) resposta_canal = requisicao_canal.execute()
if resposta_canal['items']:
estatisticas = resposta_canal['items'][0]['statistics']
id_uploads = resposta_canal['items'][0]['contentDetails']['relatedPlaylists']['uploads']
nome_canal = resposta_canal['items'][0]['snippet']['title']
logging.info(f"Processando canal {nome_canal}...") # [ALTERADO] Substituiu print() por logging.info()
info_canal = {
'Nome do Canal': nome_canal,
'Inscritos': int(estatisticas['subscriberCount']),
'Visualizações': int(estatisticas['viewCount']),
'Visualizações 30 dias': 0,
'Visualizações 3 meses': 0,
'Visualizações 6 meses': 0,
}
# Adicione a linha ao DataFrame e salve-o
dados_canais = dados_canais.append(info_canal, ignore_index=True)
dados_canais.to_excel('youtube_definitivos.xlsx', sheet_name='Canais', index=False)
escritor._save()
# Inicializando lista para guardar dados dos vídeos
dados_videos = []
# Obtém todos os vídeos da playlist 'uploads' do canal
proximo_token_pagina = None
while True:
requisicao_video = youtube.playlistItems().list(
part="contentDetails",
playlistId=id_uploads,
maxResults=50,
pageToken=proximo_token_pagina
)
resposta_video = requisicao_video.execute()
for item in resposta_video['items']:
id_video = item['contentDetails']['videoId']
data_video = datetime.strptime(item['contentDetails']['videoPublishedAt'], '%Y-%m-%dT%H:%M:%SZ')
if data_video > datetime.now() - relativedelta(months=6):
try:
info_video = youtube.videos().list(part="snippet,statistics", id=id_video).execute()['items'][0]
contagem_visualizacoes = int(info_video['statistics']['viewCount'])
except HttpError:
print("A cota da API foi excedida. Tentando com a próxima chave...")
break
dados_videos.append({
'Vídeo': info_video['snippet']['title'],
'Publicação': data_video,
'Visualizações': contagem_visualizacoes,
'tags': info_video['snippet'].get('tags', []),
'url': f"https://www.youtube.com/watch?v={id_video}"
})
if data_video > datetime.now() - relativedelta(months=1):
info_canal['Visualizações 30 dias'] += contagem_visualizacoes
if data_video > datetime.now() - relativedelta(months=3):
info_canal['Visualizações 3 meses'] += contagem_visualizacoes
info_canal['Visualizações 6 meses'] += contagem_visualizacoes
proximo_token_pagina = resposta_video.get('nextPageToken')
if not proximo_token_pagina:
break
# Criar DataFrame a partir dos dados coletados
df = pd.DataFrame(dados_videos)
# Verifique se a coluna 'Visualizações' existe antes de tentar ordenar
if 'Visualizações' in df.columns:
# Ordenar os vídeos pelo número de visualizações
df_visualizacoes = df.sort_values(by='Visualizações', ascending=False)
df_visualizacoes.to_excel(escritor, sheet_name=f'{nome_canal} - Visualizações', index=False)
# Ordenar os vídeos pela data de publicação
df_data = df.sort_values(by='Publicação', ascending=False)
df_data.to_excel(escritor, sheet_name=f'{nome_canal} - Data', index=False)
else:
logging.warning(f"Nenhum vídeo encontrado para o canal {nome_canal}. Pular para o próximo canal.")
dados_canais.append(info_canal)
escritor._save()
logging.info("Dados salvos com sucesso.") # substituiu print() por logging.info()
checkpoints[id_canal] = 'complete'
with open('checkpoints.json', 'w') as f:
json.dump(checkpoints, f)
break
except HttpError:
logging.error("A cota da API foi excedida. Tentando com a próxima chave...") # substituiu print() por logging.error()
chaves_api_youtube.remove(chave_api_youtube)
if not chaves_api_youtube:
logging.error("Todas as chaves de API foram usadas. Parando execução.")
sys.exit(1)
continue
except HttpError:
logging.exception("A cota da API foi excedida. Tentando com a próxima chave...")
break
logging.info("Processamento concluído.") # substituiu print() por logging.info()
df = pd.DataFrame(dados_canais)
df.to_excel(escritor, sheet_name='Canais', index=False)
escritor._save()
Eu evidentemente não tenho experiência alguma com APIs e mais apanhei do que qualquer coisa, mas consegui alguns docs com essa brincadeira
Olha só @danielmiorimmorais. Usei uma dessas planilhas pra plotar uma nuvem de palavras. A priori não revela tanta coisa, mas quem sabe em uma análise mais profunda e com auxílio de outros dados.
É exatamente esse tipo de coisa que precisamos. Pode parecer bobo mas é bem sério. Ele nos deu duas informações vitais que nós já sabíamos. O Podpah usa o futebol e o rap pra fazer suas aproximações e então dentro dos vídeos vai mais fundo em assuntos progressistas. Depois, em um determinado momento futuro do projeto, podemos fazer transcrição de todos os vídeos e analisar as transcrições.
Mas, só a metadata já nos revela coisas importantes.
A planilha que eu usei só tem 138 linhas. Se aumentarmos a quantidade de dados, aumentamos a precisão da análise.
Vou trabalhar no código que vc mandou agora a tarde @danielmiorimmorais e ver o que consigo gerar com ele.
Deixe-me então escrever uma versão mais robusta dele antes que possamos usar ele como início.
Já tenho o projeto lá no google cloud. Entrego agora de noite alguma coisa mais com cara de projeto
@danielmiorimmorais OK. Fico no aguardo.
Bom eu não entendo muito sobre isso, mas fiz um mapa visual do sistema, pelo que entendi:
Falta apenas um gerador de gráficos, estatísticas e análise de dados.
Podemos atrelar ao Chat GPT.
@danielmiorimmorais , já ouviu falar do Chat GPT Transcript do youtube? Dá para obter as transcrições dos vídeos automaticamente com esse plugin. E ele é compatível com o Chat GPT, mandando automaticamente para o mesmo... Dá uma olhada, pode ser interessante para verificar até mesmo o que está sendo dito em cada vídeo, daria para fazer uma busca por alguma palavra-chave dentro do vídeo, e não mais necessariamente pelas TAGs do youtube.
Transcrições de vídeos seriam muito interessantes para fazer análises de retórica. Dá pra usar técnicas de NLP pra ajudar nisso.
Sim! Daria para analisar contradições, "linha de pensamento", repertório... E isso só perguntando pro chat GPT, por exemplo:
"Com essa base de dados, em quais vídeos, e em qual minuto, o Mítico do Podpah falou algo que pode significar uma crítica ao Governo vigente?"
É muito desgastante ver video a video, para gerar respostas. Isso aumentaria a velocidade de análise dos vídeos... É óbvio que se o assunto é mais complexo, teria que ver o vídeo inteiro para obter o contexto, mas já dá para identificar e discriminar os dados por categoria, por exemplo:
"De acordo com essa base de dados, quais são os canais de direita que falam bem do Bolsonaro, quais falam mal, quais são Neutros? Quais canais de esquerda criticam o governo vigente, aplaudem, ou são neutros?"
Olha só como funciona:
Pessoal, pela discussão que tivemos lá no fórum, meus tiko e teko martelaram aqui eu pensei no seguinte:
Devido ao volume de dados, o primeiro passo seria construir um pipeline(ou múltiplos) pra colocar as infos em um banco de dados nosso. Isso se quisermos trabalhar com grandes volumes de dados.
Dito isso, acho que podemos estudar como funcionam algumas bibliotecas pra esse fim como o Apache Airflow e o Apache Spark.
Então, em discussão feita no grupo, visualizei esse cenário de atuação da plataforma pra obtermos os dados e persistir. Abaixo vou descrever a atuação de cada ator nesse processo:
Client: Usuário final, quem vai estar acessando a plataforma AB_Service: Serviço responsável por obter os dados dos hosts, persistir no banco de dados e enviar relatórios pros clients. AB_Hosts: Um serviço que entrega pro AB_SERVICE todos os endpoints atuando na obtenção de dados AB_PersonalServer: Serviços que executariam na maquina dos participantes para processar dados de paginas. Talvez por YTDLP ou Por extensao de navegador (mais organico)
@victorhdchagas AB_Hosts seria uma camada de requisições?
Seria o ator responsável por definir quem tem autorizacao pra ser um Ab_PersonalServer, pra indexar todos os Ab_PersonalServer disponivel. Sim, seria uma camada de requisição
channel_ids = ["UCKDjjeeBmdaiicey2nImISw", "UC3NjjjKEtzVCJOu7N9jLXzg", "UClFXhptz1y57TNaMiThz5LA", "UCVUeisucgxABHbP7yZhoyWg", "UCqAjL4g0-FJ4-aocsfQZkZg", "UCgw5xtd8ie9qolehvBeVfxw", "UCe71XVTb_5Z4apQWdvV0J7g", "UC1GDUf4TWS5K_WIF3iWkfgg", "UC3R3XrrqY4xsAfOMbiJ0IGw", "UCP3CtEXi5nxbhei_aBfIOVA", "UC09bGSDmqxLV8RPMJ9hJcOQ", "UC1m0j4cs9NupQPoPebme0Ww", "UCP391YRAjSOdM_bwievgaZA", "UCBQAI1ZhurW3nbtJR-gBBZg", "UCzjtGnD7qqeaHW3nvDVrjQA", "UCYyu1QvD3Y7pvtnNLxCB7Gw", "UCBUqZ4qQIYZUwi_gF-ougBQ", "UCEsbaUdrM_87K-2um30hf8Q", "UC4Tb1JgVxgKvb3MGWXenC3w", "UCTnAz569lU_nwpSmY4daZvg", "UC1_vIFYxbabRjVgs6Q9Tghg", "UC26PMEIkLYO6zb3Lypu6E4w", "UC0ZzgH66n0C7tFOF30QTLzg", "UCBPEQla2aX222gUD-ATy7Aw", "UCI7LgNz2ToBs9GO_mlICJdQ", "UC9WGhpmOdameWOFfHJXkpXg", "UCJei_luJdbCmcmxGis5BFaw", "UC-JW5lciW3a_HMKFgTPJlpg", "UCtbAO1zbJAwd6gDK7Y87JLA", "UCmvhlpPYY8qIyzNkS8fC0EQ", "UC8ioF4r2QoAp5mMp1epPAsg", "UCHKMtfb-4gS18BkxrVAMuDw", "UC11qi4ZlbwfY1dbEplqtuKA", "UC1NP93n5siQcGj174KZsp0Q", "UC24o_5Z5fG-c13Gbs_dWiKQ", "UC3au34_LvMZIK4GtvHNfxmA", "UCldjCveGLU_OjE9altJn9bg", "UCAGHY76tgzPiiXpJtM1sVCg", "UC4CLvdtnUGoq39PYcToSOSw", "UCXtnCmjyPCZuQOEakeFYuNw", "UCqkoOgSof-lNcv8OzRPQj6w", "UC8hGUtfEgvvnp6IaHSAg1OQ", "UC50d2Cgxy574C1IIa305orA", "UC4LTso63Zz2r3s4FfkXqn_g", "UC5mFdAzvfg36j6RjX1A_kyQ", "UCICrr7dnhETvLeurZGmxmmA", "UCLg5SuASzgpRUPefN4dPz4g", "UCXRIQok8uzYtg1TPwSqikVg", "UCSexIeBELLHnNQs9KLQGOKQ", "UCYnCZqaFP1Jhr0UwfbFU33Q", "UCbmVlqWD6lqn1Ur1Zv25IIg", "UC0EildeCLTYHc7sOo2UH8cg", "UCeW2XVfsOkghlDuw6N7b_ag", "UCuiukp_wROL9PdZKm1hxSfA", "UCxiwU16NR_q9kng2BXKSQbQ", "UCxSTU7c_ZZzmP9n6wkg6a2g", "UCr1PUcWPlS9aqdmvcMzp-sA", "UCqpOVi1QsG8DTi9pnVCPbKQ", "UCxI9vN6UbxmBt8VIvUKtJaA", "UC7cO4ABlA6AlaVJsTtzpeTA", "UC4JHxhA05uVEDAf8hpw-TFg", "UCCbPHWmV0DpxUProz6KtYMQ", "UCBWZMatI8ILKxPeaD7UitkA", "UCDjtslsxMMACzs4H02yDXcQ", "UCxPGQWCoUIyBjf62O_hdHEw", "UCgV2iw_02usnAjSu1tXt6mA", "UC52I9XLHCVY0U4p0MkS1sjw", "UCb3FGDCJlNBypvn4HflQQyA", "UCSjXlyiH4jOdRkmwZ0EMYRw", "UCQ0n84jgVkQv7rIU4U-Amhw", "UCkdRRbO4ySKaISiIFgz-x7Q", "UCBtpKi29d2LQTYJWTKPHAvg", "UCW0iVm3gEj56cgSA4qgkelg", "UCaP2PPubZYS_yLXFXFn8MIQ", "UCZmCiYPDBcdQrZPNUnlDdVQ", "UCao9KHtEJhuNpwkBotX_zew", "UCA6f3gzcQIHOt71hQJrrDkg", "UC9Q0wMPcSNmfBe1FGerwXoA", "UCAvgt4zPiEnzEBNK1nheAAg", "UCJhVFmQrFwST__n1yd3YL1Q", "UC9vex4V7rao8np8A8CL7kew", "UCckco9B-odWyRFywuSU7RbA", "UCDaU62jeugyOFYhpm2EcLvw", "UChbILykjNQW-c5PdRxRC-Bw", "UC9MlLx2cgfeYBEfSb9WHrHQ", "UCO5Qyvz0zEQqOuQZ_Wvavng", "UCYiM773ssvNMaBHvaWWeIoQ", "UCI0rgHRUZTqjtGOsRRiMZ8g", "UCDj73eVQ_HPS7L1HllJYvIA", "UC-nrQTbXYEH-Li40MP2cwUQ", "UC0sVhiMKF-87_B2uD6S0BiQ", "UCHZnliu2Veef_9ItM-gan5Q", "UCnYubfU6z1q_51hqG1HGQAg", "UCDwLCn3w_8-q9xwhNC6H5dw", "UC7WpnX9EqdS_XTwd3HEqYwA", "UCuS5kZkyXS2REllcX-oomEQ", "UCLIGXWt_QJN6mN9RWwuApiw", "UCF48wQrSxu7z6M1A2PAxRCA", "UCitie-To0pWGe5Qyk9SjWRA", "UCDJvKsWwZe3V4aVw1wEw2fw", "UC9HT4JrbO6TSfjDHTzFgbPQ", "UCkN5W4skWWC8_BFKYqQLtvg", "UCochWgsCyntL2QkRd6N-9mA", "UCNLtebBa1o18mAx8y0xyNDQ", "UCSED9tRuBwXnlG57hM8p1FA", "UCpLR-q8rFLe_rEAXf4PRxRg", "UC49AXYRJpDmPR5DEKFexdbw", "UCd4RtZszpEY2gqZrtFKTVxQ", "UCj8uD4DVd0wqBglLWSx4Kdw", "UClg2quzZoQeV38Vx7JFACrA", "UCel-VAIVXORe47DTgcq3WLg", "UC0XEJQ1ZlXF0HHsnfyKZZjw", "UCDAjiHmQnkHrIBNai5PiSyg", "UC0Zc-ONerbVcL0HofXLiJ5A", "UCgy-mZZFf9S5t-bu20qFebQ", "UC6KlBoZBta-zkDB2L_nAtqQ", "UCIOFx0QWYe0gkuw9GVX3nIg", "UC6RQhzm93SterWntL7GzqYQ", "UCMw4ZVvLnNiZhXze6HxeFdA", "UCP6L9TPS3pHccVRiDB_cvqQ", "UCXu5r0Ge9IfSZYLdVhW4_bA", "UC7c454AW29TTeBU1fVSpWrw", "UCBeMOGrSCGk_IfMO4oDnEyw", "UCG5UZAV7sSKIwIjlqEenHGA", "UCVLMRyUik9KrSdFdfRUiFgg", "UCJ4L35KkwL-ZA0jedMKqiwQ", "UCbOx4lvR9JmRG89Q5a8Ct7Q", "UCJ19j3nKUgZPMctIAtC7NTg", "UC-V6ATRDw4WwctqUdLW0kcQ", "UC4exbIh1nMjnA1VgajEDIRA", "UC-c3mYz6UXb1hyA5kav53Q", "UC0QAleKDu5n9C17M9vBGxoQ", "UC-L08hspKpKUqBVqL5QeWg", "UCRGECBsWoCO-kRbmEQb3Oiw", "UCQ1AlLIk29K5tNXaONRipIA", "UC44bd_9wvFti9WoairHmmXQ", "UC9Mj2bo0eaIIRzrOoQjYjSA", "UC1ClExt3Pcp10SIxXLbegig", "UCK8y6ZIo0zrFNfoZwTlIiOw", "UC03_yROqHXzt2uxnaRVL9-g", "UC5Bw0GoCfHk9tA_30ltCw9Q", "UC4V29pC1y5L5qwXZL7QoNrg", "UCK6OmGphHtJRMS87ywaJHKQ", "UCzNLsQ28aX6rA47M74oeTyw", "UCnIE5a97mzAAa1vCrqYRXLg", "UCY-8xcFwlVuy6jmUD1DG1Rg", "UCGpECYpBPN-yUf9FkxNueXQ", "UCab3GQuI4PQ8OOPlArgvoBA", "UCB63GoitTyjNv2kJ98H6OxQ", "UCCKVpXua6NV4cunYtiEytWg", "UChAuyhSUGzdUcwqxwxMaegw", "UCJgF-c_ZmBbo9BJWPVzj2SQ", "UChIliNCg4WKQu3gFNZXLyjg", "UCeZupAIGWx6FsFi2ELvERxg", "UC_EzJ_hPHmlPvxeZlB2N_zw", "UCjSEhd5bsoRwknIFQnIGIaw", "UCy3eOZh9n5P3aWWKXmrq6yg", "UC7_vgvSBx0Md1wyCGLrgLRA", "UCARDgcz77ogd21gfi7kiWUQ", "UC8QAdpiEWAOg3AOCCFDCOYw", "UCpHQyEuBXwYBvXC1_NResCg", "UC7PpQV6aBZYxiBH2TpQcKHw", "UCHIXQ0xTCv9n9QIR2-KS20g", "UCbQogQ7pc1an2vS78FFP7ww", "UC_hb3-hOFioxkjGrhvUIbOA", "UCWnjQzvwT33fv3WodrgvC2g", "UCStl60ypbkN7IjZi9SxYBcg", "UCCyg5qwGOWaW3WCWExAndIQ", "UCZYyHef3eBoBEztAOY_Fe_g", "UCcG0j6iWxcvWJDnKTxFesVg", "UCIq4fCuk3yc55868MRFakJw", "UCGuo301AFAeKLS8bdARpehg", "UCPTVIt1Yj3vh4CvyTYGSwdQ", "UCKtQVrhzkSu5gfBkXiMytkQ", "UCklg1LNIaBzLSgQAHjjfIYg", "UC1Vn06hznboN5GqYJDOjERQ", "UCLigDZXcrD1Eh9aW41fAFag", "UCwdBKw59gcCXMdTeUA-6G6A", "UCASgTJ8pQjqI0dyeC3pIz2g", "UCWdGgeyOnCOGnvhNdbVl1dQ", "UCq7H0_ta7jz6KZoNzX0jizA", "UCN3tkVdtXS1ztVsEd-VSZmQ", "UCcS4pKOMtJkmcN7bwIrpQGQ", "UCceIHbuP1FE9LR8KqH1GHRw", "UC2wHoeqJJ2J-ZXYBgDM3dmw", "UC4Bz1gqLEnfoauJDcx2HpAQ", "UCQwMWA9DGUO1cBJhg_wQWsQ", "UCnCLvcoF4iYagNOFA86ooAg", "UCVtAoTfmR6TW07i1h3q3k9Q", "UCyYrZ-UUq2CXZlwXB6B-JBw", "UCrEzK-sDYag2Ic1PTwwCSUA", "UCLmvyoJW1wJlKY-l1KguCSw", "UCWxjyrkp-9Bad1aCvz7Q5-A", "UCuwRjSv3k4YBUnyT4cIqNXA", "UC_Uv10ZCAPgsbTOOBzSKk1w", "UCCD4YygZp2AmkLZC51mXgGw", "UCkSjy-IOEq-eMtarZl2uH1Q", "UCch2L0C1R7aPnnCfESl7qtg", "UCLTWPE7XrHEe8m_xAmNbQ-Q", "UCSyG9ph5BJSmPRyzc_eGC4g", "UCD65KdRPT7v065JR62rR_qQ", "UC-NwgkrLPYmzM-xoLr2GX-Q", "UC-nr9CZ9LglgqMOqSSlzytg", "UCzSM3O0WQJVFpiRVwFWdDWg", "UCjdNqLomrTwrf-RoRwKbF5w", "UCBQPJltFCJo507dso24Geng", "UC30gstEWDqXJ0cCDnfpfebw", "UChCgNcu-o6i6f1rYlfSLzvQ", "UC54AQb7XNGeby0a9uE5eRKg", "UCwQUt82DSiRGsKQ25QnBIlQ", "UC10G36rNrfYAllV9y4ynKsw", "UClQfWNWk9UsUj317ce5FtXg", "UCJhi0NgvmZBZS3AOi8EJMbg", "UCkos901IF6k1_kWB7kGyaDA", "UCtC19BSVvaC-BslznVNFwHA", "UC_pG4d9KgUeq1IRgQN5qg6w", "UC9KfEFJJqjyjDGznUDVvjIA", "UCmArkwjUI8VRHudOjEsVCUw", "UC9tfbf3cWK9FDcNHo9q2C8Q", "UCsFgHFNWP-1I9mCoA59TixA", "UCwxNvk7DUhKQgDmb5t2eDMQ", "UCyUPk70rmP15yKa3TWhfdzg", "UC1_UK9KENF9VMFSaM68Bzhw", "UCsvFdTNmGzIIcLwQELw4Lxw", "UCjuiVJmqil8VdRen0gh3Ewg", "UCUtO_d5nwDQXZQAxLdtE3Gw", "UC1JldUVQv6CxlKXotQasM-g", "UC6h03umaDwocszMZ9-2M35A", "UCH76ut8cWi5N7I8BHT8zuFA", "UCOWeAcISucDzhRUEa9HCZmw", "UCRVeWBBoxQONj27psCHw9Iw", "UChAAlODw69g1Hon-jWoyS5A", "UC-VmSrxlJsZm5QZsp-EhSag", "UC1bBQZcMPJvb3kdHWFvSJfg", "UCUp8DNs5AHVk86IgWvkI8Eg", "UC83JZBnV7h43We_kiamwTzA", "UCl4GlGXR0ED6AUJU1kRhRzQ", "UCnNVgoZZR-itgyYq22fFWnw", "UCoTIpyf8_RGzJ1LPTmvadaA", "UCEA1iFxUnd04HqlA1wX5grQ", "UCDukNmWSGt7RXFSZ627wNkw", "UCz0ldxYfT4lY4rhapGBzHGQ", "UCRILNNrR2agMfONJBQRGbSw", "UChjz-RXIVICrH0SYvtVNwHA", "UC_VZ-oF_pAgz-h8xVWXypaA", "UC2MPnswruXnCsCEkDznyaig"]
Se vamos fazer uma aplicação em camadas, como o diagrama elaborado pelo @memascoli, então acho mais interessante criar uma organização aqui (com o nome Acorda Brasil) e deixar que possamos separar esse trabalho em cada uma das diferentes etapas e camadas. Com essa separação, é possível agilizar certas aplicação enquanto outras podem levar mais tempo para serem construídas A primeira aplicação seria a construção do pipeline para pegar os dados dos canais baseando-se nos códigos elaborados pelo @danielmiorimmorais. A segunda aplicação poderia ser a construção de uma aplicação que buscasse esses dados e realizasse a geração de gráficos úteis com os dados já catalogados
Boa noite galera, como esse repo esta vazio nao tem com abrir nenhuma PR para rabiscarmos algo, entao criei aqui um repo para conversarmos sobre como iremos puxar as metricas usando python. Deem um help la, depois que o pessoal criar um repositorio aqui, jogamos o projeto pra cá.
Oi pessoal, gostaria de contribuir nessa thread.
Análise do crescimento de canais de esquerda e direita nas principais redes sociais:
Acho essa uma boa idea. Para tornar essa ideia possível, uma ideia complementar seria utilizar a api do Youtube que fala sobre o numero de inscritos mensais de qualquer canal. Assim como existem apis do Twitter, Instagram etc. Existe um site chamado https://subscribercounter.com/channel/add-channel-name
- insira a url do canal do youtube e ele mostra em tempo real o número de inscritos. Posso criar um script em python para isso e que envia mensalmente em um relatório sobre isso em nostr(que é um protocolo de rede descentralizado como o twitter)
Através disso podemos ter relatórios automatizados e completos sobre tanto a esquerda quanto a direita com seus números de inscritos.
Fala galera. Tô abrindo essa dicussão aqui pra começarmos a elaborar um plano de ação pra dar forma ao nosso projeto. Conforme nosso amigo Daniel Miorim sugeriu, temos duas frentes para começar.
Análise do crescimento de canais de esquerda e direita nas principais redes sociais:
Visualização de dados com dados referentes a deputados e suas emendas.
Gostaria que discutíssemos nessa issue uma estratégia de plano de trabalho para começarmos a colocar em prática essas análises.