TripAdvisor坐拥网络上最大的用户生成旅游数据池之一:数亿条针对酒店、餐厅和景点的评论、评分和列表信息。这使它成为市场研究、竞品对标和声誉追踪的诱人数据源。问题在于,TripAdvisor用JavaScript渲染它的大部分内容,并对自动化流量严防死守,所以一个普通HTTP请求拿回的是一个近乎空白的页面,而且往往还会被封锁。

本指南向你展示如何用Python以可靠的方式抓取TripAdvisor:把你的请求路由通过Crawlbase Smart AI Proxy,让它替你渲染页面并轮换IP,再用BeautifulSoup解析返回的HTML,从公开的搜索列表中提取名称、评分、评论数和位置。这里的一切都可运行,整个演练只限定在公开数据上。

抓取TripAdvisor合法吗

抓取一个大型商业平台处于法律的灰色地带,是否被允许取决于TripAdvisor的使用条款、你所在的司法辖区,以及你用这些数据做什么。TripAdvisor的条款限制自动化访问,所以不论你的工具多么谨慎,抓取都可能与那些条款相抵触。这里的代码没有一行能改变这一点;它只是把技术部分跑通。

有几条值得坚守的底线。只采集公开数据:任何人无需账户就能看到的列表名称、评分、评论数和位置。尊重TripAdvisor的robots.txt及其声明的速率预期,把你的请求量保持得足够低,以免给任何人的服务器带来压力。评论是关联到真实个人的用户生成内容,所以不要把它们整体转载,也不要抓取任何能识别个人身份的内容。如果你打算把数据用于商业用途,去取得许可或正式的数据协议,而不要假定沉默就是同意。

本指南刻意限定在公开列表数据上,因为那是让这项工作站得住脚的那条线。它不涉及任何登录之后的内容、账户或个人资料数据、私信,或任何绕过身份验证的尝试。如果你的项目所需超出公开列表,正确的做法是与TripAdvisor签订一个正式的API或数据协议,而不是一个更聪明的抓取器。

为什么要抓取TripAdvisor

公众的旅游情绪时刻在变,而单次页面浏览只能告诉你一个列表此刻的样子。抓取TripAdvisor的公开列表,让你能长期追踪评分、评论数和排名,而那正是大多数研究任务真正依赖的东西。几个具体用途:

  • 市场研究。通过观察某个品类下的评论量和评分,发现热门目的地和正在变化的客户偏好。
  • 竞争分析。在同一市场上,以评分和评论数为基准,把你的酒店、餐厅或景点与竞争对手对标。
  • 声誉监控。长期追踪你自己列表的评分走势,以便在下滑早期就做出反应。
  • 趋势发现。跨多个列表汇总情绪,浮现出单个页面永远无法揭示的新兴模式。

对工程师而言,价值在于结构化数据:把一个渲染后的搜索页变成你可以查询、绘图或喂给模型的干净行。

为什么普通获取在这里会失败

如果你用一个裸HTTP客户端请求TripAdvisor的URL,你通常会得到一个状态为200、却几乎没有你想要的数据的响应。有两件事在和你作对。首先,TripAdvisor用JavaScript在浏览器中渲染它的列表,所以初始HTML只是一个外壳,要在页面脚本运行之后才填充。其次,TripAdvisor会很快标记自动化流量:数据中心IP以及看起来不像真实浏览器的请求模式,会在到达渲染后的内容之前就被质询或封锁。

因此一个能用的TripAdvisor抓取器需要在一次请求中具备两样东西:一个真正渲染页面的浏览器,以及一个被平台读作真实访客的IP。你可以自己用无头浏览器加上一组轮换的住宅代理来拼凑这套方案,但把它们缝合起来并保持健康才是大部分工作。Crawlbase Smart AI Proxy把两者折叠进一个端点:你把你普通的HTTP客户端指向它,传入几个参数,它就会在一个受信任的、轮换的IP背后渲染页面,并返回处理好的HTML。

Smart AI Proxy是什么

Smart AI Proxy是一个由Crawlbase的Crawling API支撑的即插即用代理端点。你照常继续使用requests(或任何客户端),只是把流量路由经过它。在底层,它轮换一个庞大的数据中心和住宅IP池,并能渲染JavaScript,所以你无需围绕一个新的SDK重写代码,就能获得Crawling API的能力。

搭建环境

你需要Python 3.8或更高版本。确认你的版本,创建一个虚拟环境让项目依赖保持隔离,然后安装本指南所用的三个库。

bash
python --version

