Executando verificação de segurança...
2

Python - Como fazer

Gente eu juro que tentei de tudo antes de perguntar aqui, mas parece que as vezes só me sinto um burro de não saber o que parece ser o básico e nem como pesquisar. Alguém pode me dar uma luz?

Eu to tentando fazer uma "automação" em selinium usando python, mas minha dúvida em si nem é essa, a parte do selinium consegui terminar já. Minha dúvida é em strings.

Eu queria fazer um object / json com as informações que obtive de uma lista de string, mas eu simplesmente não consigo. A informação da lista está dessa forma:

Universo De Sandman: País Dos Pesadelos Vol. 1
R$49,90
Adicionar ao Carrinho
Adicionar à lista de desejos
A Saga Da Liga Da Justiça Vol. 7
R$39,90
Adicionar ao Carrinho
Adicionar à lista de desejos
Sandman: Prelúdio - Edição Definitiva
R$180,90
Adicionar ao Carrinho
Adicionar à lista de desejos
Universo DC Por Neil Gaiman
R$104,90
Adicionar ao Carrinho
Adicionar à lista de desejos

Ou seja, a cada 4 linhas é um novo produto, queria deixar algo mais ou menos assim:

{
    {
    "Nome": "Universo De Sandman: País Dos Pesadelos Vol. 1"
    "Preco": "49,90"
    "Carrinho": "Adicionar ao Carrinho"
    "Lista": "Adicionar à lista de desejos"
    },
    {
    "Nome": "A Saga Da Liga Da Justiça Vol. 7"
    "Preco": "R$39,90"
    "Carrinho": "Adicionar ao Carrinho"
    "Lista": "Adicionar à lista de desejos"
    }
}

Mas eu realmente não consegui pensar em uma estratégia para fazer isso. Alguém sabe auxiliar?

Meu código atual:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select

import time

driver = webdriver.Edge()

driver.get("https://panini.com.br/")
time.sleep(3)
search = driver.find_element(By.ID, 'search')
search.send_keys('Universo Sandman')
search.submit()
time.sleep(3)

find_sorter = driver.find_element(By.ID, 'sorter')
select_sorter = Select(find_sorter)
select_sorter.select_by_visible_text('Mais recentes')

produtos = driver.find_element(By.CLASS_NAME, 'products-grid')
texto_produtos = produtos.text
lista_produtos = texto_produtos.split("\n")

array1 = []
i = 0

for produto in lista_produtos:
    if (i < 4):
        print(produto) 
        i = i + 1
    if (i == 4):
        print("----")
        i = 0
Carregando publicação patrocinada...
2

tl;dr

Eu não usaria Selenium para este caso, pois existem libs mais adequadas para web scraping. Em todo caso, seguem algumas soluções (com e sem Selenium).

Selenium

Se a ideia é continuar com Selenium, existem várias formas de iterar pelas linhas da string. Por exemplo, criando um iterador, e chamando next para obter a próxima linha:

# rodar selenium, obter o texto...
texto_produtos = produtos.text

linhas = iter(texto_produtos.split("\n"))
result = []
while True:
    try:
        result.append( { "Nome": next(linhas), "Preco": next(linhas), "Carrinho": next(linhas), "Lista": next(linhas) } )
    except StopIteration:
        break
print(result)

Assim, cada chamada de next traz a próxima linha, e aí basta colocá-las no dicionário, e adicioná-lo na lista. O chato é que quando um iterador se encerra, ele lança um StopIteration, que precisa ser capturado, e aí sabemos que o loop pode ser interrompido (com break).


No módulo itertools existe também uma "receita pronta" para iterar qualquer sequência de N em N elementos:

# rodar selenium, obter o texto...
texto_produtos = produtos.text

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

result = []
for nome, preco, carrinho, lista in grouper(texto_produtos.split("\n"), 4):
    result.append( { "Nome": nome, "Preco": preco, "Carrinho": carrinho, "Lista": lista } )
print(result)

Agora não preciso capturar o StopIteration, e repare que no for já posso pegar cada um dos 4 elementos separadamente.


