Skip to content

bright-data-de/Scraping-hotels-google-travel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 

Repository files navigation

Hotels aus Google Travel scrapen

Promo

Dieser Leitfaden erklärt, wie Sie Hotellistings, Preise und Ausstattungsmerkmale aus Google Travel entweder mit Selenium-Methoden oder mit den APIs von Bright Data erfassen.

Prerequisites

Um Reisedaten zu scrapen, benötigen Sie Python sowie entweder Selenium-, Requests- oder AIOHTTP-Module. Mit Selenium scrapen Sie Hotelinformationen direkt aus Google Travel. Mit Requests und AIOHTTP verwenden Sie die Booking.com API von Bright Data.

Wenn Sie Selenium verwenden, stellen Sie sicher, dass Sie webdriver installiert haben. Wenn Sie mit Selenium nicht vertraut sind, sehen Sie sich diesen Leitfaden an, um sich schnell einzuarbeiten.

Selenium installieren:

pip install selenium

Requests installieren:

pip install requests

AIOHTTP installieren:

pip install aiohttp

What To Extract From Google Travel

Alle Hotelergebnisse sind in ein benutzerdefiniertes c-wiz-Element von Google Travel eingebettet.

Inspect c-wiz Element

Allerdings gibt es viele c-wiz-Elemente auf der Seite. Jede Hotelkarte enthält ein a-Element, das direkt von einem div und diesem c-wiz-Element abstammt. Wir können einen CSS-Selektor schreiben, um alle a-Tags zu finden, die von diesen Elementen abstammen: c-wiz > div > a.

Inspect a Element

Der Name des Listings ist in einem h2 eingebettet.

Inspect h2 Element

Unser Preis ist in einem span eingebettet.

Inspect Price Element

Unsere Ausstattungsmerkmale sind in li- (Listen-)Elemente eingebettet.

Inspect Amenities

Nachdem wir eine Hotelkarte gefunden haben, können wir alle oben genannten Daten daraus extrahieren.

Extracting The Data With Selenium

Das Extrahieren dieser Daten mit Selenium ist relativ unkompliziert, sobald Sie wissen, wonach Sie suchen müssen. Allerdings lädt Google Travel die Ergebnisse dynamisch, was es zu einem heiklen Prozess macht, der durch vorkonfigurierte Waits, Mausklicks und benutzerdefinierte Fenster zusammengehalten wird.

Hier ist das vollständige Python-Script:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import json
from time import sleep

OPTIONS = webdriver.ChromeOptions()
OPTIONS.add_argument("--headless")
OPTIONS.add_argument("--window-size=1920,1080")



def scrape_hotels(location, pages=5):
    driver = webdriver.Chrome(options=OPTIONS)
    actions = ActionChains(driver)
    url = f"https://www.google.com/travel/search?q={location}"
    driver.get(url)
    done = False

    found_hotels = []
    page = 1
    result_number = 1
    while page <= pages:
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        sleep(5)
        hotel_links = driver.find_elements(By.CSS_SELECTOR, "c-wiz > div > a")
        print(f"-----------------PAGE {page}------------------")
        print("FOUND ITEMS: ", len(hotel_links))
        for hotel_link in hotel_links:
            hotel_card = hotel_link.find_element(By.XPATH, "..")
            try:
                info = {}
                info["url"] = hotel_link.get_attribute("href")
                info["rating"] = 0.0
                info["price"] = "n/a"
                info["name"] = hotel_card.find_element(By.CSS_SELECTOR, "h2").text
                price_holder = hotel_card.find_elements(By.CSS_SELECTOR, "span")
                info["amenities"] = []
                amenities_holders = hotel_card.find_elements(By.CSS_SELECTOR, "li")
                for amenity in amenities_holders:
                    info["amenities"].append(amenity.text)
                if "DEAL" in price_holder[0].text or "PRICE" in price_holder[0].text:
                    if price_holder[1].text[0] == "$":
                        info["price"] = price_holder[1].text
                else:
                    info["price"] = price_holder[0].text
                rating_holder = hotel_card.find_elements(By.CSS_SELECTOR, "span[role='img']")
                if rating_holder:
                    info["rating"] = float(rating_holder[0].get_attribute("aria-label").split(" ")[0])
                info["result_number"] = result_number
                
                if info not in found_hotels:
                    found_hotels.append(info)
                result_number+=1
                
            except:
                continue
        print("Scraped Total:", len(found_hotels))
        
        next_button = driver.find_elements(By.XPATH, "//span[text()='Next']")
        if next_button:
            print("next button found!")
            sleep(1)
            actions.move_to_element(next_button[0]).click().perform()
            page+=1
            sleep(5)
        else:
            done = True

    driver.quit()

    with open("scraped-hotels.json", "w") as file:
        json.dump(found_hotels, file, indent=4)