python -m venv tripadvisor_env
source tripadvisor_env/bin/activate

pip install requests beautifulsoup4 pandas

在Windows上,用tripadvisor_env\Scripts\activate来激活环境,替代那行source。这三个依赖完成主要工作:requests发起HTTP调用,beautifulsoup4解析返回的HTML,而pandas组织你的结果并把它们写入文件。

你还需要一个Crawlbase账户和一个访问令牌,注册之后从仪表盘获取它。把它放进代码中任何出现YOUR_ACCESS_TOKEN的地方。

通过Smart AI Proxy发送请求

Smart AI Proxy只是一个HTTP代理端点,所以你配置它的方式,和你在requests中配置任何代理的方式相同。把你的访问令牌放进代理URL,让httphttps两个条目都指向它,并发起一个普通的GET请求。因为代理替你终结TLS,所以传入verify=False来跳过本地证书检查。

python
import requests

proxy_url = "http://YOUR_ACCESS_TOKEN:@smartproxy.crawlbase.com:8012"
target_url = "https://www.tripadvisor.com/Search?q=london"

proxies = {"http": proxy_url, "https": proxy_url}

response = requests.get(target_url, proxies=proxies, verify=False)

print("Status:", response.status_code)
print(response.content[:500])

那一次调用就是整个集成。你的代码保持纯粹的requests;代理处理IP轮换以及那种让你在第一次访问就不被封锁的受信任访客信誉。

传递Crawling API参数

Smart AI Proxy通过一个请求头CrawlbaseAPI-Parameters,暴露出和Crawling API相同的那些控制项。你以查询字符串风格的值传入选项。例如,要把请求地理定位到美国,设置country参数:

python
import requests

proxy_url = "http://YOUR_ACCESS_TOKEN:@smartproxy.crawlbase.com:8012"
target_url = "https://www.tripadvisor.com/Search?q=london"

headers = {"CrawlbaseAPI-Parameters": "country=US"}
proxies = {"http": proxy_url, "https": proxy_url}

response = requests.get(target_url, headers=headers, proxies=proxies, verify=False)
print(response.content[:500])

你可以在同一个请求头的值里用&把多个选项串起来,接下来你正是这样打开JavaScript渲染的。

渲染JavaScript密集的页面

TripAdvisor在客户端加载它的列表,所以不渲染的话你又会拿到那个空壳。用javascript=true打开一个无头浏览器,并加上page_wait,在加载后保持固定的毫秒数,让晚渲染的元素在页面被捕获之前出现。五秒是一个合理的起点;如果结果回来得很单薄,就把它调高。

python
import requests

proxy_url = "http://YOUR_ACCESS_TOKEN:@smartproxy.crawlbase.com:8012"
target_url = "https://www.tripadvisor.com/Search?q=london"

headers = {"CrawlbaseAPI-Parameters": "javascript=true&page_wait=5000"}
proxies = {"http": proxy_url, "https": proxy_url}

response = requests.get(target_url, headers=headers, proxies=proxies, verify=False)
html_content = response.content.decode("utf-8")

print("Fetched", len(html_content), "characters of rendered HTML")

打开JavaScript渲染后,响应体现在包含的是填充好的列表,而不是一个空白的骨架。那就是你将交给BeautifulSoup的HTML。

Crawlbase TripAdvisor Scraper

TripAdvisor需要一个在受信任IP背后渲染的页面,而Smart AI Proxy以一个即插即用的端点把两者都给你。照常继续使用requests,传入javascript=true,它就会在真实浏览器中渲染页面,在服务端轮换住宅和数据中心IP,并把处理好的HTML交回给你,这样你就无需自己运行一支无头集群和一个代理池。先在免费层把它指向一个公开搜索试试。

用BeautifulSoup解析搜索列表

手里有了渲染后的HTML,把它加载进BeautifulSoup并遍历结果卡片。第一项工作是找到承载搜索结果的容器。在浏览器中打开页面,右键点击一个结果,选择"检查"以读取实时标记。

在TripAdvisor的搜索视图中,每个列表都位于一个带有result类的div里,而这些都活在一个以data-widget-type="LOCATIONS"为键的结果列表内。选中它并循环卡片:

python
from bs4 import BeautifulSoup

soup = BeautifulSoup(html_content, "html.parser")

search_results = soup.select(
    'div.search-results-list[data-widget-type="LOCATIONS"] div.result'
)

for result in search_results:
    # extract fields from each result here
    pass

