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.
- Voraussetzungen
- Was aus Google Travel zu extrahieren ist
- Die Daten mit Selenium extrahieren
- Die Daten mit Bright Data’s Travel API extrahieren
- Alternative Lösungen von Bright Data
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 aiohttpAlle Hotelergebnisse sind in ein benutzerdefiniertes c-wiz-Element von Google Travel eingebettet.
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.
Der Name des Listings ist in einem h2 eingebettet.
Unser Preis ist in einem span eingebettet.
Unsere Ausstattungsmerkmale sind in li- (Listen-)Elemente eingebettet.
Nachdem wir eine Hotelkarte gefunden haben, können wir alle oben genannten Daten daraus extrahieren.
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:
- Zuerst erstellen wir eine Instanz von
ChromeOptions. Diese verwenden wir, um unsere Argumente--headlessund--window-size=1920,1080hinzuzufü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.
-
Wenn wir den Browser starten, verwenden wir das Keyword-Argument
options=OPTIONS. Dadurch wird Chrome mit unseren benutzerdefinierten Optionen gestartet. -
ActionChains(driver)gibt uns eineActionChains-Instanz. Diese verwenden wir später in unserem Script, um den Cursor zur SchaltflächeNextzu bewegen und dann darauf zu klicken. -
Wir verwenden eine
while-Schleife, um unsere Laufzeit zu begrenzen. Sobald das Scraping abgeschlossen ist, verlassen wir diese Schleife. -
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, ".."). -
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
DEALundGREAT PRICE. Um sicherzustellen, dass wir immer den richtigen Preis erhalten, extrahieren wir diespan-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 vonn/a. hotel_card.find_elements(By.CSS_SELECTOR, "li")liefert unsere Ausstattungs-Container. Wir extrahieren jeden davon über seintext-Attribut.
- url:
-
Wir setzen diese Schleife fort, bis wir alle gewünschten Seiten gescrapt haben. Sobald wir unsere Daten haben, setzen wir
doneaufTrueund verlassen die Schleife. -
Wir schließen den Browser und verwenden
json.dump(), um alle gescrapten Daten in einer JSON-Datei zu speichern.
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.
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 IhrenAPI_KEY,LOCATIONundDATES. Anschließend stellt es eine Anfrage für die Daten und gibt diesnapshot_idzurück.- Die
snapshot_idwird benötigt, um den Snapshot abzurufen. - Nachdem die
snapshot_idgeneriert wurde, prüftpoll_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"
},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 auchpoll_and_retrieve_snapshot()verwenden jetzt das Objektaiohttp.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
Ü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.





