in

如何从Expedia抓取数据

如何从Expedia抓取数据 new

Expedia是一个汇总酒店列表、活动和旅行选项信息的网站。通过抓取Expedia的数据,你可以获取有价值的信息,用于市场调研、竞争对手分析,或者只是为了找到最优惠的酒店交易。

在本文中,你将了解到一些抓取Expedia数据的最佳方法,包括一个使用Python抓取Expedia酒店价格数据的完整示例。


从Expedia抓取数据有哪些挑战?

像大多数现代网站一样,Expedia是一个动态网站,使用大量JavaScript来渲染内容和处理用户交互。因此,使用传统的基于HTML的抓取工具(如Beautiful SoupCheerio)很难抓取数据,因为它们无法执行JavaScript。


有哪些工具或库可以高效地从Expedia抓取数据?

要有效地从Expedia抓取数据,你需要使用更高级的抓取工具,比如无头浏览器(如PuppeteerPlaywright),这些工具可以渲染网页并执行JavaScript,使你能够访问动态生成的内容。这些工具提供了与Expedia动态元素交互并提取所需信息的灵活性。

Playwright是抓取像Expedia这样动态网站的最佳工具之一。它有多个语言的官方绑定,如JavaScript、Python、Java和C#,使各种开发者都能使用,无论他们熟悉哪种语言。


使用Python和Playwright抓取Expedia数据

在接下来的部分中,你将学习如何通过Python使用Playwright来抓取某个城市的酒店信息。

设置

要跟随本教程,你需要在电脑上安装Python。如果你还没有安装,可以按照官方说明下载并安装。

你还需要安装Playwright。使用以下命令来安装该库及其支持的浏览器。

pip install playwright
playwright install

抓取酒店数据

要抓取特定地点和日期的酒店数据,首先需要在Playwright中打开网站。

以下示例代码启动浏览器并打开Expedia页面,查询特定地点(意大利米兰)和日期(2024年6月1日至7日)的酒店信息。

import time
from playwright.sync_api import sync_playwright

with sync_playwright() as pw:
    browser = pw.firefox.launch(
        headless=False,
    )

    page = browser.new_page()
    page.goto(        'https://www.expedia.com/Hotel-Search?adults=2&d1=2024-06-01&d2=2024-06-07&destination=Milan%20%28and%20vicinity%29%2C%20Lombardy%2C%20Italy&endDate=2024-06-07&latLong=45.47179%2C9.18617®ionId=180012&rooms=1&semdtl=&sort=RECOMMENDED&startDate=2024-06-01&theme=&useRewards=false&userIntent=')
    time.sleep(2)
    browser.close()

你可以编写一个脚本,在网页界面中输入你希望的地点和日期,并点击搜索按钮以获取URL。虽然本教程不会涉及如何实现这一点,但这是一个在完成教程后可以尝试的有趣练习。

到达页面后,你需要从搜索结果中出现的卡片中抓取信息。

epxedia 1.png

首先,选择所有的卡片。

cards = page.locator('[data-stid="lodging-card-responsive"]').all()

然后,遍历卡片以积累有关酒店的信息。在本例中,你将获取酒店当晚的标题、评级和价格。

expedia 4.png

hotels = []

for card in cards: 
  content = card.locator('div.uitk-card-content-section')
  title = content.locator('h3').text_content()
   
  if content.locator('span.uitk-badge-base-text').is_visible():
     rating = content.locator('span.uitk-badge-base-text').text_content()
  else:
     rating = False

  if content.locator('div.uitk-type-500').is_visible():
      price = content.locator('div.uitk-type-500').text_content()
  else:
     price = False

  hotel = {
    'title': title,
    'rating': rating,
    'price': price}
  
  hotels.append(hotel)

由于有些酒店尚未提供评分或价格(如果它们在所选日期已被预订满或关闭,这种情况会发生),你需要处理这些缺失值,以避免脚本崩溃。因此,上述代码在选择这些元素之前会检查它们是否可见。如果不可见,则将该元素的值设为False。

最后,你可以打印出酒店列表:

print(hotels)

下面是这部分的完整代码:

import time
from playwright.sync_api import sync_playwright