现在把每个字段映射到它的选择器。检查一张卡片,能看出名称、评分、评论数和位置都在哪里:

  • 名称位于一个div.result-title内的<span>里。
  • 评分在一个span.ui_bubble_rating中,其值存放在它的alt属性里。
  • 评论数是一个a.review_count链接的文本。
  • 位置是一个div.address-text的文本。
python
name_el = result.select_one("div.result-title span")
name = name_el.text.strip() if name_el else None

rating_el = result.select_one("span.ui_bubble_rating")
rating = rating_el["alt"] if rating_el else None

reviews_el = result.select_one("a.review_count")
reviews = reviews_el.text.strip() if reviews_el else None

location_el = result.select_one("div.address-text")
location = location_el.text.strip() if location_el else None
选择器会漂移

TripAdvisor会不经通知地更新它的标记,所以像result-titleui_bubble_rating这样的类名可能会变。把上面的选择器当作起始模板,而非契约。当某个字段返回None时,在浏览器开发者工具中重新检查一个实时搜索页并更新选择器。对任何生产级抓取器来说,这都是正常的维护,并不意味着出了什么问题。

完整的抓取器

下面是接进一个可运行文件的全部内容。它通过Smart AI Proxy获取渲染后的搜索页,解析每个结果,收集这些行,并以JSON打印出来。

python
import json
import requests
from bs4 import BeautifulSoup

proxy_url = "http://YOUR_ACCESS_TOKEN:@smartproxy.crawlbase.com:8012"
target_url = "https://www.tripadvisor.com/Search?q=london"

headers = {"CrawlbaseAPI-Parameters": "javascript=true&page_wait=5000"}
proxies = {"http": proxy_url, "https": proxy_url}


def parse_results(html):
    soup = BeautifulSoup(html, "html.parser")
    cards = soup.select(
        'div.search-results-list[data-widget-type="LOCATIONS"] div.result'
    )
    rows = []
    for result in cards:
        name_el = result.select_one("div.result-title span")
        rating_el = result.select_one("span.ui_bubble_rating")
        reviews_el = result.select_one("a.review_count")
        location_el = result.select_one("div.address-text")
        rows.append({
            "name": name_el.text.strip() if name_el else None,
            "rating": rating_el["alt"] if rating_el else None,
            "reviews": reviews_el.text.strip() if reviews_el else None,
            "location": location_el.text.strip() if location_el else None,
        })
    return rows


def main():
    response = requests.get(target_url, headers=headers, proxies=proxies, verify=False)
    html = response.content.decode("utf-8")
    rows = parse_results(html)
    print(json.dumps(rows, indent=2))


if __name__ == "__main__":
    main()

输出长什么样

python tripadvisor_scraper.py运行它,你会得到一个由结构化列表对象组成的数组。一个精简后的样本:

json
[
  {
    "name": "London Eye",
    "rating": "4.5 of 5 bubbles",
    "reviews": "89,766 reviews",
    "location": "Westminster Bridge Road, London, England, United Kingdom"
  },
  {
    "name": "Big Bus London Hop-On Hop-Off Tour and River Cruise",
    "rating": "4 of 5 bubbles",
    "reviews": "8,656 reviews",
    "location": "London, England, United Kingdom"
  },
  {
    "name": "North London Skydiving Centre",
    "rating": "5 of 5 bubbles",
    "reviews": "2,889 reviews",
    "location": "Block Fen Drove, Wimblington, Cambridgeshire, England, United Kingdom"
  }
]

处理分页

一页结果只是演示;真实的任务会遍历多页。TripAdvisor用o(offset,偏移量)查询参数来翻阅搜索结果,每次前进一页的结果数量。在一个范围上循环,每次抬高偏移量,并把这些行收集到一个列表中。

python
base_url = "https://www.tripadvisor.com/Search?q=london"
all_rows = []
offset = 0

for page in range(5):  # adjust to the number of pages you want
    target_url = f"{base_url}&o={offset}"
    response = requests.get(target_url, headers=headers, proxies=proxies, verify=False)
    html = response.content.decode("utf-8")
    all_rows.extend(parse_results(html))
    offset += 30  # results per page

print(f"Collected {len(all_rows)} listings")

复用完整脚本中的parse_results,能把提取逻辑保持在一处,所以每一页都经过相同的选择器。给循环放慢节奏,别一个接一个地连发请求;代理替你轮换IP,但平稳的节奏能在一个防御性强的站点上让一次运行保持健康。

把数据保存到Excel

在你迭代的时候记录到控制台没问题,但你会想把数据落到磁盘上。有了pandas,把你的行变成一个任何人都能在电子表格中打开的Excel文件只需两行。

