Como criar um Script Python para pegar informações de maneira mais rápida
- #Python
Publicado também na Medium
Bom dia, boa tarde ou boa noite, guys.
Na última semana eu tive a experiência de pegar um serviço como Freelancer. O serviço não era dificil, ele precisava atualizar um sistema que tinha 47.965 produtos registrados, adicionar imagens e descrição ao produto. O cliente falou que pegava as informações na internet uma a uma, mas queria fazer um jeito mais rápido.
Então logo pensei em fazer um (webscrapper) Script com Python pra acelerar toda essa busca de informações e de pois atualiza o sistema com estas informações coletadas. Como nada na vida é em vão, vou compartilhar como foi que eu fiz a parte de webscrapping de um jeito bem rápido e fácil, falando sobre alguns problemas que tive no meio do caminho, de quebra, vou falar sobre um pouco de PaiTaOn.
Fluxo do Script
Certo, antes de começar o código em si, é bom entender o fluxo para depois entender o porquê de algumas coisas. O que eu queria que o script fizesse:
0. Criar uma pasta onde serão armazenadas as imagens
- Ler o arquivo e encontrar a primeira linha, guardar o código de barras e o nome do produto
2. Entrar no Google e pesquiar por um código de barras ou um nome de produto
3. Pegar os resultados e encontrar o primeiro resultado que fosse compativel com alguns dos sites que eu pré selecionei, que tem boas imagens e descrições detalhadas do produto.
4. Localizar as tags da imagem principal e guardar a informação do “src” desta tag.
5. Localizar as tags de descrição do produto e guardar o texto destas tags como “descrição curta” e “descrição longa”
6. Baixar a imagem e salva-la em uma pasta separada.
7. Atualizar o arquivo xls, xlsx ou csv com as descrições e o caminho da imagem e salva-lo.
8. Repetir
Passo a Passo para Criar o Script
Eu uso uma distro linux no PC, então se algo não fizer muito sentido pra ti, basta pesquisar como faz com o windows que é sucesso.
1. Instalar as Bibliotecas Necessárias
Primeiro, precisamos garantir que todas as bibliotecas necessárias estejam instaladas. Podemos usar pip para instalar essas bibliotecas:
# Importação de módulos do sistema operacional
import os # Fornece funções para interagir com o sistema operacional, como criar diretórios e verificar se arquivos existem
# Importação de módulos para manipulação de datas e URLs
from datetime import datetime # Fornece classes para manipular datas e horários
from urllib.parse import urlparse, parse_qs, urljoin # Fornece funções para manipular URLs e parâmetros de consulta
# Importação de módulos para manipulação de tempo e imagens
import time # Fornece funções para trabalhar com tempo e relógio
from PIL import Image # Fornece classes para manipular imagens
from io import BytesIO # Fornece classes para trabalhar com fluxos de bytes
# Importação de módulos para manipulação de texto e requisições HTTP
import re # Fornece funções para trabalhar com expressões regulares
import requests # Fornece funções para enviar requisições HTTP
# Importação de módulos para manipulação de HTML e dados
from bs4 import BeautifulSoup # Fornece classes para parsear e manipular HTML
import pandas as pd # Fornece classes para manipular dados em estruturas tabulares
# Importação de módulos para interface gráfica
import tkinter as tk # Fornece classes para criar interfaces gráficas
from tkinter import filedialog, messagebox, ScrolledText # Fornece classes para criar caixas de diálogo e outros componentes de interface
# Garantir que openpyxl esteja instalado
try:
import openpyxl # Fornece classes para trabalhar com planilhas do Excel
except ImportError:
import pip # Fornece funções para instalar pacotes Python
pip.main(['install', 'openpyxl']) # Instala o pacote openpyxl se não estiver instalado
2. Criar a Interface Gráfica
Utilizei tkinter para criar uma interface gráfica simples que permite selecionar um arquivo de entrada e uma pasta de saída para as imagens:
# Função para selecionar um arquivo
def select_file():
# Abre uma caixa de diálogo para selecionar um arquivo
file_path = filedialog.askopenfilename(
title="Select file", # Título da caixa de diálogo
filetypes=[ # Tipos de arquivos permitidos
("XLSX files", "*.xlsx"), # Arquivos XLSX
("XLS files", "*.xls"), # Arquivos XLS
("CSV files", "*.csv") # Arquivos CSV
]
)
# Limpa o campo de entrada de arquivo
entry_file.delete(0, tk.END)
# Insere o caminho do arquivo selecionado no campo de entrada
entry_file.insert(0, file_path)
# Função para selecionar uma pasta
def select_folder():
# Abre uma caixa de diálogo para selecionar uma pasta
folder_path = filedialog.askdirectory(title="Select output folder")
# Limpa o campo de entrada de pasta
entry_folder.delete(0, tk.END)
# Insere o caminho da pasta selecionada no campo de entrada
entry_folder.insert(0, folder_path)
3. Definir Funções para Encontrar Imagens e Descrições
Criamos funções para encontrar a tag <img> e as descrições dos produtos nos sites.
Para encontrar as imagens, eu usei 3 métodos: encontrar pelo id da tag, pela class ou pelo src. No final das contas, o que teve menor chance de erros foi usar usar uma expressão regular do src da imagem, pois as classes poderiam se repetir nas páginas algumas vezes, e eram raros os casos em que eu encontrava imagem com id, mas o padrão do src me garantia que iria encontrar a imagem.
Para encontrar as descrições, utilizei o método de procurar pelo seletor CSS específico
# Função para encontrar a tag de imagem em uma página web
def find_img_tag(soup, website):
# Verifica se o website tem um ID de busca especificado
if website['searchId']:
# Procura a tag de imagem com o ID especificado
img_tag = soup.find('img', {'id': website['searchId']})
if img_tag:
# Retorna a tag de imagem se encontrada
return img_tag
# Procura a tag de âncora com o ID especificado (caso a imagem esteja dentro de uma âncora)
img_tag = soup.find('a', id=website['searchId'])
if img_tag:
# Retorna a tag de âncora se encontrada
return img_tag
# Procura a tag de div com o ID especificado (caso a imagem esteja dentro de uma div)
div_tag = soup.find('div', {'id': website['searchId']})
if div_tag:
# Procura a tag de imagem dentro da div
img_tag = div_tag.find('img')
if img_tag:
# Retorna a tag de imagem se encontrada
return img_tag
# Verifica se o website tem uma classe de busca especificada
if website['searchClass']:
# Divide a classe de busca em uma lista de classes (caso haja múltiplas classes)
classes = [cls.strip() for cls in website['searchClass'].split('.')]
# Procura a tag de imagem com a classe especificada
img_tag = soup.find('img', attrs={'class': ' '.join(classes)})
if img_tag:
# Retorna a tag de imagem se encontrada
return img_tag
# Procura a tag de âncora com a classe especificada (caso a imagem esteja dentro de uma âncora)
img_tag = soup.find('a', class_=website['searchClass'])
if img_tag:
# Retorna a tag de âncora se encontrada
return img_tag
# Verifica se o website tem um padrão de URL de imagem especificado
if website.get('srcPattern'):
# Compila o padrão de URL de imagem em uma expressão regular
pattern = re.compile(website['srcPattern'])
# Procura todas as tags de imagem na página
for img in soup.find_all('img'):
# Obtém o atributo src da tag de imagem
src = img.get('src', '')
# Verifica se o atributo src corresponde ao padrão de URL de imagem
if pattern.match(src):
# Retorna a tag de imagem se encontrada
return img
# Retorna None se nenhuma tag de imagem for encontrada
return None
# Função para encontrar as descrições de um produto em uma página web
def find_desc(soup, website):
# Inicializa um dicionário para armazenar as descrições
desc = {'descCurta': '', 'descLonga': ''}
# Verifica se o website tem um seletor de descrição curta especificado
if website['descricaoCurta']:
# Procura a descrição curta usando o seletor especificado
descCurta = soup.select_one(website['descricaoCurta'])
# Verifica se a descrição curta foi encontrada
if descCurta:
# Obtém o texto da descrição curta e remove espaços em branco
desc['descCurta'] = descCurta.get_text(strip=True)
# Verifica se o website tem um seletor de descrição longa especificado
if website['descricao']:
# Procura a descrição longa usando o seletor especificado
descLonga = soup.select_one(website['descricao'])
# Verifica se a descrição longa foi encontrada
if descLonga:
# Obtém o texto da descrição longa e remove espaços em branco
desc['descLonga'] = descLonga.get_text(strip=True)
# Retorna o dicionário de descrições
return desc
4. Função Principal do Script
A função principal do script carrega o arquivo selecionado, percorre cada linha e busca imagens e descrições nos sites especificados:
# Função para executar o script
def run_script():
# Imprime uma mensagem indicando que o script está iniciando
print("Iniciando o script...")
log_area.insert(tk.END, "Iniciando o script...\n")
# Obtém os caminhos do arquivo e da pasta de saída
file_path = entry_file.get()
folder_path = entry_folder.get()
# Verifica se ambos os campos foram preenchidos
if not file_path or not folder_path:
# Exibe um erro se algum dos campos estiver vazio
messagebox.showerror("Error", "Please select both file and output folder")
return
# Verifica o tipo de arquivo selecionado
if file_path.endswith('.xlsx'):
# Lê o arquivo Excel usando a biblioteca pandas
df = pd.read_excel(file_path)
elif file_path.endswith('.xls'):
# Lê o arquivo Excel usando a biblioteca pandas com o engine xlrd
df = pd.read_excel(file_path, engine='xlrd')
elif file_path.endswith('.csv'):
# Lê o arquivo CSV usando a biblioteca pandas
df = pd.read_csv(file_path, delimiter=';') # importante verificar no arquivo CSV que você for usar o tipo de delimiter que ele usa. no meu caso foi o ;
else:
# Exibe um erro se o tipo de arquivo não for suportado
messagebox.showerror("Error", "Unsupported file type")
return
# Define a lista de websites permitidos para a busca
websites = [
# Site Farmácias Nissei
{
'url': 'https://www.farmaciasnissei.com.br/',
'earchId': '',
'earchClass': '',
'rcPattern': 'https://farmaciasnissei\.com\.br/media/.*/[0-9]+(?:-[0-9]+)?(?:_400x400)?\.(jpg|webp)',
'descricaoCurta': '',
'descricao': 'div#myTabContent'
},
# Adicione outros sites conforme necessário
]
# Cria um diretório para armazenar as imagens
current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
image_dir = os.path.join(folder_path, f"images_{current_time}")
os.makedirs(image_dir, exist_ok=True)
log_area.insert(tk.END, f"Pasta criada: {image_dir}...\n")
# Itera sobre as linhas do arquivo
for index, row in df.iterrows():
print(f"Processando linha {index+1} de {len(df)}")
log_area.insert(tk.END, f"Processando linha {index+1} de {len(df)}...\n")
codigo = str(row['GTIN/EAN']).strip().replace('.0', '') # Resgata o código de barras do produto
name = str(row['Descrição']).strip() # Resgata o nome do produto
laboratorio = str(row['Marca']).strip() # Resgata a Marca / Laboratório do produto. Não cheguei a usar esta opção
if 'imgPath' in df.columns:
imgPathColumn = df.loc[index, 'imgPath'] or None
else:
imgPathColumn = None
# Verifica se a imagem já existe
if not pd.isnull(imgPathColumn): # Um callback para verificar se aquela linha onde vou salvar o caminho da imagem á foi preenchida
print(f"Imagem já existe: {imgPathColumn}")
log_area.insert(tk.END, f"Imagem já existe: {imgPathColumn}\n")
continue # Pula pra próxima iteração
# Cria a URL de busca no Google
search_url = f"https://www.google.com/search?q={(codigo+name).strip().replace(' ', '+')}"
# Define o Headers da requisição. Importante para fazer pesquisas no google, já que a google bloqueia pesquisa de dispositivos que não disponibilizam o user agent no header
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'}
log_area.insert(tk.END, f"Pesquisando... {search_url}...\n")
response = requests.get(search_url, headers=headers)
soup = BeautifulSoup(response.content, 'html.parser') # Lê o html recebido da pesquisa
time.sleep(2) # um sleep de 2 segundos para não bloquear as pesquisas por atividade suspeita
# Procura dentro dos resultados, se algum deles é um dos sites pré definidos anteriormente
link = None # Define o url do site que corresponde a pesquisa
web_found = None # Define a referência do site encontrado
# Itera sobre a lista de sites pré definidos para pesquisa
for website in websites:o link da página de destino
# Procura alguma tag 'a' que tenha o href que contenha um valor do url do meu site predefinido
link = soup.find('a', href=lambda t: website['url'] in t and 'google.com' not in t)
if link:
# Pega o href e trata a informação salvando a url e a query dessa url
link_href = link.get('href')
parsed_href = urlparse(link_href)
query_params = parse_qs(parsed_href.query)
if 'url' in query_params:
link_url = query_params['url'][0]
# Guarda a informação de qual site foi encontrado
web_found = website
else:
link_url = link_href
web_found = website
break
# Se o link não for encontrado, pula para iterar o próximo website pré-definido
if not link:
log_area.insert(tk.END, "Não encontramos nenhum url conhecido...\n")
continue
# Guarda o URL do link encontrado.
url = link_url
log_area.insert(tk.END, f"Primeiro resultado encontrado {url}...\n")
# Define a imagem encontrada
image_found = False
# Itera novamente a lista de websites pré-definidos,
for website in websites:
# chega se o site encontrado é o site que está sendo iterado para evitar trabalho desnecessário
if website['url'] != web_found['url']:
continue
# Define o tempo de delay entre tentativas e o numero de tentativas para acessar um site
max_retries = 3
retry_delay = 10
for retry in range(max_retries):
try:
# Tenta acessar o url guardado e define o tempo de espera para aguardar a respota
response = requests.get(url, timeout=20)
break
except requests.exceptions.RequestException as e:
if retry < max_retries - 1:
log_area.insert(tk.END, f"Retrying in {retry_delay} seconds...\n")
time.sleep(retry_delay)
else:
log_area.insert(tk.END, f"Failed to request {url} after {max_retries} retries...\n")
continue
# Recebe a resposta e faz o parse do html
soup = BeautifulSoup(response.content, 'html.parser')
# Usa a função de busca de imagem e busca de descrição para receber as informações necessárias
img_tag = find_img_tag(soup, website)
desc = find_desc(soup, website)
# Faz um pequeno filtro para o src, pois em algumas estruturas as imagens podem ser acessadas direto no servidor sem precisar fazer requisição de uma API. Quando isso acontece, preciso resgatar este endereço e acessar usando a própria url do site
if img_tag:
img_src = img_tag.get('src') or img_tag.get('data-src')
# Faz o download da imagem
if img_src.startswith(('http://', 'https://')):
response = requests.get(img_src)
img_data = response.content
else:
img_src = urljoin(url, img_src)
response = requests.get(img_src)
img_data = response.content
# Cria o arquivo da imagem e escreve o resultado recebido do download
img_path = os.path.join(image_dir, f"{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')+(codigo+name)}.jpg")
image = Image.open(BytesIO(img_data))
image.save(img_path)
image_found = True
log_area.insert(tk.END, f"Imagem baixada e salva em {img_path}...\n")
# Atualiza o arquivo planilha com o caminho da imagem e a descrição
df.loc[index, 'imgPath'] = img_path
if 'descCurta' not in df.columns:
df['descCurta'] = ""
if 'descLonga' not in df.columns:
df['descLonga'] = ""
df.loc[index, 'descCurta'] = desc['descCurta']
df.loc[index, 'descLonga'] = desc['descLonga']
log_area.insert(tk.END, f"Descrição curta: {desc['descCurta']}\n")
log_area.insert(tk.END, f"Descrição longa: {desc['descLonga']}\n")
break
if not image_found:
log_area.insert(tk.END, "Imagem não encontrada...\n")
if file_path.endswith('.xlsx'):
output_file = os.path.join(folder_path, f"output_{current_time}.xlsx")
df.to_excel(output_file, index=False)
else:
output_file = os.path.join(folder_path, f"output_{current_time}.csv")
df.to_csv(output_file, index=False, sep=';')
log_area.insert(tk.END, f"Processamento finalizado. Arquivo salvo em {output_file}\n")
print("Processamento finalizado.")
5. Executar o Script
Para executar o script, basta clicar nos botões da interface gráfica para selecionar o arquivo de entrada e a pasta de saída, e então clicar no botão para iniciar o processo:
if __name__ == "__main__":
# Cria a janela principal da aplicação
root = tk.Tk()
root.title("Image Downloader") # Define o título da janela
# Cria e posiciona um rótulo para a seleção do arquivo de entrada
tk.Label(root, text="Select input file:").grid(row=0, column=0, padx=10, pady=5)
# Cria e posiciona uma caixa de entrada de texto para exibir o caminho do arquivo selecionado
entry_file = tk.Entry(root, width=50)
entry_file.grid(row=0, column=1, padx=10, pady=5)
# Cria e posiciona um botão que, ao ser clicado, abre a caixa de diálogo para selecionar o arquivo
tk.Button(root, text="Browse...", command=select_file).grid(row=0, column=2, padx=10, pady=5)
# Cria e posiciona um rótulo para a seleção da pasta de saída
tk.Label(root, text="Select output folder:").grid(row=1, column=0, padx=10, pady=5)
# Cria e posiciona uma caixa de entrada de texto para exibir o caminho da pasta selecionada
entry_folder = tk.Entry(root, width=50)
entry_folder.grid(row=1, column=1, padx=10, pady=5)
# Cria e posiciona um botão que, ao ser clicado, abre a caixa de diálogo para selecionar a pasta
tk.Button(root, text="Browse...", command=select_folder).grid(row=1, column=2, padx=10, pady=5)
# Cria e posiciona uma área de texto rolável para exibir logs e mensagens de status
log_area = ScrolledText(root, wrap=tk.WORD, width=80, height=20)
log_area.grid(row=2, column=0, columnspan=3, padx=10, pady=5)
# Cria e posiciona um botão que, ao ser clicado, inicia a execução do script
tk.Button(root, text="Start", command=run_script).grid(row=3, column=0, columnspan=3, padx=10, pady=5)
# Inicia o loop principal da aplicação, mantendo a janela aberta
root.mainloop()
Conclusão
Este script é um jeito prático de buscar e baixar imagens de produtos junto com suas descrições, e salvá-las em um arquivo Excel ou CSV. Ele pode ser adaptado para diferentes sites e necessidades específicas que você tiver, inclusive já penso em fazer um pra automatizar apostas na Bet kkkkkk
Enfim, espero que este tutorial algum dia seja útil pra vocês algum dia.




Parabéns pelo esforço e dedicação! Gosto muito de assuntos de webscraping!! E vou salvar teu script pra tentar fazer. Sabe que me apaixonei em fazer webscraping quando comecei a fazer pra coletar dados de rações filtrando os organismos geneticamente modificados, os conservantes e todos os componentes sintéticos das rações. Usei o site da Petlove, que é extremamente dinâmico e muito poluído kkkkk. Mas foi um grande aprendizado. Me voltou umas 3 rações "saudáveis" de toda a lista, mas ok. Abraços e bons estudos!
Parabens pelo seu esforço e dedicaçao .
Esta no caminho certo e pode melhorar ainda mais seu projeto.
Minha sugestao é utilizar django ou flask para melhorar a interface grafica e o admin...
Obrigado por compartilhar.