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

Scraping está repetindo a primeira página

Olá, estou fazendo um código para pegar informações para de um site de imóveis, porém fica repetindo as informações da primeira página no dataframe.

Segue o código abaixo:

#Importando as bibliotecas

from urllib.request import Request, urlopen 
from urllib.error import URLError, HTTPError
from bs4 import BeautifulSoup 
import requests
import pandas as pd 

#Input de informações

estado = str(input('Insira o Estado do Imovel: '))
cidade = str(input('Insira a cidade do Imovel: '))
pages = int(input('Insira o número de páginas que deseja pegar: '))

print('O Estado e a cidade escolhida foi:'+estado+' e '+cidade)

#Criando lista e dicionario

cards = []

#Iterando paginas do site, só vai até o 5, verificar a forma de capturar no HTML o numero max de paginas existente

for i in range(pages):

 #Endereço do site

   url = 'https://www.vivareal.com.br/venda/'+estado+'/'+cidade+'/casa_residencial/?pagina='+str(i+1)
    #Criando uma mascara para enganar o site, ele da um erro pois percebe que nao esta sendo acessado por um navegador, por esse motivo   tem que fazer isso.

   headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36'
    }
    #Informações para fingir ser um navegador
    try:
        req = Request(url, headers = headers)
        response = urlopen(req)
        html = response.read()


    except HTTPError as e:
        print(e.status, e.reason)

    except URLError as e:
        print(e.reason)

    #Aqui ele faz um tratamento para deixar o html mais amigável
    def trata_html(input):
        return " ".join(html.split()).replace('> <', '><')

    html = html.decode('utf-8')
    " ".join(html.split()).replace('> <', '><')


    #Criando objeto para guardar a função trata_html
    html = trata_html(html)

    #Criando um objeto para guardar a função do BS e não precisar ficar repetindo a função
    soup = BeautifulSoup(html, 'html.parser')

    #Obtendo as TAGs de interesse
    anuncios =soup.find('div', {'class':'results__content'}).findAll('article', {'class': 'property-card__container'})

    for anuncio in anuncios:
        card ={}

        #Capturando informação de valores
        card['value'] = anuncio.find('div', {'class': 'property-card__price'}).getText().split()[1]

        ##Capturando informação de endereço
        card['address'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[0]

        #Capturando informação do bairro
        card['district'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[1]

        #Capturando informação de metragem
        card['floorsize'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[0]

        #Capturando informação de numero de quartos
        card['numberOfRooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[2]

        #Capturando informação de numero de banheiros
        card['numberOfBathrooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[4]

#Empilhando os cards
cards.append(card)


#Criando dataframe para faccilitar a visualização
dataset = pd.DataFrame(cards)

#Verificando o dataframe
dataset

Como faço para não repetir a primeira página?

2 respostas

Olá Controladoria, tudo bem? Espero que sim!

O código está somente com problema de indentação.

Dentro do for i in range(pages), a url e headers estão faltando um espaço de indentação e a parte principal é de Empilhando os cards que está fazendo o append fora do loop for. Dessa forma o loop vai encerrar completamente sobrescrevendo a variável card e somente o último card será armazenado com o append, visto que o append só acontece após toda a execução do loop.

Para corrigir o problema, basta inserir a linha de código cards.append(card) dentro do loop for anuncio in anuncios.

Segue abaixo o código corrigido:

#Importando as bibliotecas

from urllib.request import Request, urlopen 
from urllib.error import URLError, HTTPError
from bs4 import BeautifulSoup 
import requests
import pandas as pd 

#Input de informações

estado = str(input('Insira o Estado do Imovel: '))
cidade = str(input('Insira a cidade do Imovel: '))
pages = int(input('Insira o número de páginas que deseja pegar: '))

print('O Estado e a cidade escolhida foi:'+estado+' e '+cidade)

#Criando lista e dicionario

cards = []

#Iterando paginas do site, só vai até o 5, verificar a forma de capturar no HTML o numero max de paginas existente

for i in range(pages):

 #Endereço do site

    url = 'https://www.vivareal.com.br/venda/'+estado+'/'+cidade+'/casa_residencial/?pagina='+str(i+1)
    #Criando uma mascara para enganar o site, ele da um erro pois percebe que nao esta sendo acessado por um navegador, por esse motivo   tem que fazer isso.

    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36'
    }
    #Informações para fingir ser um navegador
    try:
        req = Request(url, headers = headers)
        response = urlopen(req)
        html = response.read()


    except HTTPError as e:
        print(e.status, e.reason)

    except URLError as e:
        print(e.reason)

    #Aqui ele faz um tratamento para deixar o html mais amigável
    def trata_html(input):
        return " ".join(html.split()).replace('> <', '><')

    html = html.decode('utf-8')
    " ".join(html.split()).replace('> <', '><')


    #Criando objeto para guardar a função trata_html
    html = trata_html(html)

    #Criando um objeto para guardar a função do BS e não precisar ficar repetindo a função
    soup = BeautifulSoup(html, 'html.parser')

    #Obtendo as TAGs de interesse
    anuncios =soup.find('div', {'class':'results__content'}).findAll('article', {'class': 'property-card__container'})

    for anuncio in anuncios:
        card ={}

        #Capturando informação de valores
        card['value'] = anuncio.find('div', {'class': 'property-card__price'}).getText().split()[1]

        ##Capturando informação de endereço
        card['address'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[0]

        #Capturando informação do bairro
        card['district'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[1]

        #Capturando informação de metragem
        card['floorsize'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[0]

        #Capturando informação de numero de quartos
        card['numberOfRooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[2]

        #Capturando informação de numero de banheiros
        card['numberOfBathrooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[4]

        #Empilhando os cards
        cards.append(card)


#Criando dataframe para faccilitar a visualização
dataset = pd.DataFrame(cards)

#Verificando o dataframe
dataset

Espero que tenha tirado sua dúvida.

Estou à disposição. Bons estudos!

solução!

Olá Controladoria,

Ao rodar o código que te passei, realmente percebi que a primeira página estava repetindo.

Isso ocorre devido a estrutura do site. Ao utilizar a URL com os números da página ele não atualiza o conteúdo, mas sim, permanece com os dados da primeira página e isso porque os dados são carregados de forma dinâmica, ou seja, é buscado em alguma API e a partir disso os elementos HTML são criados. Para remediar isso, você pode utilizar uma biblioteca mais potente em quesito de web scraping como a selenium, que será capaz de simular cliques, baixar dados, dentre outros.

Vamos utilizar um código usando o selenium. Ele possui alguns pré-requisitos para funcionar:

Utilizaremos o navegador Firefox, caso não o utilize, faça o download do mesmo neste link

Baixe o WebDriver responsável por auxiliar na automatização do procedimento, bem como abrir a página automaticamente e extrair o código fonte da página. Caso utilize o windows, baixe o arquivo geckodriver-v0.30.0-win64.zip , extraia os dados e coloque na mesma pasta que estiver salvo o código.

Instale a biblioteca Selenium através do seguinte comando:

pip install selenium

O código a ser utilizado é o seguinte:

from selenium import webdriver
from bs4 import BeautifulSoup 
import time
import pandas as pd

driver = webdriver.Firefox()

estado = "minas-gerais"
cidade = "uberlandia"
pages = 3
cards = []

url = 'https://www.vivareal.com.br/venda/'+estado+'/'+cidade+'/casa_residencial/?pagina=1'
driver.get(url)
driver.find_element_by_id("cookie-notifier-cta").click()


for i in range(pages):

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

    anuncios_de_casas =soup.find('div', {'class':'results__content'}).findAll('article', {'class': 'property-card__container'})

    for anuncio in anuncios_de_casas:
        card ={}

        #Capturando informação de valores
        card['value'] = anuncio.find('div', {'class': 'property-card__price'}).getText().split()[1]

        ##Capturando informação de endereço
        card['address'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[0]

        #Capturando informação do bairro
        card['district'] = anuncio.find('span', {'class': 'property-card__address'}).getText().split('-')[1]

        #Capturando informação de metragem
        card['floorsize'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[0]

        #Capturando informação de numero de quartos
        card['numberOfRooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[2]

        #Capturando informação de numero de banheiros
        card['numberOfBathrooms'] = anuncio.find('ul', {'class': 'property-card__details'}).getText().split()[4]

        cards.append(card)

    if i != pages - 1:
        element = driver.find_elements_by_class_name("js-change-page")[-1]
        time.sleep(3)
        element.click()
        time.sleep(5)

dataset = pd.DataFrame(cards)
dataset.to_csv("Resultado_Controladoria.csv", index=False)


driver.quit()

É basicamente o mesmo código com as adaptações do selenium. A página será aberta e o intuito é selecionar o botão de trocar para a próxima página no fim do site para que o html seja atualizado.

Primeiramente, instanciamos o webdriver que precisa estar na mesma pasta do script, depois passamos a url para o driver e clicamos no botão de aceitar os cookies para não atrapalhar no clique do botão da página. Posteriormente, utilizamos um loop for para rodar um código para a quantidade de páginas escolhida.

O código html de cada página será obtido através do driver.page_source e será passado um objeto do BeautifulSoup a cada clique que for feito para passar para a próxima página ao fim do código

element = driver.find_elements_by_class_name("js-change-page")[-1]
time.sleep(3)
element.click()
time.sleep(5)

O time.sleep() serve para aguardar uma quantidade de segundos antes de executar a próxima linha de código.

Caso queira entender mais sobre o Selenium, recomendo a documentação da ferramenta.

Espero que tenha ajudado.

Bons estudos!