E também posso usar list comprehension para obter o mesmo resultado:

# rodar selenium, obter o texto...
texto_produtos = produtos.text

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

result = [ { "Nome": nome, "Preco": preco, "Carrinho": carrinho, "Lista": lista } for nome, preco, carrinho, lista in grouper(texto_produtos.split("\n"), 4)]

print(result)

Por fim, também pode ser usado o código sugerido no outro comentário:

# rodar selenium, obter o texto...
texto_produtos = produtos.text

lista_produtos = texto_produtos.split("\n")
result = []
for i in range(0, len(lista_produtos), 4):
    nome, preco, carrinho, lista = lista_produtos[i:i + 4]
    result.append( { "Nome": nome, "Preco": preco, "Carrinho": carrinho, "Lista": lista } )
print(result)

Sem Selenium

Como eu já disse, nesse caso não precisa de Selenium, existem bibliotecas mais apropriadas para web scraping. Vamos ver uma delas: o Beautiful Soup.

Mas antes um detalhe: repare que você está preenchendo o campo de busca e mudando o filtro para "mais recentes". Ao fazer isso, a URL resultante é https://panini.com.br/catalogsearch/result?q=Universo+Sandman&product_list_order=most_recent, então você pode acessá-la diretamente, em vez de dar toda essa volta de abrir o browser, digitar, esperar o resultado, etc...

Beautiful Soup

Primeiro, aqui tem a documentação e instruções de instalação (python -m pip install beautifulsoup4).

Depois, instale também o módulo requests (python -m pip install requests) para fazer a requisição HTTP e obter o HTML. Assim é mais direto do que abrir o browser com o Selenium, pois só o que interessa é o conteúdo do HTML (vc pode usar outros também, existem várias libs para fazer requests HTTP, o importante é que no final vc tenha o HTML para repassar para o Beautiful Soup).

from bs4 import BeautifulSoup
from urllib.request import urlopen

# faz o request para a página e passa o HTML para o Beautiful Soup
url = 'https://panini.com.br/catalogsearch/result?q=Universo+Sandman&product_list_order=most_recent'
soup = BeautifulSoup(urlopen(url).read().decode("utf-8"), "html.parser")
# filtra linhas em branco e um texto que não interessa
lista_produtos = list(filter(lambda s : len(s.strip()) > 0 and s != 'Adicionar para Comparar',
                             soup.find('div', {'class': 'products-grid'}).get_text(separator='\n').split('\n')))

Mas repare que o Beautiful Soup trata um pouco diferente o conteúdo, tanto que o texto vem com muitos espaços e quebras de linha a mais, além de trazer também o texto "Adicionar para Comparar". Então precisei fazer um tratamento adicional para ter uma lista igual a que você obtém com o Selenium.

Depois disso, é só escolher um dos métodos acima para ler a lista de 4 em 4 elementos.


Outra opção é, em vez de pegar todo o texto, simplesmente buscar cada elemento que você quer. Dá um pouco mais de trabalho e precisa fuçar a estrutura da página, mas não tem jeito. Pelo menos você vai direto no elemento que tem a informação:

from bs4 import BeautifulSoup
from urllib.request import urlopen

url = 'https://panini.com.br/catalogsearch/result?q=Universo+Sandman&product_list_order=most_recent'
soup = BeautifulSoup(urlopen(url).read().decode("utf-8"), "html.parser")

result = []
# para cada item retornado
for product in soup.find_all('div', {'class': 'product-item-details'}):
    # procura o nome do produto
    nome = product.find('a', {'class': 'product-item-link'}).text
    if not nome: # por algum motivo, tinha algum elemento com o nome vazio
        continue # nesse caso, vai para o próximo
    # procura o preço do produto
    preco = product.find('span', {'class': 'price'}).text
    # procura o "carrinho"
    carrinho = product.find('button', {'class': 'tocart'})
    if carrinho is None: # quando não tem estoque, não aparece o carrinho
        carrinho = product.find('div', {'class': 'unavailable'}).text
    else:
        carrinho = carrinho.text
    # procura "adicionar a lista de desejos"
    lista = product.find('a', {'class': 'towishlist'}).find('span').text

    result.append( { "Nome": nome, "Preco": preco, "Carrinho": carrinho, "Lista": lista } )

