Voltar ao blogue
Guias
Ștefan Răcilă11 de abril de 202310 min de leitura

Como fazer um Web Crawler usando Python - Guia para iniciantes

Como fazer um Web Crawler usando Python - Guia para iniciantes

Pré-requisitos

Antes de seguir este artigo, é necessário ter um conhecimento básico de Python e tê-lo instalado no seu computador. Além disso, precisará instalar os módulos Requests e BeautifulSoup. Isso pode ser feito executando o seguinte comando no seu prompt de comando ou terminal:

$ pip install requests bs4

Para a segunda parte deste artigo, onde construiremos um exemplo de rastreador web usando o Scrapy, será necessário instalar o framework Scrapy. Os criadores desta estrutura recomendam vivamente que instale o Scrapy num ambiente virtual dedicado, para evitar conflitos com os pacotes do seu sistema.

Eu sugiro que você instale o virtualenv e o virtualenvwrapper para criar um ambiente Python isolado. Por favor, note que existe uma versão do virtualenvwrapper para Windows chamada virtualenvwrapper-win.

Também será necessário instalar o pipx via pip para instalar o virtualenv.

$ python -m pip install --user pipx

$ python -m pipx ensurepath

Depois de ter criado um ambiente Python isolado, pode instalar o Scrapy com o seguinte comando.

$ pip install Scrapy

Pode encontrar o guia de instalação do Scrapy aqui.

O que é um Web crawler?

Web crawling e web scraping são conceitos relacionados mas distintos. Web scraping é o processo geral de extração de dados de um sítio Web. O Web crawling é a tarefa específica de navegar automaticamente pelas páginas Web para encontrar os URLs que precisam de ser extraídos.

Um Web crawler começa com uma lista de URLs para onde navegar, conhecida como a semente. À medida que navega por cada URL, procura ligações no HTML e filtra-as com base em critérios específicos. Quaisquer novas ligações encontradas são adicionadas a uma fila para processamento futuro. O HTML extraído ou as informações especificadas são então passados para outro pipeline para processamento posterior.

Fluxograma de um pipeline de rastreador web que mostra a lista inicial de URLs, a fila, o download, o processamento, a extração de links e o armazenamento de dados

Ao criar um Web crawler, é importante ter em conta que nem todas as páginas de um sítio Web serão visitadas. O número de páginas que são visitadas depende do orçamento do rastreador, da profundidade do rastreio ou do tempo atribuído à execução.

Muitos sítios Web têm um ficheiro robots.txt que indica quais as partes do sítio Web que podem ser rastreadas e quais as que devem ser evitadas. Além disso, alguns sítios Web têm um sitemap.xml que é mais explícito do que o robots.txt e dá instruções específicas aos robots sobre as páginas que devem ser rastreadas e fornece metadados adicionais para cada URL.

Os Web crawlers são normalmente utilizados para uma variedade de objectivos:

  • As ferramentas de análise de SEO recolhem metadados como o tempo de resposta e o estado da resposta, além de HTML para detetar páginas quebradas e ligações entre diferentes domínios para recolher backlinks.
  • As ferramentas de monitorização de preços rastreiam os sítios Web de comércio eletrónico para encontrar páginas de produtos e extrair metadados, especificamente preços. As páginas de produtos são depois revisitadas periodicamente.
  • Os motores de pesquisa, como o Googlebot, o Bingbot e o Yandex Bot, recolhem todo o HTML de uma parte significativa da Web e utilizam os dados para a tornar pesquisável.

Mais adiante neste artigo, vamos comparar duas abordagens diferentes para construir um web crawler em Python. A primeira abordagem é usar a biblioteca Requests para fazer pedidos HTTP e BeautifulSoup para analisar o conteúdo HTML. E a segunda abordagem é usar um framework de web crawling. Iremos utilizar o Scrapy.

Utilizar as bibliotecas Requests e BeautifulSoup

O módulo requests em Python é uma ferramenta poderosa para efetuar pedidos HTTP. Para o utilizar para o rastreio da Web, pode começar por importar o módulo e fazer um pedido a um URL específico. Por exemplo:

url = 'https://amazon.com/s?k=baby+products'

response = requests.get(url)

Quando tiver a resposta, pode extrair todas as ligações do conteúdo HTML utilizando o BeautifulSoup. Por exemplo:

import json

from urllib.parse import urljoin

from bs4 import BeautifulSoup

html = response.text

links = []

soup = BeautifulSoup(html, 'html.parser')

for link in soup.find_all('a'):

    path = link.get('href')

    if path and path.startswith('/'):

        path = urljoin(url, path)

   

    links.append(path)

print(json.dumps(links, sort_keys = True, indent = 2))

Em seguida, pode iterar através das ligações e fazer pedidos às mesmas, repetindo o processo até ter visitado todas as páginas que pretende rastrear. Esta é uma função recursiva que actua exatamente dessa forma:

