You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Selenium切换新标签页并返回原标签页?代码异常求助

问题描述

我需要爬取VivaReal网站的房源信息,流程是:点击搜索结果列表中的房源,在新标签页打开后爬取发布年份,关闭新标签页返回原页面,循环执行此操作。但当前代码无报错,却没有打开新标签页,也无法完成预期的爬取流程。

问题代码
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json
import logging
import requests
import pandas as pd
from bs4 import BeautifulSoup
from urllib.parse import urlparse
import re
from fpdf import FPDF
from selenium import webdriver
from urllib.parse import urlparse
import undetected_chromedriver as uc
from selenium import webdriver

precos = []
tamanhos = []
options = webdriver.ChromeOptions() 
options.add_argument("start-maximized")
driver = uc.Chrome(options=options)
    #options.add_argument(f"--proxy-server={proxy}")    
    #options.add_argument("start-maximized")
    #options.add_argument("disable-infobars")options.add_argument("--disable-extensions")
actions = ActionChains(driver)
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox")
options.add_argument("--verbose")
        #options.add_argument("--remote-debugging-port=9222") 
options.add_argument('--headless')
options.add_argument("--disable-gpu")    
data_planilha = []
precos_m2 = []

url_base = "https://www.vivareal.com.br/venda/ceara/caucaia/bairros/caucaia/#onde=,Cear%C3%A1,Caucaia,Bairros,Caucaia,,,,BR%3ECeara%3ENULL%3ECaucaia%3EBarrios%3ECaucaia,,,/"
driver.get(url_base)
#driver.find_element(By.XPATH, "//ul[@class = 'header__nav-links']/li[2]/a").click()
#driver.implicitly_wait(30)
resultados = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//section[@class = 'results__main']" )))
quantidade_imoveis = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, "//strong[@class = 'results-summary__count js-total-records']"))).text
quantidade_imoveis = int(quantidade_imoveis)
print(quantidade_imoveis)
try:
    botao_proximo = WebDriverWait(driver, 20).until(
    EC.element_to_be_clickable((By.XPATH, "//button[@title = 'Próxima página']")))

    while True:
        
        time.sleep(1)    
        imoveis = WebDriverWait(driver, 200).until(EC.visibility_of_all_elements_located((By.XPATH, "//div[@class = 'results-list js-results-list']/div")))
        print("entrou")
        for index, value in enumerate(imoveis):
            driver.implicitly_wait(20)
            imoveis = WebDriverWait(driver, 200).until(EC.visibility_of_all_elements_located((By.XPATH, "//div[@class = 'results-list js-results-list']/div")))

            driver.execute_script("arguments[0].scrollIntoView(true);", imoveis[index])
            time.sleep(3)


            tamanho = imoveis[index].find_element(By.XPATH, ".//span[@class = 'property-card__detail-value js-property-card-value property-card__detail-area js-property-card-detail-area']").text
            #driver.execute_script("arguments[0].scrollIntoView(true);", tamanho)
           # tamanho = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, ".//span[@class = 'property-card__detail-value js-property-card-value property-card__detail-area js-property-card-detail-area']"))).text
            print(tamanho)
            driver.implicitly_wait(20)
            preco = imoveis[index].find_element(By.XPATH, ".//div[@class = 'property-card__price js-property-card-prices js-property-card__price-small']/p").text
            preco_imovel = "".join(re.findall('\\d+', preco))
            price = int(preco_imovel)
            links = imoveis[index].find_elements(By.CLASS_NAME, "property-card__labels-container js-main-info js-listing-labels-link")
            for link in links:
                
                link.click()
    
                driver.switch_to.window(driver.window_handles[1])
                driver.execute_script('window.scrollBy(0, 500)')

                anuncio_ano = WebDriverWait(driver, 100).until(EC.visibility_of_element_located((By.XPATH, "//div[@class = 'description__info-date big-space']/span[2]"))).text
                driver.implicitly_wait(20)
                driver.close()

                driver.switch_to.window(driver.window_handles[0]) 

                
            #preco = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, ".//div[@class = 'property-card__price js-property-card-prices js-property-card__price-small']/p"))).text
            size = int(tamanho)
            print(preco_imovel)
            precos.append(int(price))
            tamanhos.append(size)
            preco_m2 = price/size
            print(preco_m2)
            precos_m2.append(preco_m2)
           
            
            print(anuncio_ano)
            if("2024" in anuncio_ano):
                quantidade_2024 += 1

            
          botao_proximo = driver.find_element(By.XPATH, "//button[@title = 'Próxima página']")
        valor = botao_proximo.get_attribute("data-page")
        
        
        if valor != "":
            driver.execute_script('arguments[0].click()', botao_proximo)
        else:
            break