if __name__ == "__main__":
    PAGES = 2
    scrape_hotels("miami", pages=PAGES)

Sehen wir uns Schritt für Schritt an, was das Script macht:

  1. Zuerst erstellen wir eine Instanz von ChromeOptions. Diese verwenden wir, um unsere Argumente --headless und --window-size=1920,1080 hinzuzufügen.

Hinweis
Ohne die benutzerdefinierte Fenstergröße würden die Ergebnisse nicht korrekt geladen werden, und wir würden am Ende immer wieder dieselben Ergebnisse scrapen.

  1. Wenn wir den Browser starten, verwenden wir das Keyword-Argument options=OPTIONS. Dadurch wird Chrome mit unseren benutzerdefinierten Optionen gestartet.

  2. ActionChains(driver) gibt uns eine ActionChains-Instanz. Diese verwenden wir später in unserem Script, um den Cursor zur Schaltfläche Next zu bewegen und dann darauf zu klicken.

  3. Wir verwenden eine while-Schleife, um unsere Laufzeit zu begrenzen. Sobald das Scraping abgeschlossen ist, verlassen wir diese Schleife.

  4. hotel_links = driver.find_elements(By.CSS_SELECTOR, "c-wiz > div > a") gibt uns alle Hotellinks auf der Seite. Ihre übergeordneten Elemente finden wir über ihren xpath: hotel_card = hotel_link.find_element(By.XPATH, "..").

  5. Wir gehen durch und extrahieren alle einzelnen Datenbestandteile, die wir zuvor betrachtet haben:

    • url: hotel_link.get_attribute("href")
    • name: hotel_card.find_element(By.CSS_SELECTOR, "h2").text
    • Bei der Preissuche gibt es manchmal zusätzliche Elemente in der Karte wie DEAL und GREAT PRICE. Um sicherzustellen, dass wir immer den richtigen Preis erhalten, extrahieren wir die span-Elemente in ein Array. Wenn das Array diese Wörter enthält, nehmen wir das zweite Element (price_holder[1].text) statt des ersten (price_holder[0].text)
    • Wir verwenden auch die Methode find_elements() bei der Suche nach der Bewertung. Wenn keine Bewertung vorhanden ist, geben wir ihr einen Standardwert von n/a.
    • hotel_card.find_elements(By.CSS_SELECTOR, "li") liefert unsere Ausstattungs-Container. Wir extrahieren jeden davon über sein text-Attribut.
  6. Wir setzen diese Schleife fort, bis wir alle gewünschten Seiten gescrapt haben. Sobald wir unsere Daten haben, setzen wir done auf True und verlassen die Schleife.

  7. Wir schließen den Browser und verwenden json.dump(), um alle gescrapten Daten in einer JSON-Datei zu speichern.

Extracting the Data With Bright Data’s Travel API

Wenn Sie nicht von einem Scraper abhängig sein oder sich mit Selektoren und Locator herumschlagen möchten, können Sie unsere travel data verwenden oder Hoteldaten mit unserer Booking.com API extrahieren. Zwei Methoden dafür sind das requests-Modul und die AIOHTTP-Bibliothek.

Requests

Der folgende Code richtet Sie für die Booking.com API ein. Geben Sie einfach Ihren API-Schlüssel, den Reisezielort, das Check-in-Datum und das Check-out-Datum ein. Zunächst sendet er eine Anfrage an die API, um die Daten zu generieren. Anschließend prüft er alle 10 Sekunden erneut, ob unser Report bereit ist. Sobald wir unsere Daten erhalten haben, speichern wir sie in einer JSON-Datei.

import requests
import json
import time


def get_bookings(api_key, location, dates):
    url = "https://api.brightdata.com/datasets/v3/trigger"

    #booking.com dataset
    dataset_id = "gd_m4bf7a917zfezv9d5"

    endpoint = f"{url}?dataset_id={dataset_id}&include_errors=true"
    auth_token = api_key

    #
    headers = {
        "Authorization": f"Bearer {auth_token}",
        "Content-Type": "application/json"
    }

    payload = [
        {
            "url": "https://www.booking.com",
            "location": location,
            "check_in": dates["check_in"],
            "check_out": dates["check_out"],
            "adults": 2,
            "rooms": 1
        }
    ]

    response = requests.post(endpoint, headers=headers, json=payload)

    if response.status_code == 200:
        print("Request successful. Response:")
        print(json.dumps(response.json(), indent=4))
        return response.json()["snapshot_id"]
    else:
        print(f"Error: {response.status_code}")
        print(response.text)

