使用Selenium和BeautifulSoup爬取Airdna地图标记悬停窗口数据
问题原因
- 选择器错误:你使用的
Page__RightColumn-sc-291lxm-3是页面右侧内容栏的容器类,根本不是地图标记的类,自然获取不到任何元素。 - 动态类失效:这类站点使用CSS Module生成带随机哈希后缀的类名(比如带
sc-xxx的格式),类名会随版本更新变化,硬写死的类名大概率失效。 - 地图元素懒加载:地图标记是动态渲染的,只有出现在当前地图视口范围内的标记才会被插入DOM,视口外的标记不会提前生成。
解决方法
方案1:直接抓取接口(更稳定)
Airdna的地图标记数据都是通过接口提前加载的,你可以打开浏览器控制台的「网络」标签,筛选Fetch/XHR请求,找到返回房产列表的接口,直接请求接口拿数据,不需要模拟点击操作,稳定性高很多,也不会遇到DOM元素找不到的问题。
方案2:修改Selenium代码适配DOM结构
代码调整点:
- 替换正确的标记选择器:优先用不带动态哈希的通用类,比如Leaflet地图库的标记通用类
leaflet-marker-icon,或者通过属性筛选包含房产标识的元素。 - 增加显式等待:不要依赖手动输入暂停,用
WebDriverWait等待元素渲染完成再操作。 - 避免元素过期错误:点击标记后DOM会更新,原来的标记元素引用会失效,不要直接遍历首次获取的标记列表,每次操作后重新获取标记列表。
- 修正数据存储格式:原代码中用
{}存储两个值会生成集合而非字典,需要指定key。
修改后的参考代码:
from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains import time driver = webdriver.Chrome() driver.get("https://www.airdna.co/vacation-rental-data/app/us/california/santa-monica/overview") driver.maximize_window() # 等待引导关闭,手动操作完后程序继续 input("请手动关闭页面引导、完成验证后按回车继续...") target_list = [] wait = WebDriverWait(driver, 10) try: # 等待地图标记加载完成 wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "img.leaflet-marker-icon"))) # 第一次获取所有标记 mapMarkers = driver.find_elements(By.CSS_SELECTOR, "img.leaflet-marker-icon") for idx in range(len(mapMarkers)): # 每次重新获取标记避免元素过期 current_marker = driver.find_elements(By.CSS_SELECTOR, "img.leaflet-marker-icon")[idx] # 滚动到标记可见 driver.execute_script("arguments[0].scrollIntoViewIfNeeded();", current_marker) time.sleep(1) # 悬停触发弹窗(部分标记是悬停触发而非点击) ActionChains(driver).move_to_element(current_marker).pause(2).perform() # 等待弹窗加载 wait.until(EC.presence_of_element_located((By.CLASS_NAME, "info-window__statistics-value"))) html = driver.page_source soup = BeautifulSoup(html, "lxml") days = soup.find_all("p", class_="info-window__statistics-value") link = soup.find_all("a", class_="info-window__property-link") if days and link: target_list.append({ "days_available": days[0].text.strip().replace('\n', '').replace(' ', ''), "property_link": link[0].attrs['href'] }) # 点击空白处关闭弹窗 ActionChains(driver).move_by_offset(100, 100).click().perform() time.sleep(1) except Exception as e: print(f"运行出错:{e}") finally: driver.quit() # 输出结果 for item in target_list: print(item)
额外注意事项:
- 该站点有反爬机制,频繁操作会触发验证码,建议操作之间增加随机延迟,必要时使用代理IP。
- 地图仅渲染当前视口内的标记,如果需要抓取全量数据,需要模拟拖动地图加载更多区域的标记。
内容的提问来源于stack exchange,提问作者Neil
相关产品推荐
相关产品推荐

