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

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