except NoSuchElementException:
    print("última página")
tamanho_medio = sum(tamanhos)/quantidade_imoveis
preco_medio_total = sum(precos)/quantidade_imoveis
precos_m2_total = sum(precos_m2)/quantidade_imoveis
data_planilha.append({
        'URL': url_base,
        'Número de anúncios': quantidade_imoveis,
        'Preço Médio': preco_medio_total,
        'Tamanho Médio': tamanho_medio,
        'Preço Médio por m2': precos_m2_total,
        
        })
df_planilha = pd.DataFrame(data_planilha)
df_planilha.to_excel("catalogo_vivareal.xlsx")
print("salvou")
driver.quit()
问题分析与修复方案

核心问题:链接定位失败

代码中用find_elements(By.CLASS_NAME, "property-card__labels-container js-main-info js-listing-labels-link")定位房源链接,这是错误的:

  • By.CLASS_NAME只能接收单个类名,多个类名会被识别为一个完整的类名(而页面元素的类是三个独立的),导致找不到任何元素,自然不会执行点击打开新标签页的逻辑。

修复步骤:

  1. 修正链接定位方式
    把链接定位改为CSS选择器(支持多类名),且每个房源只有一个链接,用find_element而非find_elements:

    link = imoveis[index].find_element(By.CSS_SELECTOR, ".property-card__labels-container.js-main-info.js-listing-labels-link")
    link.click()
    

    去掉原有的for link in links:循环,直接处理单个链接即可。

  2. 调整ChromeOptions的设置顺序
    原代码中先创建了driver,再添加--headless等参数,这些参数不会生效。需要先完成所有options配置,再初始化driver:

    options = webdriver.ChromeOptions() 
    options.add_argument("start-maximized")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--no-sandbox")
    options.add_argument("--verbose")
    options.add_argument('--headless=new')  # 推荐使用新版无头模式,兼容性更好
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1920,1080")  # 无头模式下设置窗口尺寸,避免元素定位失败
    driver = uc.Chrome(options=options)
    
  3. 初始化未定义变量
    代码中quantidade_2024未初始化,运行到quantidade_2024 += 1会报错,需要在代码开头添加:

    quantidade_2024 = 0
    
  4. 优化元素定位的稳定性
    避免在循环中重复定位整个imoveis列表,改用WebDriverWait等待单个元素的可点击状态,减少StaleElementReferenceException的概率:

    # 替换原有的imoveis重复定位逻辑
    current_imovel = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.XPATH, f"//div[@class='results-list js-results-list']/div[{index+1}]"))
    )
    driver.execute_script("arguments[0].scrollIntoView(true);", current_imovel)
    

修复后的关键代码片段

quantidade_2024 = 0
# ... 其他初始化代码
options = webdriver.ChromeOptions() 
options.add_argument("start-maximized")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--no-sandbox")
options.add_argument("--verbose")
options.add_argument('--headless=new')
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
driver = uc.Chrome(options=options)
actions = ActionChains(driver)
# ... 页面访问代码
for index, value in enumerate(imoveis):
    current_imovel = WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located((By.XPATH, f"//div[@class='results-list js-results-list']/div[{index+1}]"))
    )
    driver.execute_script("arguments[0].scrollIntoView(true);", current_imovel)
    time.sleep(2)  # 缩短不必要的等待时间

    tamanho = current_imovel.find_element(By.XPATH, ".//span[@class='property-card__detail-value js-property-card-value property-card__detail-area js-property-card-detail-area']").text
    preco = current_imovel.find_element(By.XPATH, ".//div[@class='property-card__price js-property-card-prices js-property-card__price-small']/p").text
    preco_imovel = "".join(re.findall('\\d+', preco))
    price = int(preco_imovel)

    # 修正后的链接定位与新标签页处理
    link = WebDriverWait(current_imovel, 20).until(
        EC.element_to_be_clickable((By.CSS_SELECTOR, ".property-card__labels-container.js-main-info.js-listing-labels-link"))
    )
    link.click()

    # 等待新标签页加载完成
    WebDriverWait(driver, 20).until(lambda d: len(d.window_handles) == 2)
    driver.switch_to.window(driver.window_handles[1])
    
    driver.execute_script('window.scrollBy(0, 500)')
    anuncio_ano = WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[@class='description__info-date big-space']/span[2]"))).text
    
    driver.close()
    driver.switch_to.window(driver.window_handles[0])

    # 后续数据处理逻辑
    size = int(tamanho)
    precos.append(price)
    tamanhos.append(size)
    preco_m2 = price/size
    precos_m2.append(preco_m2)
    
    print(anuncio_ano)
    if("2024" in anuncio_ano):
        quantidade_2024 += 1

内容的提问来源于stack exchange,提问作者André

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 13:12:33