import requests

from urllib.parse import urljoin

from bs4 import BeautifulSoup

import logging

logging.basicConfig(

    format='%(asctime)s %(levelname)s:%(message)s',

    level=logging.INFO)

url = 'https://amazon.com/s?k=baby+products'

visited = []

def crawl(url):

    logging.info(f'Crawling: {url}')

    visited.append(url)

    html = ''

    try:

        html = requests.get(url).text

    except Exception:

        logging.exception(f'Failed to crawl: {url}')

        return

        

    soup = BeautifulSoup(html, 'html.parser')

    # here you can extract and store useful data from the page

    for link in soup.find_all('a'):

        path = link.get('href')

        if path and path.startswith('/'):

            path = urljoin(url, path)

        

        if path not in visited:

            crawl(path)

crawl(url)

A função regista uma linha para cada URL que é visitado.

2023-01-16 09:20:51,681 INFO:Rastreamento: https://amazon.com/s?k=baby+products

2023-01-16 09:20:53,053 INFO:Rastreamento: https://amazon.com/ref=cs_503_logo

2023-01-16 09:20:54,195 INFO:Rastreamento: https://amazon.com/ref=cs_503_link

2023-01-16 09:20:55,131 INFO:Rastreamento: https://amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:56,549 INFO:Rastreamento: https://www.amazon.com/ref=nodl_?nodl_android

2023-01-16 09:20:57,071 INFO:Rastreamento: https://www.amazon.com/ref=cs_503_logo

2023-01-16 09:20:57,690 INFO:Rastreamento: https://www.amazon.com/ref=cs_503_link

2023-01-16 09:20:57,943 INFO:Rastreamento: https://www.amazon.com/dogsofamazon/ref=cs_503_d

2023-01-16 09:20:58,413 INFO:Rastreamento: https://www.amazon.com.au/ref=nodl_&nodl_android

2023-01-16 09:20:59,555 INFO:Rastreamento: Nenhum

2023-01-16 09:20:59,557 ERRO:Falha na indexação: Nenhuma

Embora o código de um Web crawler básico possa parecer simples, há muitos desafios que têm de ser ultrapassados para conseguir rastrear com êxito um sítio Web inteiro. Estes incluem questões como:

  • A lógica do URL de descarregamento carece de um mecanismo de repetição e a fila de URLs não é muito eficiente com um elevado número de URLs.
  • O crawler não se identifica e ignora o ficheiro robots.txt.
  • O rastreador é lento e não suporta paralelismo. Cada URL demora cerca de um segundo a ser rastreado e o rastreador espera por uma resposta antes de avançar para o URL seguinte.
  • A lógica de extração de ligações não suporta a normalização de URLs através da remoção de parâmetros de cadeias de caracteres de consulta, não trata URLs de âncora/fragmento relativos (como href="#anchor") e não suporta a filtragem de URLs por domínio ou a filtragem de pedidos para ficheiros estáticos.

Na próxima secção, veremos como o Scrapy aborda estas questões e facilita a extensão da funcionalidade do Web crawler para casos de utilização personalizados.

Como fazer um web crawler em Python usando o framework Scrapy

O Scrapy é uma estrutura poderosa para criar rastreadores da Web em Python. Fornece uma forma integrada de seguir ligações e extrair informações de páginas Web. Terá de criar um novo projeto Scrapy e um spider para definir o comportamento do seu crawler.

Antes de começar a rastrear um site como o Amazon, é importante verificar o arquivo robots.txt do site para ver quais caminhos de URL são permitidos. O Scrapy lê automaticamente este ficheiro e segue-o quando a definição ROBOTSTXT_OBEY está definida para true, que é a predefinição para projectos criados utilizando o comando do Scrapy `startproject`.

Para criar um novo projeto Scrapy, é necessário executar o seguinte comando:

$ scrapy startproject amazon_crawler

Este comando irá gerar um projeto com a seguinte estrutura:

amazon_crawler/

├── scrapy.cfg

└── amazon_crawler

    ├── __init__.py

    ├── items.py

    ├── middlewares.py

    ├── pipelines.py

    ├── settings.py

    └── spiders

        ├── __init__.py

Para criar uma spider use o comando `genspider` da CLI do Scrapy. O comando tem a seguinte definição:

$ scrapy genspider [options] <name> <domain>

Para gerar um spider para este crawler, podemos executar:

$ cd amazon_crawler

$ scrapy genspider baby_products amazon.com

Ele deve criar um arquivo chamado `baby_products.py` dentro da pasta chamada `spiders` e ter este código gerado:

import scrapy

class BabyProductsSpider(scrapy.Spider):

    name = 'wikipedia'

    allowed_domains = ['en.wikipedia.com']

    start_urls = ['http://en.wikipedia.com/']

    def parse(self, response):

        pass

