Airbnb 房源页面承载着驱动旅游研究、市场分析和竞争监控所需的价格情报:每晚房价、房源标题、客户评分,以及房东公开展示的位置信息。问题在于,Airbnb 以客户端渲染方式呈现这些页面,并对自动化流量进行强力防御,因此一个普通的 HTTP 请求返回的只是一个空壳,其中没有任何价格信息。

本指南展示了如何以可靠的方式使用 Python 抓取 Airbnb 价格。你将构建一个小型可运行的爬虫,通过 Crawling API 获取渲染后的房源页面,使用 BeautifulSoup 解析你需要的字段,并打印出一条干净的结构化记录。整个演示限定在公开房源数据范围内,末尾的合法性章节不是套话,请在将本指南应用于任何实际场景之前务必阅读。

你将构建什么

一个 Python 脚本,接收一个公开的 Airbnb 房源 URL,通过 Crawling API 获取渲染后的 HTML,并为该房源提取结构化记录。我们将以一个单间 URL 作为运行示例,并提取以下字段:

  • 标题 页面顶部显示的房源名称。
  • 价格 你请求日期的每晚房价。
  • 评分 公开的客户评分,如"4.92"。
  • 位置 Airbnb 为该房源公开展示的区域。

为什么普通请求在 Airbnb 上会失败

如果你使用裸 HTTP 客户端请求一个 Airbnb 房间 URL,你得到的响应状态为 200,但正文中几乎没有任何房源数据。有两个因素对你不利。首先,Airbnb 使用 JavaScript 在浏览器中渲染其房源内容,因此初始 HTML 只是一个外壳,只有在页面脚本运行后才会填充内容。其次,Airbnb 会迅速标记自动化流量:看起来不像真实浏览器的数据中心 IP 和请求模式,在达到渲染内容之前就会被质询或封锁。

定价方面还有一个特殊的第三个问题。自 Airbnb 重构定价方式以来,只有当页面包含入住日期、退房日期和访客人数时,每晚房价才会出现。价格是动态的:同一房源在不同日期、周末和旺季显示不同的数字。因此,一个能正常工作的 Airbnb 价格爬虫需要:能渲染页面的浏览器、平台识别为真实访客的 IP,以及 URL 中的日期参数(否则价格根本不会渲染)。

你可以自己用无头浏览器加轮换住宅代理池来搭建这套方案,但将其组合在一起并保持健康运行才是工作量的主要所在。Crawling API 将难点合并为一次调用:你发送带有 JavaScript token 的房源 URL,它在可信 IP 背后渲染页面,并返回可供解析的完整 HTML。

为什么需要 JS token

Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 会先在真实浏览器中渲染页面。Airbnb 是客户端渲染的,因此这里需要 JS token。使用普通 token 返回的与普通请求一样只是空壳,其中没有任何价格可解析。

前提条件

编写任何代码之前,需要准备几件事。每项都不需要太长时间。

基本的 Python 知识。 你应该能够编写和运行 Python 脚本,并使用 pip 安装包。如果你是 HTML 解析的新手,我们关于如何使用 BeautifulSoup in Python 的指南涵盖了本教程假定你已了解的选择器基础知识。

Python 3.8 或更高版本。 使用 python --version 确认你的版本。如果尚未安装,请从 python.org 或通过 Anaconda 等发行版安装。

Crawlbase 账号和 JS token。 注册后,打开你的控制台,从账号文档页面复制你的 JavaScript(JS)token。像密码一样保护该 token:它用于验证你的请求,因此不要将其提交到版本控制。

搭建项目

创建一个虚拟环境以隔离项目依赖,然后安装爬虫所需的两个库。

bash
python --version

python -m venv airbnb_env
source airbnb_env/bin/activate

pip install crawlbase beautifulsoup4

在 Windows 上,使用 airbnb_env\Scripts\activate 代替 source 那行来激活环境。两个依赖项各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML,让你可以通过 CSS 选择器提取各个字段。

