Este exercício consiste no desenvolvimento de um crawler, que acessa websites de e-commerce em busca de um conjunto de produtos e ordena os resultados obtidos pelo menor preço.
Esse repositório possui testes automatizados, mas não tem finalidade avaliativa como o avaliador de projetos da Trybe. Aproveite esse momento para aprender e se divertir!
O crawler foi construído, testado e mockado com base nos sites Magalu e Pichau em meados de dezembro de 2022.
⚠️ Os sites podem mudar sua estrutura de tags e classes, nesse caso, o presente código deixará de funcionar na prática, passando a funcionar somente nos testes. Nesse caso, informe o time de currículo para que os devidos ajustes sejam feitos.⚠️
⚠️ Executar o crawler muitas vezes pode gerar um bloqueio temporário do seu IP em um ou mais sites, fazendo com que as buscas deixem de funcionar corretamente, portanto execute somente quando preciso.⚠️
Os testes realizam um mock da requisição, portanto ao utilizar o método requests.get
, uma chamada a um arquivo local será feita.
Com isso, o pytest
poderá ser chamado quantas vezes forem necessárias, uma vez que as requisições não serão feitas ao site real, mas sim ao arquivo de mock correspondente.
A biblioteca utilizada e recomendada para o desenvolvimento deste exercício é a BeautifulSoup4
. Não existindo um gabarito que faça uso de outras bibliotecas como Parsel
, Scrapy
ou similar.
Além disso, como o mock das requisições é feito diretamente no método get
da biblioteca requests
, utilizar uma biblioteca diferente para as requisições HTTP exigirão alterações nos arquivos de teste.
O presente crawler é disposto na seguinte arquitetura:
- Uma classe de dados que representa um produto (em
product.py
) - Uma classe abstrata que representa um website (em
website.py
) - Outras classes concretas de websites, na concepção dos sites Pichau e Magalu (em
websites
) - Funções auxiliares para realizar as consultas, por meio de multithreading (em
aggregator.py
) - Uma pequena CLI para interagir com o sistema (em
__main__.py
) - Diversos testes, em especial para as classes concretas
- Alguns outros arquivos utilitários e complementares
-
Crie o ambiente virtual para o projeto
python3 -m venv .venv && source .venv/bin/activate
-
Instale as dependências
python3 -m pip install -r dev-requirements.`txt`
-
Execute os testes:
python3 -m pytest
-
Execute a interface com linha comando (presente no arquivo
src/__main__.py
):python3 -m src
A atividade prática possui 2 objetivos:``
- melhorar a compreensão do uso prático da Orientação a Objetos em um projeto aplicado;
- aplicar os conhecimentos de web scrapping em um projeto simples, apenas para fins de exercício.
-
Leia atentamente a definição dos atributos e métodos das classes
Product
eWebsite
. Além disso, explore os métodos abstratos presentes na classeWebsite
. -
Leia os comentários do arquivo de testes
test_get_product_price.py
. Os comentários explicam a dinâmica dopytest
, foque no que diz respeito ao uso da classeWebsite
e das classes concretasMagalu
ePichau
.
- Implemente as classes concretas
Magalu
e/ouPichau
, de forma a conseguir os dados dos sites. Você precisará herdar deWebsite
e implementar os métodos abstratos definidos. Para isso, se oriente através das DocStrings (documentação como comentários nos métodos) e das Type Hints (indicativos de tipagem de parâmetros e retornos dos métodos).
⚠️ Atenção: O site real pode estar diferente dos mocks. É recomendável tomar como base os HTMLs presentes na pasta de mocks dos testes.Lá, os arquivos terminados em "full.html" são as páginas de busca completas, enquanto que os outros são o HTML de itens em específico.
Caso esteja difícil realizar os desafios, confira aqui algumas dicas que podem te ajudar no processo.
headers
Lembre-se de mandar um header de user-agent
customizado na sua requisição, caso contrário, você pode ter o acesso ao site recusado.
Faça uma requisição normal pelo seu navegador de internet, copie o header de user-agent
nas ferramentas de desenvolvedor do navegador e cole nos headers da requisição.
_get_product_price
dica genérica 1
Use o regex PRICE_REGEX
, em utils, especificamente seu método PRICE_REGEX.search(string_onde_será_pesquisado_o_preço).group(0)
para pesquisar se uma string dá match com um preço. Você pode ter que verificar se o retorno é diferente de None
antes de usar .group(0)
.
_get_product_price
dica genérica 2
Após o uso da regex, verifique a presença de pontos e vírgulas nas strings de preço, substituindo os incorretos pelo padrão americano (pontos para separar centavos) para só então passar para a função float(string_numérica)
.
_get_product_url
As urls estão dentro do atributo href
da primeira tag a
. Você pode ter que verificar se o atributo retorna uma string ou uma lista de strings para satisfazer o verificador de tipagem.
_get_search_page_with_search_results
A página de buscas funciona passando o termo de busca em /busca/termo+de+busca
, transformando espaços em sinais de mais.
_get_products_html
Os produtos estão dentro de uma tag li
que possuem as classes sc-fCBrnK hYPKVt
.
_get_product_price
Os preços estão dentro de uma tag p
, com o atributo data-testid
com o valor price-value
.
_get_product_name
Os nomes estão dentro de uma tag h2
, com o atributo data-testid
com o valor product-title
.
_get_search_page_with_search_results
A página de buscas funciona passando o termo de busca como valor do parâmetro q
da requisição.
_get_products_html
Os produtos estão dentro de uma tag a
, com o atributo data-cy
com o valor list-product
.
_get_product_price
Os preços estão dentro de uma tag div
, com o atributo class
com o valor jss83
.
_get_product_name
Os nomes estão dentro de uma tag h2
.
Caso queira ir além, após o momento prático você pode tentar adicionar um novo website ao projeto: basta criar uma classe que herde de Website
e respeite sua interface. Adicione essa classe na lista de classes utilizadas em src/websites/__init__.py
para que ela funcione em sua CLI, e verá o poder da Orientação a Objetos.
Os testes, caso queira criar, também são simples: basta adicionar novos valores para as parametrizações existentes nas funções já criadas.