def poll_and_retrieve_snapshot(api_key, snapshot_id, output_file="snapshot-data.json"):
    #create the snapshot url
    snapshot_url = f"https://api.brightdata.com/datasets/v3/snapshot/{snapshot_id}?format=json"
    headers = {
        "Authorization": f"Bearer {api_key}"
    }

    print(f"Polling snapshot for ID: {snapshot_id}...")

    while True:
        response = requests.get(snapshot_url, headers=headers)
        
        if response.status_code == 200:
            print("Snapshot is ready. Downloading...")
            snapshot_data = response.json()
            #write the snapshot to a new json file
            with open(output_file, "w", encoding="utf-8") as file:
                json.dump(snapshot_data, file, indent=4)
            print(f"Snapshot saved to {output_file}")
            break
        elif response.status_code == 202:
            print("Snapshot is not ready yet. Retrying in 10 seconds...")
        else:
            print(f"Error: {response.status_code}")
            print(response.text)
            break
        
        time.sleep(10)


if __name__ == "__main__":
    
    API_KEY = "your-bright-data-api-key"
    LOCATION = "Miami"
    CHECK_IN = "2025-02-01T00:00:00.000Z"
    CHECK_OUT = "2025-02-02T00:00:00.000Z"
    DATES = {
        "check_in": CHECK_IN,
        "check_out": CHECK_OUT
    }
    snapshot_id = get_bookings(API_KEY, LOCATION, DATES)
    poll_and_retrieve_snapshot(API_KEY, snapshot_id)
  • get_bookings() nimmt Ihren API_KEY, LOCATION und DATES. Anschließend stellt es eine Anfrage für die Daten und gibt die snapshot_id zurück.
  • Die snapshot_id wird benötigt, um den Snapshot abzurufen.
  • Nachdem die snapshot_id generiert wurde, prüft poll_and_retrieve_snapshot() alle 10 Sekunden, ob die Daten bereit sind.
  • Sobald die Daten bereit sind, verwenden wir json.dump(), um sie in einer JSON-Datei zu speichern.

Wenn Sie den Code ausführen, sollten Sie in Ihrem Terminal etwas Ähnliches wie Folgendes sehen.

Request successful. Response:
{
    "snapshot_id": "s_m5moyblm1wikx4ntot"
}
Polling snapshot for ID: s_m5moyblm1wikx4ntot...
Snapshot is not ready yet. Retrying in 10 seconds...
Snapshot is not ready yet. Retrying in 10 seconds...
Snapshot is not ready yet. Retrying in 10 seconds...
Snapshot is not ready yet. Retrying in 10 seconds...
Snapshot is ready. Downloading...
Snapshot saved to snapshot-data.json

Anschließend erhalten Sie eine JSON-Datei voller Objekte wie diesem.

{
        "input": {
            "url": "https://www.booking.com",
            "location": "Miami",
            "check_in": "2025-02-01T00:00:00.000Z",
            "check_out": "2025-02-02T00:00:00.000Z",
            "adults": 2,
            "rooms": 1
        },
        "url": "https://www.booking.com/hotel/us/ramada-plaze-by-wyndham-marco-polo-beach-resort.html?checkin=2025-02-01&checkout=2025-02-02&group_adults=2&no_rooms=1&group_children=",
        "location": "Miami",
        "check_in": "2025-02-01T00:00:00.000Z",
        "check_out": "2025-02-02T00:00:00.000Z",
        "adults": 2,
        "children": null,
        "rooms": 1,
        "id": "55989",
        "title": "Ramada Plaza by Wyndham Marco Polo Beach Resort",
        "address": "19201 Collins Avenue",
        "city": "Sunny Isles Beach (Florida)",
        "review_score": 6.2,
        "review_count": "1788",
        "image": "https://cf.bstatic.com/xdata/images/hotel/square600/414501733.webp?k=4c14cb1ec5373f40ee83d901f2dc9611bb0df76490f3673f94dfaae8a39988d8&o=",
        "final_price": 217,
        "original_price": 217,
        "currency": "USD",
        "tax_description": null,
        "nb_livingrooms": 0,
        "nb_kitchens": 0,
        "nb_bedrooms": 0,
        "nb_all_beds": 2,
        "full_location": {
            "description": "This is the straight-line distance on the map. Actual travel distance may vary.",
            "main_distance": "11.4 miles from downtown",
            "display_location": "Miami Beach",
            "beach_distance": "Beachfront",
            "nearby_beach_names": []
        },
        "no_prepayment": false,
        "free_cancellation": true,
        "property_sustainability": {
            "is_sustainable": false,
            "level_id": "L0",
            "facilities": [
                "436",
                "490",
                "492",
                "496",
                "506"
            ]
        },
        "timestamp": "2025-01-07T16:43:24.954Z"
    },

AIOHTTP

