简而言之:Idealista 是西班牙、意大利和葡萄牙最大的房产交易平台,但其背后部署了一套强大的反机器人防护体系,能迅速拦截粗糙的爬虫程序。本指南将带您逐步了解如何使用 Python 从头到尾爬取 Idealista 的数据,内容涵盖网站结构分析、结合 undetected-chromedriver 的 Selenium 操作、DataDome 处理、代理轮换以及数据清理导出,并包含竞争对手通常忽略的生产环境强化措施。
简介
如果您曾尝试从 Idealista 抓取数据,想必已经深有体会:操作流程既简短又残酷——发送几个干净的请求,被封禁;更换用户代理,再次被封禁;遇到验证码,然后一切重来。 Idealista是横跨西班牙、葡萄牙和意大利的主导性房地产门户网站,拥有数百万条待售和租赁房源信息,这使其成为市场分析师、买方代理和房地产科技团队的宝藏。它也是该领域防御最为严密的网站之一。
本指南专为寻求经过实战检验的完整解决方案(而非零散代码片段)的中级 Python 开发者而写。您将首先了解法律与技术背景,随后通过 undetected-chromedriver 逐步演示具体的 Selenium 工作流,并逐步加入代理、验证码处理、去重及增量跟踪等环节,涵盖大多数教程忽略的实际生产环境细节。
为何要抓取 Idealista,以及哪些数据真正值得提取
Idealista 是南欧最大的房地产交易平台之一,其西班牙、意大利和葡萄牙站点上拥有数百万条待售和待租房源信息。对于投资者研究、可比价格模型以及中介机构的潜在客户信息丰富化而言,仅凭公开 API 很难复制这种覆盖范围,这也是为何众多团队首先询问如何从 Idealista 抓取数据的原因。
规划抓取方案时,请预先确定您真正需要的字段。房源卡片和详情页通常包含:
- 标题和房源 URL(规范
/inmueble/<id>/链接) - 价格和货币,以及详情页中的每平方米单价
- 建筑面积(平方米)、房间数和卫生间数量
- 描述文本、房源发布日期和更新日期
- 中介或业主类型(私人 vs. 中介),附带电话和联系按钮
- 照片、户型图,有时还包含虚拟看房链接
- 大致坐标及社区元数据
意大利(idealista.it)和葡萄牙(idealista.pt)网站通常采用与西班牙站相似的布局,因此只需对选择器进行微调,单一的爬虫设计通常就能适配这三个站点。我们将在故障排除部分重新探讨这些差异。
抓取Idealista数据是否合法?合规基本原则
公开的房源数据通常在以合理频率采集时被视为合法,但一旦涉及个人数据,Idealista的服务条款及欧盟《通用数据保护条例》(GDPR)便会改变这一局面。根据GDPR规定,经纪人姓名、电话号码和电子邮箱地址均属于个人数据,因此大规模存储此类数据通常需要具备有据可查的合法依据、保留期限限制以及数据删除流程。
实用防护措施:遵守 robots.txt、严格限制抓取速率、避开登录区域,并在重新分发任何数据集时移除个人联系信息。请将本节内容视为指导而非法律建议,在投入生产前务必就任何敏感事项咨询您的法律顾问。此处的法律立场确实存在争议,因此在部署大规模抓取工具前,请针对您的具体用例核实当前的GDPR适用范围。
Idealista 如何检测机器人:DataDome、指纹识别和速率限制
大多数公开报道将 Idealista 的插页式验证码归因于 DataDome——一款商业反机器人服务。请将此归因视为社区共识而非官方声明,因为 Idealista 并未公开其检测技术栈。
检测机制采用分层架构,对各层机制的理解将决定爬虫是能存活一周,还是在一小时内被封禁:
- TLS 和 JA3 指纹识别。明文
requests及许多 HTTP 库生成的 TLS 握手签名,与真实的 Chrome 会话相比极易被识别。即使 User-Agent 完美伪装,加密套件的顺序也会暴露你的身份。 - 标头一致性。机器人检测引擎会检查
Accept,Accept-Language,sec-ch-ua,以及User-Agent是否相互一致。若 Chrome 120 用户代理(UA)缺少sec-ch-ua-platform标头,便是破绽。 - JavaScript 验证。DataDome 会发送一个小型 JS 有效载荷,用于检测
navigator.webdriver、canvas 哈希值、WebGL 渲染器以及时间信号。无头 Chrome 和原生 Selenium 默认情况下会在这几项中失败。 - 行为与 IP 信号。访问速度、无鼠标导航、数据中心 ASN 范围以及重复使用的 Cookie 都会影响风险评分,从而触发验证码。
您的机器人需要真实的浏览器指纹、合理的请求头、家庭或移动IP地址,以及类人的操作节奏。仅靠单一解决方案是不够的。
选择爬取方案:requests/HTTPX 与 Selenium 及爬取 API 之比较
关于如何大规模从 Idealista 抓取数据,没有放之四海皆准的正确答案;正确的选择取决于数据量、预算,以及您能接受多少 JavaScript 渲染。以下是一个简易决策矩阵,您可以将其粘贴到规划文档中。
|
方法 |
最适合 |
速度 |
DataDome 抗干扰性 |
维护 |
|---|---|---|---|---|
|
|
小型一次性抓取 |
快速 |
低 |
在被阻塞前保持低 |
|
|
中等规模抓取,支持异步 |
极快 |
低到中等 |
中等 |
|
Selenium + undetected-chromedriver |
每页提取可靠 |
慢 |
中等 |
高(驱动程序漂移) |
|
托管式抓取 API |
生产级规模,无需人工干预 |
可变 |
高 |
低 |
纯 HTTP 架构在正常工作时成本最低,但一旦 Idealista 升级为 JS 挑战,它们便会立即崩溃。 使用 Selenium 配合 undetected-chromedriver 能提供真实的浏览器指纹和 DOM 执行能力,代价是速度显著变慢且内存消耗更大。托管 API 则完全隐藏了代理和挑战解决层,一旦你的需求超出单台机器的处理能力,这便是正确的选择。大多数生产团队最终会将这些方案结合使用:针对简单页面采用快速的 HTTP 路径,针对高安全性的页面使用浏览器回退方案,并将爬取 API 作为安全网。
配置 Python 项目与依赖项
你需要 Python 3.10 或更高版本、一个干净的 virtualenv 环境以及固定版本的依赖项。由于 Idealista 的前端变更频繁,锁定版本有助于日后更轻松地诊断回归问题。
python -m venv .venv
source .venv/bin/activate
pip install selenium==4.* undetected-chromedriver selenium-wire httpx parsel tenacity python-dotenv实用布局:
idealista-scraper/
├── .env # proxy credentials, API keys
├── config.py # constants, base URLs
├── scraper/
│ ├── driver.py # uc.Chrome factory + waits
│ ├── crawl.py # provinces → municipalities → listings
│ └── parse.py # XPath/CSS extractors
├── storage.py # JSON/CSV/SQLite writers
└── main.py将 Chrome 的主版本号与您构建时使用的 undetected-chromedriver 构建版本;版本不匹配是导致无声崩溃的最常见原因。
映射 Idealista 的 URL 结构:首页、省份、市、搜索、房产
在编写任何选择器之前,请手动浏览网站。一旦掌握了规律,Idealista 的 URL 结构其实相当规整,而清晰的思维导图能防止您重复爬取相同的节点。
其层次结构大致如下:
- 主页:
https://www.idealista.com/(以及.it,.pt意大利语和葡萄牙语版本的链接) - 省目录:从首页底部链接进入
- 市镇目录:位于各省之下,列出市镇的页面
- 列表页面:
/venta-viviendas/<municipality>/出售、/alquiler-viviendas/<municipality>/出租(IT/PT 版本的 URL 模式通常与此一致,但请先核对实际页面) - 分页:
pagina-2.htm,pagina-3.htm,附加在列表URL后 - 房源详情:
/inmueble/<id>/,即规范的唯一房源标识符 - 按发布时间排序:在URL后添加
?ordenado-por=fecha-publicacion-desc以优先展示新上架的房产
将该 inmueble/<id> slug;文章其余部分将它用作去重键和变更追踪的枢纽。当选择器发生变化时,一份可靠的 CSS 选择器参考资料在此处尤为宝贵。
如何使用 Selenium 和 undetected-chromedriver 从 Idealista 抓取数据
手头有了 URL 映射表,你就可以搭建实际的爬虫了。计划如下:通过 undetected-chromedriver,导航至首页,并使用显式等待确保 DOM 渲染完成后再进行查询。接下来的四个步骤将自上而下构建爬虫,从省份到市镇,再到房源卡片,最后处理分页。
步骤 1:从首页提取完整的省份列表
西班牙语主页的页脚附近有一个省目录。我们收集该区块内的每个锚点,存储其可见名称,并保留绝对 URL 以备后续步骤使用。
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def make_driver():
opts = uc.ChromeOptions()
opts.add_argument("--window-size=1366,900")
# use_subprocess=True is currently required to avoid a destructor
# bug in some undetected-chromedriver builds; verify against the
# upstream repo before pinning, since package internals change often.
return uc.Chrome(options=opts, use_subprocess=True)
def fetch_provinces(driver):
driver.get("https://www.idealista.com/")
WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.locations-list"))
)
anchors = driver.find_elements(
By.XPATH, "//div[contains(@class,'locations-list')]//a"
)
return {a.text.strip(): a.get_attribute("href") for a in anchors if a.text.strip()}首次调用几乎肯定会触发 DataDome 验证码,因此首次运行时需将其包裹在手动解决钩子中(后文详述)。一旦会话获得有效的 Cookie,后续爬取通常就能顺利进行。
步骤 2:爬取各省的所属市镇
每个省页面通过 location_list 块展示其下辖市。我们将步骤 1 中的字典进行遍历,并将每个下辖市的名称和 URL 添加到其所属省份下方。
def fetch_municipalities(driver, provinces):
out = {}
for name, url in provinces.items():
driver.get(url)
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.ID, "location_list"))
)
anchors = driver.find_elements(By.XPATH, "//ul[@id='location_list']//a")
out[name] = {
"url": url,
"municipalities": [
{"name": a.text.strip(), "url": a.get_attribute("href")}
for a in anchors if a.text.strip()
],
}
time.sleep(random.uniform(2.5, 6.0)) # polite pacing
return out有两点值得特别注意:随机延迟比固定延迟更能模拟人类浏览行为,且应在处理完每个省份后将这个嵌套字典检查点保存到磁盘,这样即使在第 30 个省份时发生崩溃,也不会导致前 29 个省份的数据丢失。
步骤 3:解析房产卡片(标题、价格、面积、描述、URL)
列表页面为每处房产渲染一个 article.item 每个房产对应一个节点,并为标准字段提供结构化的子节点。下方的 XPath 模式使用 contains(@class, ...) 而非严格相等,因为 Idealista 偶尔会在房源卡片后附加修饰类。
def parse_listing_page(driver):
cards = driver.find_elements(By.XPATH, "//article[contains(@class,'item')]")
rows = []
for c in cards:
try:
link = c.find_element(By.XPATH, ".//a[contains(@class,'item-link')]")
url = link.get_attribute("href")
title = link.text.strip()
price = c.find_element(
By.XPATH, ".//span[contains(@class,'item-price')]"
).text.strip()
details = [
d.text.strip() for d in c.find_elements(
By.XPATH, ".//span[contains(@class,'item-detail-char')]/span"
)
]
description = c.find_element(
By.XPATH, ".//div[contains(@class,'item-description')]"
).text.strip()
rows.append({
"id": url.rstrip("/").split("/")[-1],
"url": url, "title": title, "price": price,
"details": details, "description": description,
})
except Exception:
# Sponsored or malformed cards: skip rather than abort.
continue
return rows两点生产环境注意事项。首先,Idealista 会穿插展示与自然搜索结果卡片外观几乎相同的广告卡片; try/except 包裹在每张卡片外,可防止单个错误节点导致页面崩溃。其次,选择器会随时间漂移;预计每隔几个月需更新这些 XPath。建议将其保存在单个 parse.py 模块中,以便于比较差异。
步骤 4:通过 'Siguiente' 链接追踪分页
Idealista采用数字分页方案,其中 Siguiente ("下一页")链接,该链接被 li.next。有些教程会递归处理下一个 URL 直到其消失,但无界递归会导致触发了速率限制,并在密集的城市区域触及 Python 的递归上限。因此,应为循环设置上限。
MAX_PAGES = 40
def crawl_municipality(driver, start_url, max_pages=MAX_PAGES):
rows, url, page = [], start_url, 0
while url and page < max_pages:
driver.get(url)
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.XPATH, "//main"))
)
rows.extend(parse_listing_page(driver))
try:
next_link = driver.find_element(
By.XPATH, "//li[contains(@class,'next')]/a"
)
url = next_link.get_attribute("href")
except Exception:
url = None
page += 1
time.sleep(random.uniform(3.0, 7.0))
return rows40 页的上限大致与 Idealista 自身的深度分页截止点相符,并能确保每个工作线程保持在限制范围内。若确实需要更多数据,请按价格区间或房产类型拆分搜索,并并行运行这些子查询,而非在单一结果集上进行深度挖掘。
在保持会话状态的同时解决 DataDome 验证码
任何学习如何从 Idealista 抓取数据的人最终都会遇到 DataDome 插页。好消息是(已在多个爬虫社区中得到轶事性证实),一旦你在当前会话中解决了该挑战,它通常在该浏览会话的剩余时间内不会再次出现。你可以借助以下几种具体策略来利用这一点。
最简单的方法是在开发环境中添加手动解决钩子:
def open_with_captcha_pause(driver, url):
driver.get(url)
if "captcha" in driver.page_source.lower() or "datadome" in driver.page_source.lower():
input("Solve the captcha in the browser, then press Enter to continue...")若需处理非一次性任务,请升级方案:
- 持久化配置文件。传递
--user-data-dir=./chrome-profilecookie 并让 DataDome_ddCookie 能跨运行保留。 - 挑战后切换 IP。家庭 IP 上的验证码可恢复;若同一 IP 上反复出现验证码,通常意味着该 IP 已被封禁。切换至新的出口节点并重试。
- 放慢节奏。在通过验证后重试前,插入更长的随机延迟(30 至 90 秒),而非疯狂轰炸同一 URL。
- 升级至托管解码器。当吞吐量比控制更重要时,将请求路由至能自行处理指纹识别和验证挑战的爬虫API。
将验证码视为指纹、IP 或速率已超出阈值的信号,而非仅需关闭的单次弹窗。
避免被封锁的扩展策略:代理、头部信息与速率控制
在生产级规模下从 Idealista 抓取数据的难点不在于解析 HTML,而在于如何规避封禁。一旦原型运行成功,扩展过程就会暴露新的故障模式。最初 200 次请求中幸存的数据中心 IP 会遭遇批量验证,所有工作线程使用完全相同的请求头会引起注意,而高度同步的请求则会被视为协同攻击的机器人。
请按以下顺序构建防御层,仅在前一层失效时才增加复杂度。
1. 基于地理定位的住宅或移动代理。Idealista 的流量主要来自西班牙、意大利和葡萄牙的消费者。来自这些国家的住宅 IP 能很好地融入环境;而美国数据中心的 ASN 则无法做到这一点。配合 selenium-wire:
from seleniumwire.undetected_chromedriver import Chrome
seleniumwire_options = {
"proxy": {
"http": "http://user:pass@es.proxy.example:8000",
"https": "http://user:pass@es.proxy.example:8000",
"no_proxy": "localhost,127.0.0.1",
}
}
driver = Chrome(seleniumwire_options=seleniumwire_options, use_subprocess=True)2. 标头轮换。循环使用一小批真实的Chrome 12x标头(匹配 User-Agent, sec-ch-ua,以及 Accept-Language)。保持一致性;不匹配的客户端提示会被标记的速度比任何单个错误标头都快。
3. 并发限制。将每个 IP 的工作线程限制为 1 个,每个区域限制为 4 到 8 个,每次爬取的数量则根据您的代理套餐支持的上限而定。在页面抓取之间添加 3 到 10 秒的随机延迟,以避免流量呈现脉冲状。
4. 将托管式抓取 API 作为安全网。一旦每日抓取量超过数千页,维护自有指纹识别栈的成本将超过其带来的收益。托管式 Scraper API 通过单一接口处理 IP 轮换、验证挑战及重试逻辑,这使您能够保留解析代码,仅需替换抓取层。将其视为备用方案而非默认方案。
保存、去重并导出抓取的房产数据集
将行数据打印到标准输出(stdout)仅适用于演示,在生产环境中毫无用处。应采用流式写入磁盘的方式,这样即使在第八小时发生崩溃,也不会导致前七小时的数据丢失。
import json, sqlite3
def upsert_sqlite(db_path, rows):
conn = sqlite3.connect(db_path)
conn.execute(
"CREATE TABLE IF NOT EXISTS listings ("
"id TEXT PRIMARY KEY, url TEXT, title TEXT, price TEXT, "
"details TEXT, description TEXT, scraped_at TEXT)"
)
conn.executemany(
"INSERT OR REPLACE INTO listings VALUES (?,?,?,?,?,?,datetime('now'))",
[(r["id"], r["url"], r["title"], r["price"],
json.dumps(r["details"]), r["description"]) for r in rows],
)
conn.commit(); conn.close()使用 inmueble/<id> slug 作为主键;标题和价格会变,ID 不会变。对于更轻量级的管道,仅追加的 JSONL 加上基于相同键的每日去重处理即可。
按计划跟踪 Idealista 上新挂牌的房产
大多数询问如何定期从 Idealista 抓取数据的团队,更关注过去 24 小时内的新增内容,而非一次性批量导出。买方经纪人、投资基金和潜在客户生成工具都希望获取最新的增量数据,而非完整的存档。Idealista 提供了按时间倒序排序的功能 ?ordenado-por=fecha-publicacion-desc,这正是构建轻量级变更追踪工作流的起点。
实现模式很简单:针对每个关注的市镇,使用按时间排序后的 URL 运行爬虫,将生成的 ID 与上次运行结果进行比对,并仅输出新增的行。
def diff_new_listings(rows, seen_ids_path):
seen = set(pathlib.Path(seen_ids_path).read_text().splitlines()) if pathlib.Path(seen_ids_path).exists() else set()
new_rows = [r for r in rows if r["id"] not in seen]
pathlib.Path(seen_ids_path).write_text(
"\n".join(seen | {r["id"] for r in new_rows})
)
return new_rows通过 cron 或 Airflow 每小时定时执行该任务,通过 Slack 或电子邮件对新增记录进行提醒,您便拥有了一个无需自定义后端即可运行的房地产监测系统。
故障排除:结果为空、选择器过时及 ChromeDriver 崩溃
读者报告的大多数故障可归因于几种常见模式。
use_subprocess怪癖。最近的undetected-chromedriver版本use_subprocess=True,以避免可能导致驱动程序损坏的析构函数警告。请对照上游仓库进行验证,因为不同版本的解决方法各不相同。NoSuchElementException关于卡片。通常是 Idealista 交换了类名。请重新检查 DOM,并优先使用contains(@class, ...)严格相等。/en/布局差异。英语语言环境提供的 DOM 略有不同。请锁定为实际所需的语言环境,若涉及跨站点场景,请按语言环境分别维护选择器。- Chrome 与驱动程序不匹配。请固定两者的版本。
SessionNotCreatedException几乎总是意味着修补后的驱动程序与您安装的 Chrome 主版本号不一致。
关键要点
- Idealista 的阻断机制结合了 DataDome 挑战、TLS 指纹识别和 IP 声誉评估,因此仅靠轮换 User-Agent 这一单一策略在大规模场景下难以奏效。
- 请将
inmueble/<id>slug 作为去重键和主要标识符;标题和价格会变,ID 不会变。 - 限制分页范围(每个市镇约 40 页),并将更深入的查询按价格区间或房产类型进行拆分,而不是无限递归。
- 按顺序分层部署防御措施:真实浏览器指纹、基于地理位置的住宅 IP、标头轮换、礼貌的速率控制,最后以托管抓取 API 作为后备方案。
- 通过按
fecha-publicacion-descID 并与上次运行结果进行比对;这正是将爬虫转变为实用产品的关键。
常见问题
抓取 Idealista 是否合法?GDPR 如何适用于经纪人的联系方式?
公开房源数据通常在合理频率下收集是合法的,但经纪人的姓名、电话和电子邮件属于 GDPR 下的个人数据,需要有记录在案的合法依据、保留期限和删除路径。在没有这些控制措施的情况下大规模存储或重新分发经纪人联系信息,是任何 Idealista 数据管道中风险最大的部分。请咨询律师以获取针对特定司法管辖区的指导。
Idealista是否提供官方的房产数据API?
Idealista 未发布通用型公开房源 API。其开发者计划侧重于合作伙伴集成和广告投放,而非开放数据访问。对于大多数分析或研究用例,抓取公开网站或购买托管房地产数据集是切实可行的选择。在假设任何特定接口可用之前,请直接查阅当前的开发者门户。
为何即使使用 undetected-chromedriver,Idealista 仍会显示 DataDome 验证码?
undetected-chromedriver 仅修复了一个信号,即 navigator.webdriver 标志以及少量 Chrome 内部机制。DataDome 还会评估 TLS 指纹、头部一致性、IP 声誉及行为速率。如果您的 IP 位于被标记的数据中心范围,或头部信息不一致,验证码挑战将持续触发。请首先添加住宅代理并降低请求速率。
针对 Idealista,我应该使用 Selenium、带 parsel 的 HTTPX,还是托管式爬取 API?
这取决于数据量。对于几百个页面,HTTPX 配合 parsel 速度最快。若需通过 JS 渲染实现可靠的逐页提取,Selenium 配合 undetected-chromedriver 是经过验证的折中方案。若每日处理量超过几千个页面,托管式爬取 API 可消除代理和挑战解决的开销,且通常比维护自己的指纹识别系统更经济。
同一个爬虫能否同时处理 Idealista 的意大利语和葡萄牙语网站以及租房列表?
基本上可以。意大利(idealista.it)和葡萄牙(idealista.pt)站点通常采用与西班牙站相同的布局,而租房信息则遵循 alquiler-viviendas 与 venta-viviendas。请务必在实时页面上验证选择器,因为 Idealista 偶尔会对卡片布局和分页功能进行针对特定语言环境的 A/B 测试。
结论
掌握如何从 Idealista 抓取数据,主要在于如何应对该网站多年来构建的分层防御机制。确保 URL 映射准确,使用带有 undetected-chromedriver 的真实浏览器指纹,绑定分页逻辑,在 inmueble/<id> ,并将写入数据流式传输至磁盘,确保崩溃不会导致整个运行流程报废。将 DataDome 提示视为 IP、指纹或速率已触及红线的警示信号,而非只需点击跳过的弹窗。一旦从原型阶段迈入生产级规模,多层防御策略(住宅代理、地理定位、头部轮换、重试逻辑)几乎能立即体现其价值。
如果您希望完全跳过代理配置和挑战处理环节,WebScrapingAPI 的 Scraper API 可在单一端点后处理数据抓取层,返回的 HTML 内容可直接投入上述解析器中。无论采用哪种方式,此处的工作流都能为您提供一条清晰的路径,助您从单机原型逐步过渡到定时运行的管道,以合理的频率追踪西班牙语、意大利语和葡萄牙语的新房源信息。




