Web Scraping
Web Scraping
O Web scraping é a técnica de extrair dados automaticamente de páginas da web. Em vez de copiar informações manualmente de um site, um script de scraping faz isso de forma programada: ele acessa a página, lê o HTML e extrai os dados desejados.
É amplamente utilizado em tarefas como:
- Coleta de preços de produtos de e-commerce
- Agregadores de notícias
- Monitoramento de dados públicos (como cotações, tempo, ou estatísticas)
- Indexação de conteúdo
- Análise de tendências com dados de redes sociais
Funcionamento do Web Scraping
O processo de scraping geralmente segue estes passos:
- Enviar uma requisição HTTP para a página alvo (com
requests
) - Receber a resposta com o conteúdo HTML da página
- Analisar o HTML com um parser (como
BeautifulSoup
oulxml
) - Localizar os dados desejados dentro do HTML (tags, classes, ids)
- Extrair e estruturar esses dados (em listas, dicionários, CSV, JSON, banco de dados)
requests
Usado para enviar requisições HTTP de forma simples e rápida.
BeautifulSoup
A biblioteca que faz a mágina, permite navegar e extrair informações do HTML.
lxml
Parser muito mais rápido que o padrão, pode ser usado com BeautifulSoup:
soup = BeautifulSoup(resposta.text, "lxml")
Ou diretamente com XPath:
from lxml import html
tree = html.fromstring(resposta.content)
titulo = tree.xpath('//h1/text()')[0]
Exemplo completo: scraping de um título
#!/usr/bin/python3
import requests
from bs4 import BeautifulSoup
import pdb
from datetime import datetime
class Notify:
def __init__(self, url):
#pdb.set_trace()
headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'}
html_site = requests.get(url, headers=headers)
self.soup_parser = BeautifulSoup(html_site.text, 'html.parser')
#print(response.content.decode())
agora = datetime.now()
self.data_formatada = agora.strftime('%d/%m/%Y-%H:%M:%S')
try:
self.valor_a_vista = self.soup_parser.find('h4', class_='finalPrice').text.strip()
except AttributeError:
self.valor_a_vista = 'None'
try:
self.valor_antigo = self.soup_parser.find('span', class_='oldPrice').text.strip()
except AttributeError:
self.valor_antigo = 'None'
try:
self.valor_cheio = self.soup_parser.find('b', class_='regularPrice').text.strip()
except AttributeError:
self.valor_cheio = 'None'
try:
self.infos = self.soup_parser.find('div', class_='generalInfo').text.strip()
except AttributeError:
self.infos = 'Sem informacao'
try:
self.parcelas = self.soup_parser.find('span', class_='cardParcels').text.strip().replace(u'\xa0', u'')
except AttributeError:
self.parcelas = 'Sem informacao'
try:
self.nome_produto = self.soup_parser.find('h1').text.strip()
except AttributeError:
self.nome_produto = 'Sem informacao'
self.coleta_num = [self.valor_a_vista, self.valor_antigo, self.valor_cheio]
self.coleta_info = [self.infos, self.parcelas, self.nome_produto]
self.get_info()
def get_info(self):
#pdb.set_trace()
for num in range(3):
if self.coleta_num[num] == 'None':
continue
self.coleta_num[num] = float(self.coleta_num[num].replace('R$', '').replace(u'\xa0', u'').replace('.', '').replace(',','.'))
# 0 FinalPrice - 1 OldPrice - 2 FullPrice
#print(coleta_num)
self.show()
def show(self):
#pdb.set_trace()
print('\n\n' + self.infos + '\n' + 'Nome do produto: ' + self.nome_produto + '\n' + 'O valor a vista é:', self.coleta_num[0])
print('O valor antigo é:', self.coleta_num[1])
print('O valor Total caso seja parcelado fica em:', self.coleta_num[2])
print(self.parcelas)
with open('arquivo.txt', 'a') as f:
f.write(
'Data da consulta: ' + self.data_formatada +
'\n' + self.infos +
'\n' + 'Nome do produto: ' + self.nome_produto +
'\n' + 'O valor a vista é: ' + str(self.coleta_num[0]) + # precisa converter o valor numérico para string
'\n' + 'O valor antigo é: ' + str(self.coleta_num[1]) + # precisa converter o valor numérico para string
'\n' + 'O valor Total caso seja parcelado fica em: ' + str(self.coleta_num[2]) + # precisa converter o valor numérico para string
'\n' + self.parcelas +
'\n-------------------------------------------------------------------------------------------------------\n'
)
#if valor_a_vista is None:
# print("O valor a vista não foi encontrado na página!")
#if valor_a_vista < valor_minimo:
# print(f'O valor atual ({valor_a_vista}) é menor do que o valor mínimo de R${valor_minimo:.2f}!')
#else:
# print(f'O valor atual ({valor_a_vista}) ainda está acima do valor mínimo de R${valor_minimo:.2f}.')
urls = [
'https://www.kabum.com.br/produto/376222/placa-mae-asus-tuf-gaming-x670e-plus-amd-x670-am5-ddr5-90mb1bj0-m0eay0',
'https://www.kabum.com.br/produto/392052/placa-mae-asus-tuf-gaming-b650m-plus-amd-am5-b650-matx-ddr5-90mb1bg0-m0eay0?gclid=Cj0KCQjw_r6hBhDdARIsAMIDhV97RGydvI6V4zRIC0U_Hyuz_qQ0OmzRKYWZMiDCVo_3xo-22oAxWWcaAkIEEALw_wcB',
'https://www.kabum.com.br/produto/392050/placa-mae-asus-tuf-gaming-b650-plus-amd-am5-b650-atx-ddr5-90mb1by0-m0eay0?gclid=Cj0KCQjw_r6hBhDdARIsAMIDhV9Sv7T16HF1o2ILdgb5nSgCBNytt9weAC5eQubQP3Z1MXGOwkcK5VYaAv8_EALw_wcB',
'https://www.kabum.com.br/produto/378411/processador-amd-ryzen-9-7950x-5-7ghz-max-turbo-cache-80mb-am5-16-nucleos-video-integrado-100-100000514wof',
'https://www.kabum.com.br/produto/378412/processador-amd-ryzen-9-7900x-5-6ghz-max-turbo-cache-76mb-am5-12-nucleos-video-integrado-100-100000589wof',
'https://www.kabum.com.br/produto/320797/processador-amd-ryzen-7-5700x-3-4ghz-4-6ghz-max-turbo-cache-36mb-am4-sem-video-100-100000926wof',
'https://www.kabum.com.br/produto/388091/memoria-kingston-fury-beast-para-intel-xmp-rgb-64gb-2x32gb-5600mhz-ddr5-cl40-preto-kf556c40bbak2-64',
'https://www.kabum.com.br/produto/388081/memoria-kingston-fury-beast-para-intel-xmp-rgb-16gb-2x8gb-5200mhz-ddr5-cl40-preto-kf552c40bbak2-16',
'https://www.kabum.com.br/produto/285970/memoria-kingston-fury-beast-para-intel-xmp-32gb-2x16gb-6000mhz-ddr5-cl40-preto-kf560c40bbk2-32',
'https://www.kabum.com.br/produto/320908/gabinete-gamer-rise-mode-galaxy-glass-mid-tower-lateral-e-frontal-em-vidro-temperado-preto-rm-ga-gg-fb',
'https://www.kabum.com.br/produto/208060/roteador-vpn-multi-wan-gigabit-safestream-tl-er605-smb'
]
if __name__ == "__main__":
for posicao in range(len(urls)):
init_class = Notify(urls[posicao])
Antes de sair fazendo scraping de qualquer site, é importante entender as limitações legais e éticas.
robots.txt
Todo site pode conter um arquivo chamado/robots.txt
(ex:https://example.com/robots.txt
) que define o que pode ou não ser acessado por robôs. Ele não é uma regra técnica, mas sim uma declaração de intenção que deve ser respeitada.Termos de uso
Ler e respeitar os termos de serviço do site. Alguns sites proíbem explicitamente o uso de scrapers em seus termos.Dados pessoais
Nunca colete, armazene ou processe dados pessoais sem consentimento. Isso fere leis como LGPD (Brasil) e GDPR (Europa).Uso responsável
- Faça requisições com intervalo (ex:
time.sleep(1)
) - Identifique seu User-Agent
- Não sobrecarregue os servidores
- Se possível, use a API oficial do site
- Faça requisições com intervalo (ex:
Como evitar bloqueios
Alguins sites podem detectar e bloquear scraping. Para evitar isso:
- Use headers realistas (como User-Agent de navegador)
- Rotacione IPs (proxy)
- Espere entre as requisições
- Use bibliotecas como
fake_useragent
,requests-html
, ou ferramentas comoScrapy
,Playwright