如何用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只能接收单个类名,多个类名会被识别为一个完整的类名(而页面元素的类是三个独立的),导致找不到任何元素,自然不会执行点击打开新标签页的逻辑。
修复步骤:
修正链接定位方式
把链接定位改为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:循环,直接处理单个链接即可。调整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)初始化未定义变量
代码中quantidade_2024未初始化,运行到quantidade_2024 += 1会报错,需要在代码开头添加:quantidade_2024 = 0优化元素定位的稳定性
避免在循环中重复定位整个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é
相关产品推荐
相关产品推荐