print(result)

Outras opções

Existem várias bibliotecas para web scraping, como o Scrapy, entre outras. Sugiro testá-las para ver como são, mas nesse caso específico, Selenium não me parece a melhor opção. Não há necessidade de abrir um browser, buscar, clicar em tal elemento, quando você pode obter o HTML acessando a URL diretamente (na página que vc acessa, não precisa ficar rodando JavaScript para que certos elementos apareçam, etc, situação na qual o Selenium é mais apropriado).

2

eu tambem não sabia a resposta completa, mas talvez ajude na parte de pesquisar, peguei essa string grande

Universo De Sandman: País Dos Pesadelos Vol. 1
R$49,90
Adicionar ao Carrinho
Adicionar à lista de desejos
A Saga Da Liga Da Justiça Vol. 7
R$39,90
Adicionar ao Carrinho
Adicionar à lista de desejos
Sandman: Prelúdio - Edição Definitiva
R$180,90
Adicionar ao Carrinho
Adicionar à lista de desejos
Universo DC Por Neil Gaiman
R$104,90
Adicionar ao Carrinho
Adicionar à lista de desejos

e a primeira coisa q pensei foi em como separar as linhas, dividir elas pelo sepador de quebra de linha parece uma boa opção variavel.strip().split("\n") o strip é só pra tirar espaços e formatações residuais, isso gerou

['Universo De Sandman: País Dos Pesadelos Vol. 1',
 'R$49,90',
 'Adicionar ao Carrinho',
 'Adicionar à lista de desejos',
 'A Saga Da Liga Da Justiça Vol. 7',
 'R$39,90',
 'Adicionar ao Carrinho',
 'Adicionar à lista de desejos',
 'Sandman: Prelúdio - Edição Definitiva',
 'R$180,90',
 'Adicionar ao Carrinho',
 'Adicionar à lista de desejos',
 'Universo DC Por Neil Gaiman',
 'R$104,90',
 'Adicionar ao Carrinho',
 'Adicionar à lista de desejos']

aqui eu tinha idea de como fazer em outra linguagem mas não no python, minha ideia foi, como esta tudo dividido preciso agrupar eles corretamente, pesquisei por split list in sublists e vi q não tem uma funções padrão como a q conheço pra fazer isso então, vai ter q ser em laço mesmo. essa pesquisa tambem deu resposta de como fazer isso com laço de repetição e adaptando o tamanho do grupo

for i in range(0, len(tmp), 4):
   chunk.append(tmp[i:i+4])

isso resulta em

[['Universo De Sandman: País Dos Pesadelos Vol. 1',
  'R$49,90',
  'Adicionar ao Carrinho',
  'Adicionar à lista de desejos'],
 ['A Saga Da Liga Da Justiça Vol. 7',
  'R$39,90',
  'Adicionar ao Carrinho',
  'Adicionar à lista de desejos'],
 ['Sandman: Prelúdio - Edição Definitiva',
  'R$180,90',
  'Adicionar ao Carrinho',
  'Adicionar à lista de desejos'],
 ['Universo DC Por Neil Gaiman',
  'R$104,90',
  'Adicionar ao Carrinho',
  'Adicionar à lista de desejos']]

não é exatamente o que vc queria, mas iterando esses elementos vc consegue chegar no formato que quer.

mas pra pesquisar pode ver q peguei seu problema , pegar uma string e colocar as linhas em grupos especificos em 2 problemas, separar os itens da string (pois é mais simples trabalhar com listas do q com string) e trasforma isso em lista que é muito melhor de se trabalhar, dai sim eu pesquisei como agrupar os itens da lista

pra pesquisar é melhor usar problemas mais simples, então tente seperar seu problema em varios passos ( tu pode não saber oq vai precisar fazer em todos os passos, mas se vc conseguir saber o q precisa ser feito no proximo passo vc consegue pregredir de passo em passo ), agora um proximo passo seria como transforma uma lista de listas em uma lista de dicionario