Como Analista de Compras sua principal tarefa consiste em identificar qual fornecedor oferece o melhor preço para cada um dos produtos de sua empresa.
Diariamente, você busca no Google Shopping e no Buscapé os preços dos seguintes produtos: iPhone 12 64GB e Placa de Vídeo RTX 3060.
A fim de tornar sua busca mais refinada, você usa os critérios definidos na planilha produtos.xlsx
, a qual especifica os preços mínimo e máximo para cada produto bem como os termos banidos da busca.
Por fim, você sumariza os resultados de sua busca numa tabela que é enviada por e-mail para o seguinte endereço: diegotorrescoder@gmail.com.
Para automatizar este processo, vamos adotar o seguinte passo a passo:
# Importa o webdriver a partir da biblioteca selenium
from selenium import webdriver
# Importa a classe By
from selenium.webdriver.common.by import By
# Importa a classe Keys
from selenium.webdriver.common.keys import Keys
# Importa o gerenciador de webdriver do Google Chrome
from webdriver_manager.chrome import ChromeDriverManager
# Importa a classe Service
from selenium.webdriver.chrome.service import Service
# Importa a função WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
# Importa a função expected_conditions
from selenium.webdriver.support import expected_conditions as EC
# Importa a classe datetime
from datetime import datetime as dt
# Importa o módulo os
import os
# Importa o pandas com o apelido pd
import pandas as pd
# Importa o yagmail
import yagmail
# Importa a função load_dotenv do módulo python-dotenv
from dotenv import load_dotenv
# Faz o download do webdriver do Google Chrome
servico = Service(ChromeDriverManager().install())
# Cria um navegador do Google Chrome
navegador = webdriver.Chrome(service=servico)
# Lê a base de dados em Excel
df_produtos = pd.read_excel('buscas.xlsx')
# Exibe a base de dados
display(df_produtos)
Nome | Termos banidos | Preço mínimo | Preço máximo | |
---|---|---|---|---|
0 | iphone 12 64gb | mini watch usado seminovo | 3000 | 3800 |
1 | rtx 3060 | zota galax usado seminovo | 2300 | 4500 |
def possui_termos_banidos(lista_termos_banidos: list, titulo_anuncio: str) -> bool:
'''Verifica se o título do anúncio contém algum dos termos banidos.'''
possui_termos_banidos = False
for termo in lista_termos_banidos:
if termo in titulo_anuncio:
possui_termos_banidos = True
return possui_termos_banidos
def possui_todos_termos(lista_termos_busca: list, titulo_anuncio: str) -> bool:
'''Verifica se o título do anúncio contém todos os termos da busca.'''
possui_todos_termos_busca = True
for termo in lista_termos_busca:
if termo not in titulo_anuncio:
possui_todos_termos_busca = False
return possui_todos_termos_busca
def tratar_texto_preco(preco: str) -> str:
'''Trata o texto que informa o preço do produto para convertê-lo para decimal.'''
# Trata o texto com o preço do produto
preco = preco.replace('R$', '').replace(' ', '').replace('.', '').replace(',', '.')
# Retorna o preço como um número decimal
return float(preco)
def buscar_ofertas_google_shopping(navegador, produto, termos_banidos, preco_min, preco_max):
'''Faz a busca por ofertas no Google Shopping.'''
# Coloca todas as letras do nome do produto em minúsculas
nome_produto = produto.lower()
# Lista com as palavras do nome do produto
lista_termos_busca = nome_produto.split(' ')
# Coloca todas as letras dos termos de busca em minúsculas
termos_banidos = termos_banidos.lower()
# Lista de termos banidos
lista_termos_banidos = termos_banidos.split(' ')
# Preço mínimo
preco_min = float(preco_min)
# Preço máximo
preco_max = float(preco_max)
# Lista com as ofertas encontradas (lista de tuplas)
ofertas_encontradas = []
# Acessar a página do Google
navegador.get('https://www.google.com.br/')
# Localiza o campo de buscas do Google
campo_busca = navegador.find_element(By.CLASS_NAME, 'gLFyf')
# Digitar nome do produto no campo de buscas
campo_busca.send_keys(nome_produto)
# Dá enter para buscar
campo_busca.send_keys(Keys.RETURN)
# Seleciona todos os elementos da barra de links
links = navegador.find_elements(By.CLASS_NAME, 'hdtb-mitem')
for link in links:
if 'Shopping' in link.text:
link.click()
break
# Seleciona todos os anúncios que possuem a classe especificada
anuncios = navegador.find_elements(By.CLASS_NAME, 'i0X6df')
for anuncio in anuncios:
# Obtém o nome do produto
nome_anuncio = anuncio.find_element(By.CLASS_NAME, 'tAxDx').text
# Coloca todas as letras do nome do produto em minúsculas
nome_anuncio = nome_anuncio.lower()
# Chama a função para verificar se o nome do produto contém algum termo banido
tem_termos_banidos = possui_termos_banidos(lista_termos_banidos, nome_anuncio)
# Chama a função para verificar se o nome do produto contém todos os termos da busca
tem_todos_termos_busca = possui_todos_termos(lista_termos_busca, nome_anuncio)
# Verifica se o produto do anúncio atende aos critérios de busca
if not tem_termos_banidos and tem_todos_termos_busca:
# Obtém o texto com o preço do produto
preco_anuncio = anuncio.find_element(By.CLASS_NAME, 'a8Pemb').text
# Obtém o preço do produto
preco_produto = tratar_texto_preco(preco_anuncio)
# Verifica se o preço no anúncio está no intervalo de preço mínimo e máximo
if preco_produto >= preco_min and preco_produto <= preco_max:
# Elemento de referência (filho) para localizar o link
elemento_filho = anuncio.find_element(By.CLASS_NAME, 'KoNVE')
# Elemento que contém o link
elemento_pai = elemento_filho.find_element(By.XPATH, '..')
# Link do produto
link_anuncio = elemento_pai.get_attribute('href')
# Obtém um carimbo de data e hora atual
carimbo_data_hora = dt.now()
# Formata o carimbo de data e hora
carimbo_data_hora = carimbo_data_hora.strftime('%d/%m/%Y %H:%M:%S')
# Acrescenta uma tupla à lista de ofertas encontradas
ofertas_encontradas.append(
(carimbo_data_hora, 'Google Shopping', nome_anuncio, preco_produto, link_anuncio)
)
# Retorna a lista de ofertas
return ofertas_encontradas
def buscar_ofertas_buscape(navegador, produto, termos_banidos, preco_min, preco_max):
'''Faz uma busca por ofertas no Buscapé'''
# Coloca todas as letras do nome do produto em minúsculas
nome_produto = produto.lower()
# Obtém todos os termos da busca, dividindo o texto da busca com base nos espaços
lista_termos_busca = nome_produto.split(' ')
# Coloca todas as letras dos termos banidos em minúsculas
termos_banidos = termos_banidos.lower()
# Obtém todos os termos banidos
lista_termos_banidos = termos_banidos.split(' ')
# Preço mínimo
preco_min = float(preco_min)
# Preço máximo
preco_max = float(preco_max)
# Lista de ofertas encontradas
ofertas_encontradas = []
# Acessar o site do Buscapé para fazer uma busca
navegador.get('https://www.buscape.com.br/')
# Localiza o campo de busca pelo nome da classe
campo_busca = navegador.find_element(By.CLASS_NAME, 'AutoCompleteStyle_input__HG105')
# Digita a busca no campo de buscas e faz a pesquisa
campo_busca.send_keys(nome_produto, Keys.RETURN)
# Aguarda no máximo 30s para que o elemento seja localizado
elemento = WebDriverWait(navegador, 30).until(
EC.presence_of_element_located((By.CLASS_NAME, 'SearchHeader_titleSection__5SEk9'))
)
# Localiza todos os anúncios
anuncios = navegador.find_elements(By.CLASS_NAME, 'SearchCard_ProductCard_Inner__7JhKb')
for anuncio in anuncios:
# Obtém o nome do produto
nome_anuncio = anuncio.find_element(By.CLASS_NAME, 'SearchCard_ProductCard_Name__ZaO5o').text
# Coloca as palavras do nome do produto em minúsculas
nome_anuncio = nome_produto.lower()
# Chama a função para verificar se o nome do produto possui termos banidos
tem_termos_banidos = possui_termos_banidos(lista_termos_banidos, nome_anuncio)
# Chama a função para verificar se o nome do produto possui todos os termos da busca
tem_todos_termos_busca = possui_todos_termos(lista_termos_busca, nome_anuncio)
if not tem_termos_banidos and tem_todos_termos_busca:
# Obtém o texto com o preço do produto
preco_anuncio = anuncio.find_element(By.CLASS_NAME, 'Text_MobileHeadingS__Zxam2').text
# Obtém o preço do produto
preco_produto = tratar_texto_preco(preco_anuncio)
# Verifica se o preço no anúncio está no intervalo de preço mínimo e máximo
if preco_produto >= preco_min and preco_produto <= preco_max:
# Obtém o atributo href do link
link_anuncio = anuncio.get_attribute('href')
# Obtém um carimbo de data e hora atual
carimbo_data_hora = dt.now()
# Formata o carimbo de data e hora
carimbo_data_hora = carimbo_data_hora.strftime('%d/%m/%Y %H:%M:%S')
# Acrescenta uma tupla à lista de ofertas
ofertas_encontradas.append(
(carimbo_data_hora, 'Buscapé', nome_anuncio, preco_produto, link_anuncio)
)
# Retorna a lista de ofertas encontradas
return ofertas_encontradas
def salvar_resultados(ofertas):
'''Salva os resultados da busca numa planilha do Excel.'''
# Cria um dataframe com as ofertas encontradas
df_ofertas = pd.DataFrame.from_records(
ofertas,
columns=['Data/Hora', 'Local da Busca', 'Produto', 'Preço (R$)', 'Link Oferta']
)
# Caminho absoluto da planilha de ofertas
caminho_absoluto_arquivo_ofertas = os.path.abspath('Ofertas.xlsx')
# Verifica se o arquivo Ofertas.xlsx não existe
if not os.path.exists(caminho_absoluto_arquivo_ofertas):
# Exporta o dataframe para um arquivo intitulado Ofertas.xlsx
df_ofertas.to_excel('Ofertas.xlsx', index=False)
else:
# Lê o arquivo Ofertas.xlsx
df_existente = pd.read_excel('Ofertas.xlsx')
# Concatena os dois dataframes
# df_existente contém os dados da planilha Ofertas.xlsx
# df_ofertas é o dataframe que contém os resultados da busca
df_concatendado = pd.concat([df_existente, df_ofertas])
# Exporta o dataframe concatenado
df_concatendado.to_excel('Ofertas.xlsx', index=False)
for indice, linha in df_produtos.iterrows():
# Obtém o nome do produto
nome_produto = linha['Nome']
# Obtém os termos banidos da busca
termos_banidos = linha['Termos banidos']
# Obtém o preço mínimo
preco_min = linha['Preço mínimo']
# Obtém o preço máximo
preco_max = linha['Preço máximo']
# Obtém as ofertas no Google Shopping
ofertas_google_shopping = buscar_ofertas_google_shopping(navegador, nome_produto, termos_banidos, preco_min, preco_max)
if ofertas_google_shopping:
# Salva os resultados da busca na planilha Ofertas.xlsx
salvar_resultados(ofertas_google_shopping)
# Obtém as ofertas no Buscapé
ofertas_buscape = buscar_ofertas_buscape(navegador, nome_produto, termos_banidos, preco_min, preco_max)
if ofertas_buscape:
salvar_resultados(ofertas_buscape)
# Obtém a data atual
hoje = dt.today()
hoje = hoje.strftime('%d/%m/%Y')
# Lê a base de ofertas
df_ofertas = pd.read_excel('Ofertas.xlsx')
# Obtém somente as linhas com resultados que foram encontrados hoje
df_filtrado = df_ofertas[df_ofertas['Data/Hora'].str.contains(hoje)]
# Caminho absoluto do arquivo .env
caminho_absoluto_arquivo_dotenv = os.path.abspath('.env')
# Carrega as variáveis de ambiente a partir do arquivo .env
load_dotenv(caminho_absoluto_arquivo_dotenv)
# E-mail do remetente
remetente = os.getenv('EMAIL_REMETENTE')
# Senha do e-mail do remetente
senha_email_remetente = os.getenv('SENHA_EMAIL_REMETENTE')
# E-mail do destinatário
destinatario = 'diegotorrescoder@gmail.com'
# Assunto do e-mail
assunto = 'Estratégia de Compras - Produtos Encontrados'
# Corpo do e-mail (mensagem)
mensagem = f'''
<p>Prezados, </p>
<p>Segue em anexo uma tabela atualizada com as ofertas encontradas para nossos produtos.</p>
<p>Estas são as ofertas encontradas hoje:</p>
{df_filtrado.to_html()}
{df_filtrado.to}
<p>Atenciosamene, Diego Moura Torres</p>
'''
try:
# Inicializa uma conexão com um servidor SMTP
yag = yagmail.SMTP(
user=remetente,
password=senha_email_remetente)
# Envia o e-mail para o destinatário
yag.send(to=destinatario, subject=assunto, contents=mensagem)
# Exibe uma mensagem de sucesso
print(f'E-mail enviado com sucesso para {destinatario}.')
except:
# Exibe uma mensagem de erro
print('Erro: Não foi possível enviar o e-mail.')
E-mail enviado com sucesso para diegotorrescoder@gmail.com.