Orion-Hunter / AcordaBrasil

0 stars 0 forks source link

Plano de Trabalho #1

Open Orion-Hunter opened 1 year ago

Orion-Hunter commented 1 year ago

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.

Gostaria que discutíssemos nessa issue uma estratégia de plano de trabalho para começarmos a colocar em prática essas análises.

danielmiorimmorais commented 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)

danielmiorimmorais commented 1 year ago

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.

Caiombr commented 1 year ago

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

danielmiorimmorais commented 1 year ago

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.

Orion-Hunter commented 1 year ago

O primeiro passo é definir quais indicadores iremos coletar. Sugiro a seguinte lista de métricas, usando o Youtube como ponto de partida:

Orion-Hunter commented 1 year ago

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.

danielmiorimmorais commented 1 year ago

3youtube_data.pdf

danielmiorimmorais commented 1 year ago

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

Configuração básica do logging.

logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)

Insira sua chave API do YouTube aqui

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 = []

Criação da API

youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])

Lista de IDs do canal

Carregar IDs dos canais a partir do arquivo JSON

with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']

Verificar se o arquivo Excel já existe

arquivo_excel = 'youtube_canais.xlsx' arquivo_existe = os.path.isfile(arquivo_excel)

Inicializando lista para guardar dados

dados_canais = []

Se o arquivo Excel já existe, ler o DataFrame existente

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.")

Criar DataFrame a partir dos dados coletados dos canais

df = pd.DataFrame(dados_canais)

Salvar o DataFrame em um arquivo Excel

df.to_excel(arquivo_excel, sheet_name='Canais', index=False)

danielmiorimmorais commented 1 year ago

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

Configuração básica do logging.

Isso vai formatar cada mensagem de log para mostrar o tempo que ocorreu e o nível do log.

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 = []

Criação da API

youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])

Lista de IDs do canal

Carregar IDs dos canais a partir do arquivo JSON

with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']

Calcula a data e hora de 64 horas atrás

time_limit = datetime.utcnow() - timedelta(hours=64)

Converte para o formato RFC 3339

time_limit_rfc3339 = time_limit.isoformat("T") + "Z"

Cria um nome de arquivo com a data do dia anterior

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

Save the document

doc.save(filename)

danielmiorimmorais commented 1 year ago

import json from googleapiclient.discovery import build from googleapiclient.errors import HttpError

Carrega as chaves da API do arquivo JSON

with open('api_keys.json') as file: api_keys = json.load(file)

def get_channel_username(channel_id, youtube, api_key): try:

Faz a solicitação para recuperar informações do canal com base no ID

    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

Carrega a lista de IDs de canais do arquivo JSON

with open('ids_canais.json') as file: data = json.load(file)

output_data = []

Itera sobre os IDs dos canais e as chaves da API

for channel_id in data['ids_canais']: for api_key in api_keys:

Cria um objeto de serviço da API do YouTube com a chave atual

    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

Salva os dados de saída em um arquivo JSON

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.")

danielmiorimmorais commented 1 year ago

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

Tente abrir o arquivo de checkpoints. Se não existir, crie um novo.

try: with open("checkpoints.json", 'r') as f: checkpoints = json.load(f) except FileNotFoundError: checkpoints = {}

Configuração básica do logging.

Isso vai formatar cada mensagem de log para mostrar o tempo que ocorreu e o nível do log.

logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s', level=logging.INFO)

Insira sua chave API do YouTube aqui

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 = []

Criação da API

youtube = build('youtube', 'v3', developerKey=chaves_api_youtube[0])

Lista de IDs do canal

Carregar IDs dos canais a partir do arquivo JSON

with open("ids_canais.json", 'r') as f: data = json.load(f) ids_canais = data['ids_canais']

Inicializando lista para guardar dados

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'])

Inicializando o escritor de excel

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()

Criar DataFrame a partir dos dados coletados dos canais

df = pd.DataFrame(dados_canais)

Escrever DataFrame na primeira folha do arquivo Excel

df.to_excel(escritor, sheet_name='Canais', index=False)

Salvar o arquivo Excel

escritor._save()

danielmiorimmorais commented 1 year ago

Eu evidentemente não tenho experiência alguma com APIs e mais apanhei do que qualquer coisa, mas consegui alguns docs com essa brincadeira

danielmiorimmorais commented 1 year ago

dados_youtube.xlsx dados_youtube_teste.xlsx dados_youtube2.xlsx youtube_data.xlsx youtube_definitivo.xlsx youtube_definitivos 2.xlsx youtube_definitivos.xlsx youtube_teste.xlsx

Orion-Hunter commented 1 year ago

image

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.

danielmiorimmorais commented 1 year ago

É 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.

danielmiorimmorais commented 1 year ago

Mas, só a metadata já nos revela coisas importantes.

Orion-Hunter commented 1 year ago

A planilha que eu usei só tem 138 linhas. Se aumentarmos a quantidade de dados, aumentamos a precisão da análise.

Orion-Hunter commented 1 year ago

Vou trabalhar no código que vc mandou agora a tarde @danielmiorimmorais e ver o que consigo gerar com ele.

danielmiorimmorais commented 1 year ago

Deixe-me então escrever uma versão mais robusta dele antes que possamos usar ele como início.

danielmiorimmorais commented 1 year ago

Já tenho o projeto lá no google cloud. Entrego agora de noite alguma coisa mais com cara de projeto

Orion-Hunter commented 1 year ago

@danielmiorimmorais OK. Fico no aguardo.

memascoli commented 1 year ago

Bom eu não entendo muito sobre isso, mas fiz um mapa visual do sistema, pelo que entendi:

Fluxograma do sistema

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.

Orion-Hunter commented 1 year ago

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.

memascoli commented 1 year ago

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?"

memascoli commented 1 year ago

É 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?"

memascoli commented 1 year ago

Olha só como funciona:

  1. ele simplesmente capta as transcrições seja elas geradas automaticamente ou feitas a mão:

image

  1. Depois, você pode interagir com o ChatGPT ou pode colocar uma chave de API:

image

Orion-Hunter commented 1 year ago

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.

victorhdchagas commented 1 year ago

image

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)

Orion-Hunter commented 1 year ago

@victorhdchagas AB_Hosts seria uma camada de requisições?

victorhdchagas commented 1 year ago

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

memascoli commented 1 year ago

IDs dos canais de Direita

Adicione aqui a sua lista de IDs de canais

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"]

jlucfarias commented 1 year ago

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

smilebtw commented 1 year ago

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á.

https://github.com/smilebtw/metrics-py

ghost commented 8 months ago

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.