Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Erro no get_text()

Eu estou fazendo um scraping de umas informações de preço, mas me parece que existe um erro no get_text() que eu ainda não entendi

De primeria, eu estou tentando uma estrutura mais simples de print

from urllib.request import urlopen
from bs4 import BeautifulSoup

url = 'https://tepbac.com/gia-thuy-san/cat/2-thuy-san/'

response = urlopen(url)
html = response.read()

soup = BeautifulSoup(html, 'html.parser')

print(soup.find('a', class_="h6").get_text())
print(soup.find('td', class_="text-right text-danger font-weight-bold").get_text())
print(soup.find_next('td').get_text())
print(soup.find('td', class_="text-truncate").get_text())

e me vem o erro

Eu estou usando o find _next no terceiro item porque quando eu descrimino no class correto {'class':'text-right'}) ele me repete o valor do segundo {'class':'text-right text-danger font-weight-bold'}— que é o preço e não a data

AttributeError                            Traceback (most recent call last)
<ipython-input-54-b84b9a4eca20> in <module>
     11 print(soup.find('a', class_="h6").get_text())
     12 print(soup.find('td', class_="text-right text-danger font-weight-bold").get_text())
---> 13 print(soup.find_next('td').get_text())
     14 print(soup.find('td', class_="text-truncate").get_text())
     15 

AttributeError: 'NoneType' object has no attribute 'get_text'

Claro que esse print é só para checar se o scraping está funcionando. Eu preciso puxar uma tabela inteira!

Então, eu tenho um for que eu estou ajustando para fazer o meu df

for item in listings:
    market = {}

    market['specie'] = item.find('a', {'class':'h6'}).getText()
    market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
    market['date'] = item.find('td', {'class':'text-right'}).getText()
    market['city'] = item.find('td class', {'a rel':'nofollow'}).getText()


    markets.append(market)

pricing = pd.DataFrame(markets)
pricing

e vem o mesmo erro

AttributeError                            Traceback (most recent call last)
<ipython-input-34-d14b83920384> in <module>
      2     market = {}
      3 
----> 4     market['specie'] = item.find('a', {'class':'h6'}).getText()
      5     market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
      6     market['date'] = item.find('td', {'class':'text-right'}).getText()

AttributeError: 'NoneType' object has no attribute 'getText'

Enfim...alguma luz??

3 respostas

Olá Luiz,

O que está acontecendo é que o find não está retornando nada, e assim não é possível executar o getText() a partir de um retorno vazio.

Eu não entendo muito bem como funciona o find_next mas trocando essa parte por soup.find_all('td', class_="text-right")[1].get_text() consegui executar o seu primeiro código sem erros (o [1] vai selecionar apenas o segundo item de todos os encontrados).

Aplicando isso no for o city também apresentou o mesmo problema, mas coincidentemente a solução é igual:

# Dentro do for
market['specie'] = item.find('a', {'class':'h6'}).getText()
market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
market['date'] = item.find_all('td', {'class':'text-right'})[1].getText()
market['city'] = item.find_all('a')[1].getText()

Testa se isso resolve e qualquer coisa é só falar!

Eu tenho um erro anterior que as minhas listas estão vindo vazias e aí o código roda, mas não tráz nada... Veja só..

markets = []
listings = soup.find('div', {'class': 'bg-white rounded'})

Esse código em traz um div com um tabela dentro. O conteúdo tá todo dentro dessa tabela A minha variável listing [] traz tudo certinho para eu rodar o FOR

for item in listings:
    market = {}

    market['specie'] = item.find('a', {'class':'h6'}).getText()
    market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
    market['date'] = item.find_all('td', {'class':'text-right'})[1].getText()
    market['city'] = item.find_all('a')[1].getText()


    markets.append(market)

shrimp_pricing = pd.DataFrame(markets)
shrimp_pricing

Mas ainda assim me retorna um erro porque as variáveis que eu criei estão vazias


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-32-5d47b0c177c5> in <module>
      2     market = {}
      3 
----> 4     market['specie'] = item.find('a', {'class':'h6'}).getText()
      5     market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
      6     market['date'] = item.find_all('td', {'class':'text-right'})[1].getText()

TypeError: slice indices must be integers or None or have an __index__ method

E ainda não cosengui! :-/

solução!

No código listings = soup.find('div', {'class': 'bg-white rounded'}) como está sendo utilizado o find vai ser retornado apenas 1 item que é a div bg-white completa, mas para o for o necessário é passar uma lista de valores e não apenas 1 item.

Olhando na página como temos uma tabela e o que queremos selecionar é cada linha (cada tr), o melhor caminho é então selecionar todas as tags tr com o find_all()

# Aqui o listings vai ser uma lista onde cada item da lista é um tr da página
listings = soup.find_all('tr')

Exemplo completo:

from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd

url = 'https://tepbac.com/gia-thuy-san/cat/2-thuy-san/'

response = urlopen(url)
html = response.read()

soup = BeautifulSoup(html, 'html.parser')
markets = []
listings = soup.find_all('tr')
print(listings)

for item in listings:
    market = {}

    market['specie'] = item.find('a', {'class':'h6'}).getText()
    market['price'] = item.find('td', {'class':'text-right text-danger font-weight-bold'}).getText()
    market['date'] = item.find_all('td', {'class':'text-right'})[1].getText()
    market['city'] = item.find_all('a')[1].getText()


    markets.append(market)

shrimp_pricing = pd.DataFrame(markets)
shrimp_pricing