Criando um script de extração de dados em Python 🐍

Francis Dias
10 min readJul 10, 2024

--

Quer criar aprender algo simples e que pode te ajudar muito no dia-a-dia em Python? Ou, quem sabe, quer se abrir e a partir daí deixar a criatividade te convidar a pensar o que pode surgir a partir desse pequeno aprendizado?

Vem entender comigo como eu fiz para passar um conjunto de dados de uma tabela para dentro de um banco de dados do MongoDB.

Feliz da vida de estar escrevendo o meu primeiro artigo aqui

Tirando da caixa hoje algo bem especial, hoje eu quero mostrar a vocês como eu criei na semana passada um extrator de dados utilizando o Python como linguagem. Tive como ponto de partida o objetivo principal de criar uma base de dados dentro do MongoDB para posteriormente poder trabalhar com consultas nestes dados (que são de meu grande interesse — já chego lá e explico pra vocês que dados são esses).

Bom, mas daí tu me pergunta… de onde surgiu toda essa vontade de colocar em prática o python agora?? do nêidahhhh??? E onde tá o valor nisso???

Conta mais!

Conto! Vem comigo!

O exemplo que eu vou mostrar aqui é aplicado aos meus interesses pessoais, voltado ao ecossistema filmes e séries para atender a propósitos de curadoria de filmes. Foi prático de fazer e o resultado foi atingido, então decidi fazer esse tutorial que julguei relevante de ser compartilhado.

Link da branch do projeto: aqui

IMDb: a inspiração e fonte de dados deste tutorial. IMDb, I adore you!

O extrator que eu vou mostrar pra vocês foi idealizado e concebido em alguns dias, e tem por responsabilidade ler os dados de um arquivo baixado da internet no formato TSV (Tab-Separated Values), realizar tratamentos quando necessário (normalização) e salvar os dados em coleções (collections) dentro de um banco de dados no MongoDB.

Quer saber como levar um bulk de dados da internet pra dentro de uma coleção do Mongo para posteriormente poder trabalhar com esta coleção, aplicando filtros, por exemplo?

Eu fui de um arquivo de dados disponibilizado gratuitamente pelo IMDb à criação de uma base de dados no MongoDB com os conhecimentos em Python

Pois bem, para darmos o pontapé inicial na criação do script extrator, precisamos obter um conjunto de dados de interesse. Os arquivos com os dados que serão usados aqui são de propriedade do Internet Movie Database (IMDb) e têm por objetivo fornecer metadados a respeito de filmes (nome, ano de lançamento, gênero, duração, avaliação do filme, número de votos). O IMDb fornece as bases de dados de uso não comercial através do endereço https://developer.imdb.com/non-commercial-datasets/. As bases de dados que serão usadas nesse momento são as seguintes: title.basics.tsv.gz e title.ratings.tsv.gz.

Dentro de uma página de um filme, podemos ver o IMDb rating (canto superior direito). Essa nota é um dos valiosos dados a respeito de filmes que a IMDb oferece para fins não comerciais aos desenvolvedores

Parênteses: o IMDb é referência absoluta em catalogação de dados de filmes, séries de TV e jogos. Eles possuem uma robusta API para fins comerciais através do AWS Data Exchange ao custo de $150k USD/y (tá validado de que tá correta essa informação mesmo, tá? hahaha. Eu quando li precisei ler umas 3 vezes e ainda confirmei a informação pesquisando hehe). Essa API oferece exatamente os mesmos dados que são obtidos através dos bulks não-comerciais gratuitos que estamos utilizando, mais o adicional do Box Office Mojo (um site que rastreia as receitas de bilheteria de forma sistemática e algorítmica).

Robertinho abençoa esse artigo!

Data-mining com pandas

Pandas! A força necessária na manipulação dos dados

