Ir para o conteúdo

Armazenamento de dados

Ao coletar dados em web scraping, um dos passos mais importantes do processo é decidir como esses dados serão armazenados. O armazenamento eficiente é crucial, pois permite que você acesse, analise e utilize os dados extraídos de maneira prática e organizada.

Scrapy oferece diversas opções de armazenamento, incluindo arquivos CSV e bancos de dados, que atendem a diferentes necessidades e cenários.

Armazenamento em arquivo

Para exportar os dados coletados por uma spider Scrapy para um arquivo CSV, você pode usar um comando simples ao executar sua spider no terminal. O Scrapy possui um recurso de exportação que permite que você salve os itens diretamente em formatos como CSV, JSON ou XML.

$ Execução no terminal!
scrapy crawl shopecommerce -O produtos.csv
$ Execução no terminal!
crawl shopecommerce -O produtos.json
$ Execução no terminal!
crawl shopecommerce -O produtos.xml

Uau! O Scrapy tem várias baterias incluídas e facilitou nossa vida! Mais um requisito do cliente foi atendido e já podemos disponibilizar os dados em CSV.

Ops! Nosso cliente gostou da entrega e solicitou mais um requisito envolvendo armazenamento de dados que não estava previsto inicialmente. O time comercial precisa receber os dados em formato de planilha em Excel.

Vamos ver o que acontece se tentamos pedir ao Scrapy para exportar em formato XLSx.

$ Execução no terminal!
scrapy crawl shopecommerce -O produtos.xlsx
$ Execução no terminal!
Usage
=====
scrapy crawl [options] <spider>
crawl: error: Unrecognized output format 'xlsx'. Set a supported one (('json', 'jsonlines', 'jsonl', 'jl', 'csv', 'xml', 'marshal', 'pickle')) after a colon at the end of the output URI (i.e. -o/-O <URI>:<FORMAT>) or as a file extension.

Ah, o Scrapy não tem uma solução nativa que exporte para planilhas Excel. Que tal criarmos uma pipeline para isso? Agora é sua vez de se desafiar =D

Armazenamento em banco de dados

Uma das opções mais populares e eficientes para esse propósito é o uso do banco de dados SQLite. Iremos criar uma pipeline para inserir os itens coletados diretamente no banco de dados.

Pipeline para armazenamento no SQLite
import sqlite3

class SqlitePipeline:

    def __init__(self): # (1)! 

        self.conn = sqlite3.connect('db.sqlite3') # (2)! 
        self.cursor = self.conn.cursor() # (3)! 

        # (4)! 
        self.cursor.execute(""" 
            CREATE TABLE IF NOT EXISTS product( 
                id INTEGER PRIMARY KEY,
                sku TEXT,
                category TEXT,
                name TEXT,
                image TEXT,
                price TEXT,
                url TEXT,
                description TEXT,
                size TEXT,
                color TEXT
            )
            """)

    def process_item(self, item, spider): #(5) !

        self.cursor.execute( # (6)!
            """
            INSERT INTO product (sku, category, name, image, price, url, description, size, color) 
            VALUES (:sku, :category, :name, :image, :price, :url, :description, :size, :color)
            """,
            item
        )

        self.conn.commit() # (7)!
        return item # (8)!

    def close_spider(self, spider): 
        self.cursor.close() # (9)!
        self.conn.close() # (10)!
  1. Será executado quando o pipeline for ativado pelo spider
  2. Cria a conexão do banco de dados
  3. Cria o cursor que será usado para executar os comandos
  4. Cria a tabela product se não existir
  5. Será ativado toda vez que um item for coletado pela spider
  6. Definição dos dados que serão inseridos no banco de dados
  7. Execução do insert no banco de dados
  8. Retorno do item processado
  9. Fecha o cursor que executa os comandos
  10. Fecha a conexão com o banco de dados

E não podemos esquecer de habilitar a SqlitePipeline no arquivo settings.py. Outra configuração importante é ordem de execução das pipelines. Já temos uma que converte o preço para real e faz sentido para o contexto do projeto que ela seja executada antes dos dados serem armazenados no banco de dados.

Simula o comportamento de um navegador Firefox
ITEM_PIPELINES = {
    "tutorial.pipelines.PriceConversionToBRLPipeline": 100,
    "tutorial.pipelines.SqlitePipeline": 300,
}

Vamos executar a spider e analisar o log de execução:

$ Execução no terminal!
scrapy crawl shopecommerce 
$ Execução no terminal!
pass

No diretório do projeto foi criado o arquivo do banco de dados! E os dados estão lá =D

Vamos fazer mais um teste: Execute novamente a spider. O que acontece com na tabela de produtos?

Os dados que já foram coletados estão sendo salvos novamente a cada execução da spider.

Precisamos melhorar nosso código para evitar isso. Vamos refatorar o método process_item() dessa pipeline para verificar se já existe o produto na base antes de fazer a inserção no banco de dados.

Pipeline para armazenamento no SQLite
    def process_item(self, item, spider):

        self.cursor.execute( # (1)!
            """
            SELECT * FROM product WHERE sku = ?           
            """,
            (item['sku'],)
        )

        result = self.cursor.fetchone()

        if result:
            ('Item already in database: %s - %s\n\n' %(item['sku'], item['name']))
        else:
            self.cursor.execute(
                """
                INSERT INTO product (sku, category, name, image, price, url, description, size, color) 
                VALUES (:sku, :category, :name, :image, :price, :url, :description, :size, :color)
                """,
                item
            )

            self.conn.commit()
        return item
  1. Verifica se já existe o produto na tabela de produtos

Vamos executar a spider novamente e verificar os registros na tabela de produtos no banco de dados.

$ Execução no terminal!
scrapy crawl shopecommerce