rennerocha.com/content/posts/validando-dados-extraidos-com-scrapy.md
2022-01-05 23:45:30 -03:00

5 KiB

title publishdate tags
Validando dados extraídos com Scrapy 2019-03-08
scrapy
scraping
monitoramento

Como você garante a qualidade e a confiabilidade dos dados que você está extraindo em seu crawler?

Se o seu crawler foi desenvolvido com Scrapy, você tem a disposição a extensão Spidermon, que junto com a biblioteca Schematics permite que você inclua validações de dados de maneira simples e altamente customizável.

Definindo modelos de dados, é possível comparar os items retornados com uma estrutura pré-determinada, garantido que todos os campos contém dados no formato esperado.

Nosso exemplo

Para explicar o uso do Spidermon para realizar a validação dos dados, criamos um projeto de exemplo simples, que extrai citações do site http://quotes.toscrape.com/.

Começamos instalando as bibliotecas necessárias:

{{}} $ python3 -m venv .venv $ source .venv/bin/activate (.venv) $ pip install scrapy spidermon schematics {{}}

Em seguida criamos um novo projeto Scrapy:

{{}} $ scrapy startproject quotes_crawler $ cd myproject & scrapy genspider quotes quotes.com {{}}

Definimos um item que será retornado pelo nosso spider:

{{}}

quotes_crawler/spiders/items.py

import scrapy

class QuoteItem(scrapy.Item): quote = scrapy.Field() author = scrapy.Field() author_url = scrapy.Field() tags = scrapy.Field() {{}}

E finalmente criamos o nosso spider:

{{}}

quotes_crawler/spiders/quotes.py

import scrapy from myproject.items import QuoteItem

class QuotesSpider(scrapy.Spider): name = "quotes" allowed_domains = ["quotes.toscrape.com"] start_urls = ["http://quotes.toscrape.com/"]

def parse(self, response):
    for quote in response.css(".quote"):
        item = QuoteItem(
            quote=quote.css(".text::text").get(),
            author=quote.css(".author::text").get(),
            author_url=response.urljoin(
                quote.css(".author a::attr(href)").get()),
            tags=quote.css(".tag *::text").getall(),
        )
        yield item
    yield scrapy.Request(
        response.urljoin(response.css(
            ".next a::attr(href)").get())
    )

{{}}

Com isso já é possível executar o nosso crawler e obter os dados desejados:

{{}} $ scrapy crawl quotes -o quotes.csv {{}}

Definindo nosso modelo de validação

Schematics é uma biblioteca de validação de dados baseada em modelos (muito parecidos com modelos do Django). Esses modelos incluem alguns tipos de dados comuns e validadores, mas também é possível extendê-los e definir regras de validação customizadas.

Baseado no nosso exemplo anterior, vamos criar um modelo para o nosso item contendo algumas regras básicas de validação:

{{}}

quotes_crawler/spiders/validators.py

from schematics.models import Model from schematics.types import URLType, StringType, ListType

class QuoteItem(Model): # String de preenchimento obrigatório quote = StringType(required=True)

# String de preenchimento obrigatório
author = StringType(required=True)

# Uma string que represente uma URL de preenchimento obrigatório
author_url = URLType(required=True)

# Lista de Strings de preenchimento não obrigatório
tags = ListType(StringType)

{{}}

Precisamos agora habilitar o Spidermon e configurá-lo para que os campos retornados pelo nosso spider sejam validados contra esse modelo:

{{}}

quotes_crawler/settings.py

SPIDERMON_ENABLED = True

EXTENSIONS = { "spidermon.contrib.scrapy.extensions.Spidermon": 500, }

ITEM_PIPELINES = { "spidermon.contrib.scrapy.pipelines.ItemValidationPipeline": 800 }

SPIDERMON_VALIDATION_MODELS = ( "quotes_crawler.validators.QuoteItem", )

Inclui um campo '_validation' nos items que contenham erros

com detalhes do problema encontrado

SPIDERMON_VALIDATION_ADD_ERRORS_TO_ITEMS = True {{}}

Conforme os dados forem extraidos, eles serão validados de acordo com as regras definidas no modelo e, caso algum erro seja encontrado, um novo campo _validation será incluído no item com detalhes do erro.

Caso um item receba uma URL inválida no campo author_url por exemplo, o resultado será:

{{}} { '_validation': defaultdict( <class 'list'>, {'author_url': ['Invalid URL']}), 'author': 'C.S. Lewis', 'author_url': 'invalid_url', 'quote': 'Some day you will be old enough to start reading fairy tales ' 'again.', 'tags': ['age', 'fairytales', 'growing-up'] } {{}}