Mãos-à-obra no entendimento dos dados. Como etapa inicial, é necessário fazer download dos arquivos title.basics.tsv.gz e title.ratings.tsv.gz . Iremos nesse momento de reconhecimento dos dados, abrir os arquivos, ver os tipos de dados e entender o tipo de mineração nos dados que deve ser feita. Quero dizer com isso que iremos pensar no tipo de dados, nos assegurar de que os dados estão no formato desejado e de que forma iremos limpar eles (deixá-los consistentes e com as informações preenchidas). Basicamente é todo o entendimento de se iremos submeter os dados a alguma espécie de tratamento e como esse tratamento deverá ser feito.

Iremos criar aqui 2 scripts, um para a importação de cada arquivo. Cada um deles irá ler um documento no formato TSV e realizar manipulações (parametrizações) nos dados. Para isso, nada melhor do que usar o pandas, que é uma biblioteca para Python amplamente usada para a manipulação e análise de dados.

Os nossos conjuntos de dados são tabelas, com os dados separados por tabulação. Para a manipulação de tabelas, o pandas possui a estrutura chamada DataFrame, que é a estrutura bidimensional do pandas, similar à tabela, onde cada coluna pode ter o seu tipo de dado. No arquivo title.ratings.tsv, os dados ocupam 3 colunas e estão separados pela tabulação:

tconst averageRating numVotes
tt0000001 5.7 2059
tt0000002 5.6 277
tt0000003 6.5 2026
tt0000004 5.4 179
tt0000005 6.2 2793

No DataFrame do pandas, cada coluna é tratada como uma chave, que pode ser usada para acessar os dados dessa coluna, de modo que o panda lê os dados da tabela acima deste modo:

data = {
'tconst': ['tt0000001', 'tt0000002', 'tt0000003', 'tt0000004', 'tt0000005'],
'averageRating': [5.7, 5.6, 6.5, 5.4, 6.2],
'numVotes': [2059, 277, 2026, 179, 2793]
}

Onde cada coluna possui seus valores, e, após a aplicação da função de conversão dos dados em DataFrame, o DataFrame resultante é:

      tconst  averageRating  numVotes
0 tt0000001 5.7 2059
1 tt0000002 5.6 277
2 tt0000003 6.5 2026
3 tt0000004 5.4 179
4 tt0000005 6.2 2793

Porém, este formato do DataFrame não é suportado pelo Mongo. No MongoDB, os registros individuais são chamados de documentos. Cada documento é uma estrutura de dados JSON/BSON que consiste em pares chave-valor:

[
{'tconst': 'tt0000001', 'averageRating': 5.7, 'numVotes': 2059},
{'tconst': 'tt0000002', 'averageRating': 5.6, 'numVotes': 277},
{'tconst': 'tt0000003', 'averageRating': 6.5, 'numVotes': 2026},
{'tconst': 'tt0000004', 'averageRating': 5.4, 'numVotes': 179},
{'tconst': 'tt0000005', 'averageRating': 6.2, 'numVotes': 2793}
]

Precisararemos realizar uma conversão de modo que os dados sejam transformados em uma lista de dicionários. Um dicionário em Python é uma coleção de itens, onde cada item é um par chave-valor. Com os itens convertidos, poderá ser salvo no banco.

Criando os scripts

Começaremos criando um projeto novo usando o editor de código VSCode — código aqui — e criaremos um arquivo para o script de extração para cada um dos documentos de origem.

Bom, com o projeto criado e as bases de dados baixadas na máquina, iremos elaborar e rodar dois scripts de extração de dados, um para o title.basics, e um para o title.ratings.

Será comum a ambos os scripts:

  1. As importações das bibliotecas necessárias: pandas para a manipulação dos dados, pymongo para realizar a leitura e escrita no MongoDB, e as dependências os e dotenv para a interação com variáveis de ambiente;
  2. A função que realizará a leitura do arquivo bruto dos dados e o converterá em um DataFrame do pandas, tornando-o, assim, apto à manipulação;
  3. Algum tratamento, quando aplicável, nos dados;
  4. A criação de um Mongo Client para realizar a conexão com o servidor MongoDB e o acesso ao banco de dados e coleções que serão criadas;
  5. A conversão do DataFrame para uma lista de dicionários, aceito pelo Mongo para salvamento dos registros;
  6. A inserção dos dados já tratados e na estrutura de dicionário na coleção do Mongo.