步骤 1:获取渲染后的房源页面

首先获取完整页面。导入 CrawlingAPI 类,用你的 JS token 初始化它,并附带日期参数请求房源 URL。在解析之前检查状态码,这样错误就会立即显现而不是悄悄失败。

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("latin1")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    page_url = (
        "https://www.airbnb.com/rooms/721540609203378406"
        "?check_in=2026-07-09&check_out=2026-07-12&adults=2"
    )
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

check_incheck_outadults 查询参数对于获取价格不是可选的。缺少它们,页面虽然能渲染,但每晚房价始终隐藏,无论你的选择器多么精确,价格字段都会返回空值。两个等待选项对于像这样的客户端渲染目标至关重要:ajax_wait 告诉 API 等待异步内容完成加载,page_wait 在加载后等待固定的毫秒数,确保延迟渲染的元素在捕获页面之前出现。五秒是合理的起点;如果价格返回空白,可以适当增加。Airbnb 以多种编码格式发送页面,因此使用 latin1 解码可以避免严格 UTF-8 解码可能抛出的字节错误。运行脚本,你应该会看到真实的房源标记,而不是普通请求返回的空壳。

Crawlbase Airbnb Scraper

Airbnb 需要在一次调用中同时完成页面渲染和可信 IP 访问。Crawling API 接收 JS token,在真实浏览器中运行页面,在服务端轮换住宅 IP,并将完整 HTML 返还给你,省去了自行运行无头浏览器集群和代理池的麻烦。先在免费套餐上试试公开的房源。

步骤 2:使用 BeautifulSoup 解析房源字段

拿到渲染后的 HTML 后,将其加载到 BeautifulSoup 中,通过选择器提取每个字段。Airbnb 将核心信息展示在稳定的区段容器中,因此你可以将标题、价格、评分和位置分别映射到各自的选择器。将整个提取过程包裹在 try/except 中,这样某个字段缺失也不会导致整个运行崩溃。

python
from bs4 import BeautifulSoup

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")

    return {
        "title": text_of(soup, 'div[data-section-id="TITLE_DEFAULT"] h1'),
        "price": text_of(soup, 'div[data-testid="book-it-default"] span > span'),
        "rating": text_of(soup, 'div[data-testid="pdp-reviews-highlight-banner-host-rating"] span'),
        "location": text_of(soup, 'div[data-section-id="LOCATION_DEFAULT"] h3'),
    }

text_of 辅助函数同时完成两件有用的事:查询单个元素,并在元素缺失时返回 None,而非在对空值调用 .get_text() 时抛出异常。这使得提取过程在某个字段不存在时仍然具有弹性,这种情况很常见,例如全新的房源可能还没有任何评分。Airbnb 的 data-section-iddata-testid 属性比其哈希化的 CSS 类名更为稳定,因此尽量以这些数据属性作为锚点。

选择器会发生变化

Airbnb 的标记,尤其是其哈希化的类名,会在不通知的情况下更改。将上述选择器视为起始模板,而非合同。当某个字段返回 None 时,请在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器是任何生产爬虫的正常操作,而非出了问题的信号。

步骤 3:整合代码

现在将获取和解析合并为一个可运行的脚本。获取渲染后的 HTML,将其传给解析器,并以 JSON 格式打印结构化记录。

python
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("latin1")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    return {
        "title": text_of(soup, 'div[data-section-id="TITLE_DEFAULT"] h1'),
        "price": text_of(soup, 'div[data-testid="book-it-default"] span > span'),
        "rating": text_of(soup, 'div[data-testid="pdp-reviews-highlight-banner-host-rating"] span'),
        "location": text_of(soup, 'div[data-section-id="LOCATION_DEFAULT"] h3'),
    }

