Scrap fácil com o Scrapy

Vinícius Veríssimo
6 min readApr 20, 2019

Nem sempre todos os dados necessários estão disponíveis facilmente para download, muitas vezes é necessária a realização de scraps em página web para a obtenção desses dados.

Da primeira vez que precisei escrever um crawler ou scraper para pegar dados de uma página web em Python os primeiros tutorais e dicas que achei utilizavam as bibliotecas requests e Beautiful Soup para isso. São ótimas bibliotecas, não posso tirar o mérito disso, além de terem me quebrado um galho em alguns momentos.

Apesar dessas e algumas outras bibliotecas para scrap serem boas e funcionais, algumas coisas como a geração de arquivos e o acesso e busca aos elementos da página podem ser um pouco massantes e repetitivos em vários projetos com a mesma finalidade.

O Scrapy é um framework open source e colaborativo para a extração de dados em websites. É rápido, simples e extensível. Ele abstrai muitas dificuldades de tarefas repetitivas quando se está escrevendo um crawler ou um scraper.

Sem mais delongas, vamos colocar as mãos na massa!

O que iremos fazer?

Vou aproveitar a ocasião para explicar um projeto que eu fiz para resolver um problema maior.

Eu estava precisando de um lista de abreviações utilizadas na língua portuguesa, qualquer um que tente fazer qualquer coisa que envolva trabalhar de forma automática com português irá se deparar com a barreira de não ter muitos dados em português, em relação à quantidade de dados em inglês.

Das listas de abreviações que eu encontrei, muitas delas não abrangiam a quantidade de casos que eu queria, mas acabei achando uma lista em uma página da Wikipédia com diversos casos de abreviações.

https://pt.wikipedia.org/wiki/Lista_de_abreviaturas

A ideia será escrever um web scrap para essa página de modo a pegar todas os exemplos de abreviações e suas descrições e salva-los em um arquivo para uma posterior consulta.

Instalação

Podemos instalar o Scrapy utilizando um dos comandos:

  • Pip
$ pip install scrapy
  • Anaconda
$ conda install -c anaconda scrapy

Criando um novo projeto

Criar um projeto novo com o Scrapy é simples, basta escrever no terminal:

$ scrapy startproject AbreviaturasScraper

Esse comando já preparará todo o ambiente e estrutura de pastas necessários para o nosso projeto.

AbreviaturasScraper/
AbreviaturasScraper/
spiders/
__init__.py
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
scrapy.cfg

Alguns desses arquivos possuem variáveis que possibilitam a configuração do funcionamento do Scrapy, como o robô que será utilizado para as requisições e a quantidade de requisições simultâneas. Essas configurações não serão tratadas para esse caso.

Vamos navegar para o diretório com os códigos iniciais e começar a o trabalho

Item

O arquivo items.py irá conter as classes que irão representar os nossos itens ou entidades dos dados.

Como o scrap que iremos fazer são itens simples com apenas dois campos, vamos utilizar a classe já criada pelo Scrapy e adicionar os campos: abrev — para as abreviações e desc — para as descrições das abreviações.

Explorando o HTML

Antes de escrever as nossas regras de scrap, vamos dar uma olhada no HTML da página e buscar onde estão os dados que precisamos.

Inspecionando um dos elementos, teremos algo como:

O dado que nós queremos está dentro de uma div com a classe mw-parser-out e que possui como filha outra div, que não possui id e nem class, que tem como filha um ul com várias li, onde finalmente estão os dados que queremos.

É um longo caminho a se percorrer. Por isso que no usaremos algo para simplificar esse caminho.

No Scrapy podemos descrever esse caminho de duas formas: usando um padrão chamado XPath, uma linguagem voltado para a descrição de caminhos de consulta em arquivos XML; ou utilizando o CSS da página, assim podemos navegar pelos elementos usando os elementos em si e as classes deles.

Nesse caso iremos utilizar a linguagem XPath, não vou explica-la aqui, mas ela é bem simples de se entender, uma vez que você está familiarizado com os conceitos de hierarquias, classes, atributos e conteúdos de elementos HTML.

Tomemos como base a descrição do caminho que queremos:

O dado que nós queremos está dentro de uma div com a classe mw-parser-out e que possui como filha outra div, que não possui id e nem class, que tem como filha um ul com várias li, onde finalmente estão os dados que queremos.

O XPath para esse caso seria:

//div[@class="mw-parser-output"]/div/ul/li

Assim estamos consultando todas as tags li que possuem esse caminho, no caso serão todos elementos da lista de abreviações.

Escrevendo o Scrap

Agora sim finalmente podemos escrever o código que realizará o scrap dos dados:

Primeiramente iremos criar um script chamado AbreviaturasScraper.py no caminho

AbreviaturasScraper/AbreviaturasScraper/spiders/

A estrutura será:

AbreviaturasScraper/
AbreviaturasScraper/
spiders/
__init__.py
AbreviaturasScraper.py
__init__.py
items.py
middlewares.py
pipelines.py
settings.py
scrapy.cfg

No arquivo AbreviaturasScraper.py iremos criar a classe AbreviaturasScraper que estende a classe pai Spider do Scrapy. Assim teremos:

import scrapy
class AbreviaturasScraper(scrapy.Spider):

Essa classe pai pede que preenchamos dois atributos:

  • name — o nome do nosso scraper
  • start_urls — vetor com as URLs que devem ser acessadas pelo scraper
import scrapyclass AbreviaturasScraper(scrapy.Spider):
name = 'AbreviaturasScraper'
start_urls = ['https://pt.wikipedia.org/wiki/Lista_de_abreviaturas']

Agora nós só precisamos atender a mais um requisito da classe Spider: implementar o método parse, que recebe como parâmetro a resposta da requisição.

Nesse método parse é que acontece a mágica.

O objeto com a resposta da requisição contém o HTML da página (além dos demais dados da resposta) e nele podemos utilizar o XPath para encontrar os elementos que queremos. Quando fazemos isso, será retornado um vetor com objetos do tipo Selector que possui o elemento com o qual o XPath “deu match”. Com isso, precisamos apenas iterar sobre esses elementos para obter cada um dos nosso elementos.

import scrapyclass AbreviaturasScraper(scrapy.Spider):
name = 'AbreviaturasScraper'
start_urls = ['https://pt.wikipedia.org/wiki/Lista_de_abreviaturas']
def parse(self, response): for item in response.xpath('//div[@class="mw-parser-output"]/div/ul/li'): yield self.get_values(item.extract())

Repare que não é utilizado o return para retornar os valores e sim o yield, utilizado para a criação de generators em Python.

O método extract do item retorna o conteúdo do elemento em forma de string, no caso, toda a tag li.

O método get_values foi um método que eu fiz para tratar o texto do elemento e retornar um dicionário com os valores de abrev e desc. Primeiramente são removidas todas as tags da string. Depois tenta-se separar a abreviação da descrição pelo símbolo de “ — “. Caso dê errado, tenta-se a separação pelo primeiro espaço encontrado. Se mesmo assim não for possível, considera-se que não há descrição, apenas abreviação.

O código completo é esse:

Executando

Agora é a hora de ver o nosso scraper funcionando.

Vamos para o diretório raiz do nosso projeto, ou seja, o AbreviaturasScraper.

Para ter certeza que está tudo certo execute o comando

$ scrapy list

Se aparecer AbreviaturasScraper, que é o nome que demos para o nosso scraper, então está tudo certo.

Agora precisamos executar o nosso scrap.

Se você executar apenas

$ scrapy crawl AbreviaturasScraper

Você verá várias linhas aparecendo, mas aparentemente nada aconteceu. Na verdade todo o scrap foi feito e apresentado para você, mas não é isso que queremos. Nó precisamos gerar um arquivo a partir desses dados.

O Scrapy já faz isso para a gente, apenas precisamos utilizar o argumento -o com o nome do arquivo e pronto. Assim:

$ scrapy crawl AbreviaturasScraper -o abreviaturas.csv

Pronto, o arquivo abreviaturas.csv será gerado com todos os dados recolhidos da página. No meu caso eu preferi um .csv, mas também poderia ser .json ou qualquer outro suportado pelo framework (veja a documentação).

O Scrapy tem muito mais coisa que isso, até mesmo para tratar paginação e páginas com certos dinamismos. Vale a pena explorar esse framework mais a fundo.

💻 -> Repositório do código

🌐 -> Post no blog

🐙 Github

👤 Lattes

👦 LinkedIn

--

--

Vinícius Veríssimo

Bacharel em Ciência da Computação pela UFPB. Estudante de Mestrado em Computação na UFPB.