O Scrapy também oferece uma variedade de classes de spider pré-construídas, como CrawlSpider, XMLFeedSpider, CSVFeedSpider e SitemapSpider. A classe CrawlSpider, que é construída sobre a classe Spider base, inclui um atributo extra "rules" para definir como navegar através de um site. Cada regra utiliza um LinkExtractor para determinar quais links devem ser extraídos de cada página.

Para o nosso caso de utilização, devemos herdar a nossa classe Spider da CrawlSpider. Também precisaremos de criar uma regra LinkExtractor que diga ao crawler para extrair links apenas da paginação da Amazon. Lembre-se que o nosso objetivo era recolher dados de todos os produtos para bebés da Amazon, por isso não queremos seguir todos os links que encontrarmos na página.

Depois precisamos criar mais dois métodos na nossa classe, `parse_item` e `parse_product`. O `parse_item` será dado como uma função de callback para nossa regra LinkExtractor e será chamado a cada link extraído. `parse_product` vai analisar cada produto... ¯\_(ツ)_/¯

from scrapy.spiders import CrawlSpider, Rule

from scrapy.linkextractors import LinkExtractor

from bs4 import BeautifulSoup

class BabyProductsSpider(CrawlSpider):

    name = 'baby_products'

    allowed_domains = ['amazon.com']

    start_urls = ['https://amazon.com/s?k=baby+products']

    rules = (

        Rule(

            LinkExtractor(

                restrict_css='.s-pagination-strip'

            ),

            callback='parse_item',

            follow=True),

        )

    def parse_item(self, response):

        soup = BeautifulSoup(response.text, 'html.parser')

        products = soup.select('div[data-component-type="s-search-result"]')

        data = []

        for product in products:

            parsed_product = self.parse_product(product)

            if (parsed_product != 'error'):

                data.append(parsed_product)

        return {

            'url': response.url,

            'data': data

        }

    def parse_product(self, product):

        try:

            link = product.select_one('a.a-text-normal')

            price = product.select_one('span.a-price > span.a-offscreen').text

            return {

                'product_url': link['href'],

                'name': link.text,

                'price': price

            }

        except:

            return 'error'

Para iniciar o rastreador, pode executar:

$ scrapy crawl baby_products

Verá muitos registos na consola (pode especificar um ficheiro de registo com `--logfile [nome_do_ficheiro_de_log]`).

Utilizei o Amazon Search como exemplo para demonstrar os princípios básicos da criação de um rastreador da Web em Python. No entanto, o crawler não encontra muitas hiperligações para seguir e não está adaptado a um caso de utilização específico para os dados. Se pretende extrair dados específicos da Amazon Search, pode considerar a utilização da nossa API de dados de produtos da Amazon. Criámos analisadores personalizados para a página Amazon Search, Product e Category e devolve dados em formato JSON prontos para serem utilizados na sua aplicação.

Porque é que é melhor utilizar um serviço profissional de recolha de dados do que um crawler

Embora o web crawling possa ser uma ferramenta útil para extrair dados da Internet, também pode ser demorado e complexo de configurar. Além disso, a recolha de dados da Web pode ser contrária aos termos de serviço de alguns sítios Web e pode resultar no bloqueio do seu IP ou mesmo em acções legais contra si.

Por outro lado, os serviços profissionais de scraping utilizam técnicas e tecnologias avançadas para contornar as medidas anti- scraping e extrair dados sem serem detectados. Também tratam da manutenção e do dimensionamento da infraestrutura de recolha de dados, permitindo-lhe concentrar-se na análise e utilização dos dados. Além disso, também fornecem um nível mais elevado de exatidão e integridade dos dados, uma vez que são capazes de lidar com casos de utilização de extração de dados mais avançados e podem lidar com trabalhos de extração em grande escala.

Resumo

Em conclusão, embora o rastreio da Web possa ser uma ferramenta útil para extrair dados da Internet, também pode ser demorado e complexo de configurar. Além disso, o Web crawling pode ser contra os termos de serviço de alguns sítios Web e pode resultar no bloqueio do seu IP ou mesmo em acções legais contra si. Por conseguinte, para trabalhos de raspagem mais avançados e em grande escala, é preferível utilizar um serviço de raspagem profissional.

Se procura uma alternativa à recolha de dados por si próprio, considere a utilização do WebScrapingAPI. O WebScrapingAPI é um serviço profissional de raspagem da Web que lhe permite extrair facilmente dados de sítios Web sem a necessidade de criar e manter o seu próprio raspador da Web.

Trata-se de uma solução rápida, fiável e económica, adequada a empresas de todas as dimensões.

Porque não experimenta hoje mesmo! É gratuito e temos um período experimental de 14 dias, sem necessidade de cartão.

Sobre o autor
Ștefan Răcilă, Desenvolvedor Full Stack na WebScrapingAPI
Ștefan RăcilăDesenvolvedor Full Stack

Stefan Racila é engenheiro de DevOps e Full Stack na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante a fiabilidade da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.