def main():
    page_url = (
        "https://www.airbnb.com/rooms/721540609203378406"
        "?check_in=2026-07-09&check_out=2026-07-12&adults=2"
    )
    html = crawl(page_url)
    if not html:
        return
    data = scrape_listing(html)
    print(json.dumps(data, indent=2))

if __name__ == "__main__":
    main()

输出结果示例

使用 python scraper.py 运行完整脚本,你会得到该房源的一条干净的结构化记录,可直接写入 JSON、CSV 或数据库。

json
{
  "title": "Lovely Studio with Burj Khalifa views from Balcony",
  "price": "$187 per night",
  "rating": "4.92",
  "location": "Dubai, United Arab Emirates"
}

跨日期抓取价格

单一日期范围只是演示;真正有价值的定价工作是对同一房源跨多个日期进行比较,因为 Airbnb 的价格随日历变化而波动。结构保持不变:维护一个日期对列表,为每个日期对重建 URL,通过 Crawling API 获取,并使用同一函数解析。由于房源结构不会改变,你已经编写的解析器对每个日期都能正常工作,无需任何修改。

python
room_id = "721540609203378406"
date_ranges = [
    ("2026-07-09", "2026-07-12"),
    ("2026-08-13", "2026-08-16"),
]

results = []
for check_in, check_out in date_ranges:
    url = (
        f"https://www.airbnb.com/rooms/{room_id}"
        f"?check_in={check_in}&check_out={check_out}&adults=2"
    )
    html = crawl(url)
    if html:
        row = scrape_listing(html)
        row["check_in"] = check_in
        results.append(row)

with open("airbnb_prices.json", "w") as f:
    json.dump(results, f, indent=2)

要找到房源 URL,你可以使用相同的模式获取 Airbnb 的公开搜索结果,收集房间链接,然后附带日期参数逐个访问。只需将请求量控制在合理范围内,并遵守下文涵盖的速率预期。如果你打算将这些数据输入定价模型,我们关于将网络爬取用于价格情报的指南介绍了团队如何将一系列房价数据转化为可决策的信息。

保持不被封锁

即使渲染问题已解决,Airbnb 仍然会监控爬虫形态的流量。以下几个习惯能保持运行的健康,它们适用于任何防御严密、JavaScript 密集的目标。

  • 控制请求节奏。 在紧密循环中对同一房源跨多个日期范围频繁请求,是最快遭到限流的方式。分散请求,变换目标,而不是以全速爬取同一路径。
  • 依赖轮换。 一组住宅 IP 将请求分散到众多真实用户地址,使没有任何单一 IP 触发速率限制。Crawling API 已为你处理这些;如果自建方案,这一环节是关键。
  • 读懂状态码。 如果运行开始返回质询或错误,说明当前速率或 IP 层级已不再足够。将这视为需要退后的信号,而非可以忽略的噪音。

关于更广泛的方法,请参阅如何不被封锁地爬取网站和关于如何在网络爬取中绕过 CAPTCHA 的深度指南。由于 Airbnb 的 JavaScript 密集程度很高,我们关于如何抓取 JavaScript 网站的演示也值得一读。如果你更倾向于通过轮换 IP 池路由自己的流量而非使用托管 API,Smart AI Proxy 提供与之相同的住宅 IP 轮换,作为即插即用的代理端点。

抓取 Airbnb 合法吗?

爬取 Airbnb 是否被允许,取决于 Airbnb 的服务条款、你所在的司法管辖区,以及你对数据的处理方式。Airbnb 的服务条款明确限制了自动访问,因此无论你的工具多么谨慎,爬取都可能与这些条款相违背。这里的任何代码都无法改变这一点;代码只是让技术部分可行。请阅读 Airbnb 的服务条款和其 robots.txt,并将两者视为你采集内容的边界。