with sync_playwright() as pw:
    browser = pw.firefox.launch(
        headless=False,
    )

    page = browser.new_page()
    page.goto(
        'https://www.expedia.com/Hotel-Search?adults=2&d1=2024-06-01&d2=2024-06-07&destination=Milan%20%28and%20vicinity%29%2C%20Lombardy%2C%20Italy&endDate=2024-06-07&latLong=45.47179%2C9.18617®ionId=180012&rooms=1&semdtl=&sort=RECOMMENDED&startDate=2024-06-01&theme=&useRewards=false&userIntent=')
    
    time.sleep(2)
    
    #scrape hotels 

    cards = page.locator('[data-stid="lodging-card-responsive"]').all()
    hotels = []

    for card in cards: 
      content = card.locator('div.uitk-card-content-section')
      title = content.locator('h3').text_content()
       
      if content.locator('span.uitk-badge-base-text').is_visible():
         rating = content.locator('span.uitk-badge-base-text').text_content()
      else:
         rating = False

      if content.locator('div.uitk-type-500').is_visible():
          price = content.locator('div.uitk-type-500').text_content()
      else:
         price = False

      hotel = {
        'title': title,
        'rating': rating,
        'price': price}
      
      hotels.append(hotel)
    
    print(hotels)

    browser.close()

一旦运行,它应该返回一个酒店列表:

