
本文详解如何通过精准定位和等待策略,解决 selenium 在动态加载页面中点击无效、内容不更新的问题,以成功抓取 noveatknox 网站各楼层的可用公寓详情。
本文详解如何通过精准定位和等待策略,解决 selenium 在动态加载页面中点击无效、内容不更新的问题,以成功抓取 noveatknox 网站各楼层的可用公寓详情。
在动态渲染的房产网站(如 https://www.php.cn/link/c7fddc94d0081819e0e6153ff2f9fb09)中,楼层切换并非传统页面跳转,而是通过 JavaScript 触发局部 DOM 更新。若仅用 presence_of_element_located + .click(),常因事件未绑定、目标元素未真正可交互或后续内容异步加载延迟,导致 #unit-list-items 始终为空或显示“no available apartments”。
关键在于:必须确保点击的是具备完整交互能力的、已渲染且可见的元素,并等待其关联内容真实就绪。原代码中直接定位 并点击,但该链接实际依赖父级容器(.swiper-slide)及内部 data-js-hook="mobile-floor-selector" 的 JS 逻辑;更可靠的方式是定位其内部带数据属性的 元素——它既是视觉焦点,又明确携带 data-floor 和可用数量,且 visibility_of_all_elements_located 能确保元素不仅存在,而且处于可视状态(即已渲染完成、无 CSS 隐藏)。
以下是优化后的完整流程:
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
url = 'https://www.php.cn/link/c7fddc94d0081819e0e6153ff2f9fb09'
driver = webdriver.Chrome()
driver.maximize_window()
driver.get(url)
wait = WebDriverWait(driver, 10)
# 定位所有楼层标签(含 data-js-hook 的 span),它们代表可点击的楼层入口
floor_spans = wait.until(
EC.visibility_of_all_elements_located((By.CSS_SELECTOR, "#mobile-floor-carousel-list > a span[data-js-hook='available-unit-count']"))
)
for floor_span in floor_spans:
unit_count = floor_span.text.strip()
if unit_count != "0":
# 获取对应楼层号(从父级 <a> 标签提取 data-floor)
floor_link = floor_span.find_element(By.XPATH, "./ancestor::a")
floor_num = floor_link.get_attribute("data-floor")
print(f"Floor: {floor_num}, Available units: {unit_count}")
# 执行点击 —— 此时 floor_link 已确认可见且可交互
floor_link.click()
# 等待单位列表完全加载并可见(非仅存在!)
unit_items = wait.until(
EC.visibility_of_all_elements_located((By.CSS_SELECTOR, "#unit-list-items > a.skylease__unit-list-item"))
)
for unit in unit_items:
# 提取结构化信息(示例:单元号、户型、面积、价格)
try:
unit_id = unit.find_element(By.CSS_SELECTOR, ".skylease__unit-list-item-details--unit").text.strip()
bedrooms_baths = [el.text.strip() for el in unit.find_elements(By.CSS_SELECTOR, ".skylease__unit-list-item-details:not(.skylease__unit-list-item-details--unit):not(.skylease__unit-list-item-details--price)")]
sqft = next((t for t in bedrooms_baths if "sq. ft." in t), "")
price_spans = unit.find_elements(By.CSS_SELECTOR, ".skylease__unit-list-item-details--price span")
prices = [span.text.strip() for span in price_spans] if price_spans else []
print(f" Unit: {unit_id}")
print(f" Details: {', '.join(bedrooms_baths)}")
print(f" Area: {sqft}")
print(f" Prices: {prices}")
except Exception as e:
print(f" [Warning] Failed to parse unit: {e}")
print("-" * 40)</a>
核心要点总结:
✅ 优先使用 visibility_of_* 而非 presence_of_*:前者确保元素渲染完成、尺寸非零、无 display:none 或 visibility:hidden,避免点击“幽灵元素”。
✅ 点击目标应为语义清晰、稳定存在的节点:此处选择 标签(含 data-floor),而非 XPath 硬编码索引(如 a[11]),提升可维护性与鲁棒性。
✅ 等待内容加载需匹配真实 DOM 状态:#unit-list-items > a.skylease__unit-list-item 比泛化的 #unit-list-items 更精确,防止误判空容器。
⚠️ 注意反爬与稳定性:该站点无强反爬,但建议添加 time.sleep(0.5) 或显式 WebDriverWait 防止高频操作被限;生产环境应加入异常重试与日志记录。
通过以上策略,即可稳定抓取所有可用楼层及其公寓详情,为后续数据清洗与分析奠定可靠基础。