Primeiro script: avaliações de filmes

Vamos começar criando o script que irá extrair os dados do arquivo de avaliações de um filme ou série (title.ratings). O script irá extrair todos os parâmetros existentes no arquivo original, composto por:

  • tconst (string) — identificador alfanumérico exclusivo do título
  • AverageRating — média ponderada de todas as avaliações individuais dos usuários
  • numVotes — número de votos que o título recebeu
Recorte dos 8 filmes com as maiores avaliações informadas pelos usuários frequentes do IMDb. Me chamou a atenção que 2 filmes da trilogia The Godfather estão presentes no top10, bem como 2 filmes da trilogia The Lord of the Rings.

Importações

Como os arquivos com os dados possuem origem na máquina local (aka. "meu computador") , tornei estes caminhos variáveis de ambiente dentro do arquivo .env , e fiz o mesmo com o nome das coleções que serão criadas para armazenar cada um dos documentos também. Utilizei para isso as dependências os e dot-env para interagir com elas a partir do .env no ambiente de execução do Python. Enquanto que o os é uma biblioteca padrão do Python, que fornece funções para interagir com o sistema operacional (manipulação de arquivos e diretórios, variáveis de ambiente, etc), o dotenv é uma biblioteca externa usada para carregar variáveis de ambiente a partir de um arquivo .env para o ambiente de execução do Python. A função load_dotenv é utilizada para carregar essas variáveis.

Função de leitura dos dados

Entrando de fato na execução do script, iniciamos escrevendo a função que irá ter como argumento o caminho do arquivo na máquina local. Iremos dar à função o nome de read_tsv :

função de leitura do arquivo TSV.

Dentro da função, o bloco try é usado para tentar executar o código. Nele, eu defini uma variável local "df" para retornar o método de leitura de arquivos read_csv do pandas, que é usado para carregar o código e convertê-lo em um DataFrame do pandas. Passei como argumentos do método: file-path, sep (o tipo de separação) e low-memory, um argumento usado para melhorar a eficiência de leitura de arquivos muito grandes (esta base tem cerca de 1.5M registros). Caso a excecução entre na Exception, irá parar a execução do código e imprimir o erro.

Inserção dos registros no Mongo

função de inserção de registros no Mongo

Adicionei logs nas etapas dentro de cada bloco para ir acompanhando a execução do código.

Dentro do bloco try:

  • Conecta ao MongoDB através da dependência pymongo e seleciona a coleção especificada. Uma coleção é um grupo de documentos no MongoDB, semelhante a uma tabela em um banco de dados relacional. Aqui, estamos criando a collection referente às avaliações (ratings) de um filme dentro plataforma do IMDb;
  • Prepara o documento: converte o DataFrame em uma lista de dicionários, através do método .to_dict() do pandas , pois o MongoDB espera documentos no formato JSON/BSON, conforme já vimos antes. O método .to_dict() espera como parâmetro uma orientação. Para criar registros no MongoDB a partir de um DataFrame do pandas convertido em dicionário, a orientação mais adequada é 'records', onde casa linha é transformada em um dicionário, facilitando a inserção de múltiplos documentos. Assim, cada linha do DataFrame será um BSON.
  • Insere os dados na coleção usando o método insert_many() do pymongo . Este método aceita uma lista de dicionários, onde cada dicionário é um item a ser inserido na coleção.

Após a exception, que irá lançar a exceção em caso de erro, encerramos a conexão com o Mongo dentro do finally.

Executando as funções

Com as funções que executam as atividades já escritas, precisamos concatená-las para rodar o código que irá ler o arquivo e inserir os registros no MongoDB.