以下是应当坚守的几条原则。仅收集公开房源数据:标题、每晚价格、公开评分,以及 Airbnb 在无需账号的情况下显示的位置。遵守 Airbnb 的速率预期,保持请求量足够低,确保不会给其服务器造成压力。本指南刻意限定在公开房源页面范围内,因为这是使工作具有可辩护性的界限。

同样重要的是明确涵盖的内容。不要收集房东或房客的个人数据、联系信息或消息。不要访问需要登录的页面、账户或预订数据,以及任何需要绕过身份验证才能访问的内容。这些内容在此范围之外,且违反了 Airbnb 的服务条款。对于商业或大规模使用,正确的路径是与 Airbnb 达成官方合作或 API 协议,而非更聪明的爬虫。仅限公开房源数据。

回顾

核心要点

  • Airbnb 是客户端渲染的。 普通请求返回的只是空壳,因此在解析之前必须先渲染页面。
  • 价格需要日期参数。 在 URL 中传入 check_incheck_outadults,否则每晚房价永远不会渲染;价格是动态的,随日历变化。
  • 渲染和可信 IP 需要同时具备。 使用 JS token 的 Crawling API 在一次调用中完成两者;ajax_waitpage_wait 控制等待内容加载的时长。
  • BeautifulSoup 负责提取。data-section-iddata-testid 属性为锚点提取标题、价格、评分和位置,并预期选择器会发生变化。
  • 坚守公开数据边界。 遵守 Airbnb 的服务条款和 robots.txt,商业用途优先考虑官方合作,绝不触碰房东或房客的个人数据、消息或需要登录才能访问的页面。

常见问题

为什么普通请求从 Airbnb 得不到价格?

有两个原因。首先,Airbnb 使用 JavaScript 在客户端渲染其房源内容,因此原始 HTML 只是一个外壳,只有在页面脚本运行后才会填充内容。其次,每晚房价只有当 URL 携带入住和退房日期以及访客人数时才会渲染。没有日期的裸 HTTP 请求返回状态 200 且没有价格。使用 Crawling API 的 JS token 渲染页面,并在 URL 中附带日期,才能让价格出现。

Airbnb 需要普通 token 还是 JS token?

JS token。普通 token 获取静态 HTML,在 Airbnb 上返回的与普通请求一样只是空壳。JS token 在将 HTML 返回之前先在真实浏览器中渲染页面,因此当 BeautifulSoup 解析时,房源字段(包括价格)已经存在。

为什么同一房源显示不同的价格?

Airbnb 使用动态定价。每晚房价随你请求的日期、工作日与周末、需求和季节而变化,一些房东还会设置入住天数折扣。这是预期行为,而非你爬虫的问题。如果你想比较价格,请像本指南中的多日期示例那样,对同一房间循环几个日期范围并记录每次的房价。

我的选择器返回 None。发生了什么变化?

几乎肯定是 Airbnb 的标记发生了变化。其哈希化的 CSS 类名频繁更改,区段布局也会随时间调整,因此上个月还能正常工作的选择器可能已经失效。尽可能将 data-section-iddata-testid 属性作为锚点,当某个字段返回空值时,请在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器是任何生产爬虫的正常操作。

如何避免爬取 Airbnb 时被封锁?

保持低频率的 IP 请求速率,变换目标而不是对同一房源跨多个日期连续循环请求,并通过轮换住宅 IP 路由,确保没有单一地址触发速率限制。Crawling API 为你管理轮换和可信 IP 池;如果自建方案,这一环节是值得投入的地方。留意状态码,一旦开始看到质询就立即退后。

我可以合法收集哪些 Airbnb 数据?

坚守公开房源数据:标题、每晚价格、公开评分,以及无需登录即可在房源页面上看到的位置。不要收集房东或房客的个人数据、联系方式、消息,或任何需要登录才能访问的内容,也不要绕过身份验证。遵守 Airbnb 的服务条款和 robots.txt,对于商业或大规模使用,追求官方合作或 API 协议,而不是针对公开网站扩展爬虫规模。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量