python
import pandas as pd

df = pd.DataFrame(all_rows)
df.to_excel("tripadvisor_data.xlsx", index=False)
print("Saved tripadvisor_data.xlsx")

每个对象键都成为一列,所以你最终得到一张整洁的工作表,含有名称、评分、评论数和位置。如果你更愿意用SQL查询数据,就把同样的行写进一张SQLite表;解析部分保持不变。

保持不被封锁

即便渲染和IP轮换已由Smart AI Proxy处理,TripAdvisor仍会监控具有抓取器特征的流量。一些习惯能让一次运行保持健康,它们适用于任何难啃的商业目标。

  • 给请求放慢节奏。在一个紧凑的循环里猛锤同一个搜索,是最快被限流的方式。把请求摊开,并变换你的搜索查询。
  • 依靠轮换。一组住宅代理把请求分散到许多真实用户IP上,这样就没有任何单一地址会触发速率限制。Smart AI Proxy替你处理这件事;如果你自己搭建,这就是需要做对的部分。
  • 读懂状态码。一次开始返回质询或错误的运行,是在告诉你当前的速率或IP层级已经不够用了。把那些响应当作信号,收手,稍后再重试。

关于更宏观的攻略,参见如何抓取网站而不被封锁

回顾

核心要点

  • TripAdvisor是客户端渲染的。普通获取返回的是一个近乎空白的外壳,所以你必须在解析之前先渲染页面。
  • Smart AI Proxy是一个即插即用的修复方案。把纯粹的requests路由经过它,传入javascript=true,你就能在一个端点里得到渲染加IP轮换,无需新的SDK。
  • 用参数调优。通过CrawlbaseAPI-Parameters请求头发送像countrypage_wait这样的选项,以进行地理定位和等待内容。
  • 由BeautifulSoup完成提取。把名称、评分、评论数和位置映射到当前的选择器,并预期那些选择器会随时间漂移。
  • 守在公开数据上。尊重TripAdvisor的使用条款和robots.txt;不碰账户、不碰个人数据、不整体转载评论。

常见问题

抓取TripAdvisor合法吗?

这取决于TripAdvisor的使用条款、你所在的司法辖区,以及你的目的,而它们的条款限制自动化访问。严格只采集公开的列表数据,例如名称、评分、评论数和位置,尊重robots.txt和声明的速率预期,并且永远不要触碰账户、个人数据或登录墙后的内容。评论是关联到真实个人的用户内容,所以不要把它们整体转载。对于商业再利用,去取得许可或正式的数据协议,而不要依赖一个抓取器。

为什么普通获取从TripAdvisor返回不了数据?

因为TripAdvisor用JavaScript在客户端渲染它的列表。初始HTML只是一个外壳,要在页面脚本在浏览器中运行之后才填充,所以一个原始HTTP请求返回的是状态200、而字段为空。要拿到真实数据,你必须先渲染页面,而那正是Smart AI Proxy的javascript=true参数替你处理的事。

我如何处理TripAdvisor上的动态内容加载?

通过在CrawlbaseAPI-Parameters请求头中传入javascript=true,经由Smart AI Proxy启用JavaScript渲染,并加上page_wait,在加载后保持几秒,让晚渲染的元素出现。那个组合会在返回HTML之前于一个真实的无头浏览器中运行页面,所以当BeautifulSoup解析时,动态列表已经在那里了。

我能抓取TripAdvisor搜索结果的多个页面吗?

可以。TripAdvisor用o偏移量参数来翻阅搜索结果,所以你在一个范围上循环,每次把偏移量抬高一页的结果数量,并把这些行收集到一个列表中。给循环放慢节奏,别一个接一个地连发请求,并对每一页复用相同的解析函数。

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

几乎肯定是TripAdvisor的标记变了。像result-titleui_bubble_rating这样的类名会不经通知地变,所以上个月还能用的选择器可能会失效。在浏览器开发者工具中重新检查一个实时搜索页并更新选择器。对任何生产级抓取器来说,定期维护选择器都属正常。

TripAdvisor允许网络抓取吗?

TripAdvisor的条款限制自动化访问,所以它并不普遍许可抓取。话虽如此,在负责任地进行时,通过像Smart AI Proxy这样的工具采集像列表名称、评分、评论数和位置这类公开可见的数据,是风险更低的路径:守在公开数据上,尊重robots.txt和速率限制,并避开任何关联到个体用户的内容。

开始构建

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

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

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