Criamos uma função main para a execução principal do script.

  • Dentro do bloco try, é executada a função que lê o arquivo tsv e carrega em um DataFrame, e posteriormente insere os dados do DataFrame no MongoDB, na coleção especificada por collection_name.
  • A linha if __name__ == "__main__": é uma construção comum em scripts Python. Ela é usada para garantir que um certo bloco de código seja executado apenas quando o script é executado diretamente, e não quando ele é importado como um módulo em outro script.

Após instalar o ambiente local, ativar o ambiente local, instalar as dependências, conectar o Mongo e rodar o código, vemos o script iniciar através dos logs espalhados pelo script, e podemos acompanhar a importação dos registros:

Segundo script: metadados essenciais sobre os filmes

Vamos agora criar o script que irá extrair os dados do arquivo de avaliações de um filme ou série (title.basics). O script irá extrair todos os parâmetros existentes no arquivo original, composto por:

  • tconst (string) — identificador alfanumérico exclusivo do título
  • titleType (string) — o tipo/formato do título (por exemplo, filme, curta, série de TV, episódio de TV, vídeo, etc)
  • primaryTitle (string) — o título mais popular/o título usado pelos cineastas em materiais promocionais no momento do lançamento original
  • Title (string) — título original, no idioma original
  • isAdult (booleano) — 0: título não adulto; 1: título adulto
  • startYear (YYYY) — representa o ano de lançamento de um título. No caso de séries de TV, é o ano de início da série
  • endYear (YYYY) — Ano final da série de TV. ‘\N’ para todos os outros tipos de títulos
  • runtimeMinutes — tempo de execução principal do título, em minutos
  • gêneros (array de strings) — inclui até três gêneros associados ao título

Diferentemente do script anterior, iremos realizar algumas transformações nos dados. Em se tratando de um arquivo TSV, os genres de um filme estão armazenados em uma única célula como uma string única, mas semanticamente, representam uma matriz. Esta propriedade precisará ser trabalhada de modo a ser transformada em um array de strings. Além disso, iremos converter em número os tipos de dados numéricos (startYear, endYear e runtimeMinutes).

Dados antes da transformação versus resultado desejado. Tipos de dados: usar tipos numéricos é mais adequado para cálculos e análises. Estrutura de dados: armazenar os gêneros em um array irá trazer vantagens na filtragem e manipulação.

Manipulando dados no DataFrame

Para realizar a transformação da imagem acima, foi feito tratamento nas colunas startYear, endYear, runtimeMinutes e genres.

Função de inserção de registros no mongo: antes de inserir o registro no banco, é feita a manipulação das colunas de interesse do DataFrame.

Dentro do bloco try foi usado o método .to_numeric() do pandas, nas colunas nas quais o tipo do dado é um número. Esse método irá converter o argumento para um tipo numérico. Usamos o parâmetro errors='coerce' de modo a tratar os valores não numéricos, que serão substituídos por NaN. Deste modo, ao invés de levantar um erro, irá aplicar NaN nos valores.

Já no caso da coluna genres, onde temos uma string contendo os gêneros, foi usado um string acessor .str do pandas que permite aplicar funções de string nos elementos da coluna. Junto ao acessor foi aplicado o método .split , que divide cada string na coluna em uma lista de strings, usando a vírgula como separador.

Do mesmo modo que o script anterior, este script possui as funções read_tsv e main próprias, além de file_path e collection_name próprio. Ao rodar este segundo script, obtivemos não somente 1.5M registros, mas sim 10.5M de registros, o que nos indica que muitos títulos não possuem avaliação dentro da base de dados do IMDb.

Um caminho lindo se abre para usar estas bases de dados enormes!

Ufaaa… bastante coisa!! Mas estamos mais fortes ao final rs!

Se você chegou até aqui, curte, comenta, me conta que tipo de conteúdo te desperta interesse no código, e o que gostaria de ver por aqui! Também aceito aquela diquinha de filme!! adorooo hehehe!!

Link da branch do projeto: aqui

--

--