Mit der AIOHTTP-Bibliothek kann dieser Prozess schneller werden, da wir mehrere Datensätze gleichzeitig triggern, pollen und herunterladen können. Der folgende Code baut auf den Konzepten aus dem Requests-Beispiel oben auf, verwendet jedoch stattdessen aiohttp.ClientSession(), um mehrere Anfragen asynchron zu stellen.

import aiohttp
import asyncio
import json


async def get_bookings(api_key, location, dates):
    url = "https://api.brightdata.com/datasets/v3/trigger"
    dataset_id = "gd_m4bf7a917zfezv9d5"
    endpoint = f"{url}?dataset_id={dataset_id}&include_errors=true"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    payload = [
        {
            "url": "https://www.booking.com",
            "location": location,
            "check_in": dates["check_in"],
            "check_out": dates["check_out"],
            "adults": 2,
            "rooms": 1
        }
    ]

    async with aiohttp.ClientSession(headers=headers) as session:
        async with session.post(endpoint, json=payload) as response:
            if response.status == 200:
                response_data = await response.json()
                print(f"Request successful for location: {location}. Response:")
                print(json.dumps(response_data, indent=4))
                return response_data["snapshot_id"]
            else:
                print(f"Error for location: {location}. Status: {response.status}")
                print(await response.text())
                return None


async def poll_and_retrieve_snapshot(api_key, snapshot_id, output_file):
    snapshot_url = f"https://api.brightdata.com/datasets/v3/snapshot/{snapshot_id}?format=json"
    headers = {
        "Authorization": f"Bearer {api_key}"
    }

    print(f"Polling snapshot for ID: {snapshot_id}...")

    async with aiohttp.ClientSession(headers=headers) as session:
        while True:
            async with session.get(snapshot_url) as response:
                if response.status == 200:
                    print(f"Snapshot for {output_file} is ready. Downloading...")
                    snapshot_data = await response.json()
                    # Save snapshot data to a file
                    with open(output_file, "w", encoding="utf-8") as file:
                        json.dump(snapshot_data, file, indent=4)
                    print(f"Snapshot saved to {output_file}")
                    break
                elif response.status == 202:
                    print(f"Snapshot for {output_file} is not ready yet. Retrying in 10 seconds...")
                else:
                    print(f"Error polling snapshot for {output_file}. Status: {response.status}")
                    print(await response.text())
                    break

            await asyncio.sleep(10)


async def process_location(api_key, location, dates):
    snapshot_id = await get_bookings(api_key, location, dates)
    if snapshot_id:
        output_file = f"snapshot-{location.replace(' ', '_').lower()}.json"
        await poll_and_retrieve_snapshot(api_key, snapshot_id, output_file)


async def main():
    api_key = "your-bright-data-api-key"
    locations = ["Miami", "Key West"]
    dates = {
        "check_in": "2025-02-01T00:00:00.000Z",
        "check_out": "2025-02-02T00:00:00.000Z"
    }

    # Process all locations in parallel
    tasks = [process_location(api_key, location, dates) for location in locations]
    await asyncio.gather(*tasks)


if __name__ == "__main__":
    asyncio.run(main())
  • Sowohl get_bookings() als auch poll_and_retrieve_snapshot() verwenden jetzt das Objekt aiohttp.ClientSession, um asynchrone Anfragen an den Server zu erstellen.
  • process_location() wird verwendet, um alle Daten für einen Standort zu verarbeiten.
  • main() ermöglicht es uns, process_location() für alle Standorte gleichzeitig aufzurufen.

Hier ist die Ausgabe:

Request successful for location: Miami. Response:
{
    "snapshot_id": "s_m5mtmtv62hwhlpyazw"
}
Request successful for location: Key West. Response:
{
    "snapshot_id": "s_m5mtmtv72gkkgxvdid"
}
Polling snapshot for ID: s_m5mtmtv62hwhlpyazw...
Polling snapshot for ID: s_m5mtmtv72gkkgxvdid...
Snapshot for snapshot-miami.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-miami.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-miami.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-miami.json is ready. Downloading...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot saved to snapshot-miami.json
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is not ready yet. Retrying in 10 seconds...
Snapshot for snapshot-key_west.json is ready. Downloading...
Snapshot saved to snapshot-key_west.json

Bright Data’s Alternative Solutions

Über die Web Scraper APIs hinaus stellt Bright Data einsatzbereite Datensätze bereit, die auf unterschiedliche Anforderungen zugeschnitten sind. Zu unseren gefragtesten Reisedatensätzen gehören:

Sie können zwischen vollständig verwalteten oder selbst verwalteten benutzerdefinierten Datensätzen wählen. So können Sie Daten von jeder öffentlichen Website extrahieren und sie exakt nach Ihren Spezifikationen anpassen.

About

Hotel-Daten aus Google Travel mit Selenium oder den APIs von Bright Data Scraping, mit praktischen Beispielen und Code Snippets

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors