image

Acesse bootcamps ilimitados e +650 cursos

50
%OFF
Article image
Matheus Cunha
Matheus Cunha08/08/2024 16:21
Compartilhe

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

  1. 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.

Compartilhe
Comentários (2)
Regilene Silva
Regilene Silva - 09/08/2024 10:42

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!


Ronaldo Schmidt
Ronaldo Schmidt - 08/08/2024 18:09

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.