[{'title': 'Milano Verticale | UNA Esperienze', 'rating': '9.2', 'price': '$379'}, {'title': 'Hyatt Centric Milan Centrale', 'rating': '8.8', 'price': '$326'}, {'title': 'UNAHOTELS Galles Milano', 'rating': '8.6', 'price': '$258'}, {'title': 'Residence de la Gare', 'rating': '8.8', 'price': '$137'}...

但这份名单并没有列出所有的酒店。要访问所有这些,你需要通过单击列表底部的“Show More”按钮展开列表。

expedia 2.png

这就是使用web自动化库进行抓取的地方——你可以点击、编写和执行普通用户可以执行的任何其他操作!

使用Playwright扩展搜索结果

为了获取完整的搜索结果,你需要反复展开列表,直到“显示更多”按钮消失,然后再抓取结果。

在开始抓取卡片信息之前,将以下代码添加到脚本中间。它会定位按钮,点击它,等待结果加载,然后如果按钮仍然存在,就重复这个过程。

#scroll to the bottom of page

show_more = page.locator("button", has_text="Show More")

while show_more.is_visible() is True:
  show_more.click()
  time.sleep(5)

这种模式对于处理任何类型的元素重复加载都很有用,比如在动态网站中。

以下是脚本的完整代码:

import time
from playwright.sync_api import sync_playwright

with sync_playwright() as pw:
    browser = pw.firefox.launch(
        headless=False,
    )

    page = browser.new_page()
    page.goto(
        'https://www.expedia.com/Hotel-Search?adults=2&d1=2024-06-01&d2=2024-06-07&destination=Milan%20%28and%20vicinity%29%2C%20Lombardy%2C%20Italy&endDate=2024-06-07&latLong=45.47179%2C9.18617®ionId=180012&rooms=1&semdtl=&sort=RECOMMENDED&startDate=2024-06-01&theme=&useRewards=false&userIntent=')
    
    time.sleep(2)

    #scroll to the bottom of page

    show_more = page.locator("button", has_text="Show More")

    while show_more.is_visible() is True:
      show_more.click()
      time.sleep(5)
    
    #scrape hotels 

    cards = page.locator('[data-stid="lodging-card-responsive"]').all()
    hotels = []

    for card in cards: 
      content = card.locator('div.uitk-card-content-section')
      title = content.locator('h3').text_content()
       
      if content.locator('span.uitk-badge-base-text').is_visible():
         rating = content.locator('span.uitk-badge-base-text').text_content()
      else:
         rating = False

      if content.locator('div.uitk-type-500').is_visible():
          price = content.locator('div.uitk-type-500').text_content()
      else:
         price = False

      hotel = {
        'title': title,
        'rating': rating,
        'price': price}
      
      hotels.append(hotel)
    
    print(hotels)

    browser.close()

如何通过使用代理来克服Expedia的限制和反抓取措施?

仅抓取一个城市和一组日期的酒店数据不会触发Expedia的反抓取措施。由于该网站的流量非常大,这样的操作甚至不会引起注意。但如果你想收集包含多个城市、日期,甚至是酒店价格历史变化的大规模数据集,你的行为可能会被检测到,并且你的IP地址可能会被Expedia拒绝服务。

因此,代理被用于大规模的网页抓取活动。代理作为客户端和服务器之间的中间人,转发请求到服务器,但改变请求来源的IP地址。使用住宅代理这样的服务,你可以在每个请求中更换IP,从全球范围内的道德来源IP池中随机选择。这将有助于避免被检测到:你的请求看起来像是来自许多不同的用户,而不仅仅是一个。

以下是如何在你的Playwright脚本中添加代理的方法。

首先,你需要找到所选代理服务器的主机、端口、用户名和密码信息。这些在你购买代理IP后,可以在对应代理商的控制面板中找到。

然后,用数据更新pww .chromium.launch()函数。

browser = pw.chromium.launch(
    headless=False,
    proxy={
        'server':'host:port',
        'username':'xxx',
        'password': 'xxx',
    }
)

现在,Playwright会话中的所有请求都将通过你选择的代理服务器进行。


从Expedia抓取数据的潜在应用和使用场景有哪些?

从Expedia抓取数据可以为各种应用和使用场景提供有价值的信息。以下是一些潜在的应用和使用场景:

  • 比较价格:旅行者经常使用价格比较网站来找到最优惠的交易。抓取Expedia数据可以用来创建一个价格比较工具,帮助用户找到航班、酒店和度假套餐的最低价格。
  • 市场研究:Expedia的数据可以用于分析旅行趋势、定价策略和客户偏好。这些信息对希望进入或优化其在旅游行业中的业务的企业非常有价值。
  • 竞争对手分析:通过抓取Expedia数据,酒店可以了解竞争对手在定价、可用性和设施方面的提供情况。这可以帮助他们做出明智的决策并保持竞争力。
  • 客户评论分析:抓取Expedia上的评论和评分可以为酒店、航空公司和其他旅游服务提供宝贵的反馈。这些数据可以帮助企业评估其表现并进行改进。
  • 预测分析:从Expedia抓取的数据可以用来构建旅行需求、定价趋势和季节性变化的预测模型。这可以帮助企业优化其运营和营销策略。

常见问题

从Expedia抓取数据用于个人或商业用途是否合法?

由于Expedia上的数据是公开的(对所有人可访问),如果你没有登录账号进行抓取,这是完全合法的。但如果你创建了一个账号并在抓取时使用它,你就需要遵守Expedia的服务条款,其中禁止“使用任何机器人、蜘蛛、抓取器或其他自动化手段或任何手动过程来访问、监控或复制我们服务上的任何内容”。因此,建议你的脚本只访问网站的公开部分。

在从Expedia抓取数据时,对请求数量或并发连接数有限制吗?

没有公开信息表明有任何限制,但大多数网站在收到大量请求(尤其是并发请求)时会采取行动。因此,如果你计划发出大量请求,使用代理是很重要的。这将隐藏大规模抓取的事实,并帮助保护你的IP地址不被列入黑名单。阅读我们上面的Expedia抓取示例,了解如何在脚本中添加代理。

我可以使用代理IP从Expedia抓取酒店价格和可用性吗?

是的,无论你计划抓取多少数据,使用代理IP来抓取Expedia的数据都是可能的,甚至是建议的。它有助于保护你的IP地址不因抓取而被禁止访问网站。


结    论

通过使用常见的网页抓取工具,如Python和Playwright,你可以轻松地从Expedia抓取酒店信息。同样,你还可以抓取单个酒店页面,以获取市场上更详细的数据。

当你增加抓取活动的深度时,开始使用代理IP来保护你的IP地址免受检测和封禁是非常重要的。为了解决这个问题,一个常见的解决方案是找一个可靠的住宅代理提供商,它们将提供一个可以在请求之间轮换的代理池。

Written by 河小马

河小马是一位杰出的数字营销行业领袖,广告中国论坛的重要成员,其专业技能涵盖了PPC广告、域名停放、网站开发、联盟营销以及跨境电商咨询等多个领域。作为一位资深程序开发者,他不仅具备强大的技术能力,而且在出海网络营销方面拥有超过13年的经验。