Google Maps Examples
This page shows six interactions with Google Maps — visit, search, click a link or result, scroll, zoom, and toggle a layer — implemented six ways: two browser-automation tools (Selenium and Playwright) times three languages (JavaScript, Python, Rust).
Two things to know before you start
The six interactions
Every example below implements the same six steps. They are defined once here rather than repeated in each code sample:
- Visit — navigate to
https://www.google.com/maps. - Search — locate the search box (has
aria-label="Search Google Maps"in current Google Maps markup — note this may drift over time like any third-party site) and type a query, e.g."Cardiff Castle", then submit (Enter, or the search button, which typically hasaria-label="Search"). - Click a link/result — click the first search result in the results panel (results are normally DOM list items, not canvas — findable by role or text even though the map itself is canvas).
- Scroll — simulate a scroll/pan over the map canvas: for tools with a real mouse-wheel/scroll API, dispatch a wheel event or scroll gesture over the canvas element's bounding box, not over the whole page.
- Zoom — click the zoom-in button (
aria-label="Zoom in") or zoom-out button (aria-label="Zoom out") — a much more reliable approach than trying to pinch or scroll-zoom the canvas programmatically, and worth calling out as the pragmatic choice. - Activate a layer — open the Layers panel (button
aria-label="Layers"), then choose a layer such as "Traffic" (typically a labelled option or button once the panel is open — described below as "the traffic layer toggle" without overclaiming an exact stable selector).
Selenium
All three languages below use aria-label-based locators throughout, per the lesson above. The Rust example uses thirtyfour, the de facto Selenium/WebDriver client for Rust — there is no official one, and its name nods to selenium's atomic number, 34.
Selenium + JavaScript
const { Builder, By, Key, until } = require('selenium-webdriver');
(async function googleMapsWalkthrough() {
const driver = await new Builder().forBrowser('chrome').build();
try {
// 1. Visit
await driver.get('https://www.google.com/maps');
// 2. Search
const searchBox = await driver.findElement(By.css('[aria-label="Search Google Maps"]'));
await searchBox.sendKeys('Cardiff Castle', Key.RETURN);
// 3. Click the first result
const firstResult = await driver.wait(
until.elementLocated(By.css('[role="feed"] a')),
10000
);
await firstResult.click();
// 4. Scroll: dispatch a synthetic wheel event over the map canvas.
// (selenium-webdriver's newer driver.actions().scroll() API works
// too; this executeScript fallback is shown because it works across
// more selenium-webdriver versions.)
const canvas = await driver.findElement(By.css('canvas'));
await driver.executeScript((el) => {
const rect = el.getBoundingClientRect();
el.dispatchEvent(
new WheelEvent('wheel', {
deltaY: 200,
clientX: rect.x + rect.width / 2,
clientY: rect.y + rect.height / 2,
bubbles: true
})
);
}, canvas);
// 5. Zoom in
const zoomIn = await driver.findElement(By.css('[aria-label="Zoom in"]'));
await zoomIn.click();
// 6. Toggle the traffic layer
const layersButton = await driver.findElement(By.css('[aria-label="Layers"]'));
await layersButton.click();
const trafficOption = await driver.wait(
until.elementLocated(By.xpath("//*[contains(text(), 'Traffic')]")),
5000
);
await trafficOption.click();
} finally {
await driver.quit();
}
})();
Selenium + Python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
try:
# 1. Visit
driver.get('https://www.google.com/maps')
# 2. Search
search_box = driver.find_element(By.CSS_SELECTOR, '[aria-label="Search Google Maps"]')
search_box.send_keys('Cardiff Castle')
search_box.send_keys(Keys.RETURN)
# 3. Click the first result
first_result = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '[role="feed"] a'))
)
first_result.click()
# 4. Scroll: dispatch a synthetic wheel event over the map canvas,
# mirroring the JavaScript example's executeScript fallback above.
# (ActionChains.scroll_to_element / scroll_by_amount is a real
# alternative on recent selenium versions.)
canvas = driver.find_element(By.CSS_SELECTOR, 'canvas')
driver.execute_script(
"""
const rect = arguments[0].getBoundingClientRect();
arguments[0].dispatchEvent(new WheelEvent('wheel', {
deltaY: 200,
clientX: rect.x + rect.width / 2,
clientY: rect.y + rect.height / 2,
bubbles: true
}));
""",
canvas
)
# 5. Zoom in
driver.find_element(By.CSS_SELECTOR, '[aria-label="Zoom in"]').click()
# 6. Toggle the traffic layer
driver.find_element(By.CSS_SELECTOR, '[aria-label="Layers"]').click()
traffic_option = WebDriverWait(driver, 5).until(
EC.presence_of_element_located((By.XPATH, "//*[contains(text(), 'Traffic')]"))
)
traffic_option.click()
finally:
driver.quit()
Selenium + Rust
use thirtyfour::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let caps = DesiredCapabilities::chrome();
let driver = WebDriver::new("http://localhost:9515", caps).await?;
// 1. Visit
driver.goto("https://www.google.com/maps").await?;
// 2. Search
let search_box = driver
.query(By::Css("[aria-label='Search Google Maps']"))
.desc("Maps search box")
.single()
.await?;
search_box.send_keys("Cardiff Castle\u{E007}").await?;
// 3. Click the first result. Results are ordinary DOM list items, not
// canvas, so they are findable like any other element.
let first_result = driver
.query(By::Css("[role='feed'] a"))
.desc("first search result")
.single()
.await?;
first_result.click().await?;
// 4. Scroll: dispatch a synthetic wheel event over the map canvas.
// thirtyfour has no dedicated scroll gesture, so this mirrors the
// execute-script fallback used in the JavaScript and Python examples.
driver
.execute(
r#"const el = document.querySelector('canvas');
const rect = el.getBoundingClientRect();
el.dispatchEvent(new WheelEvent('wheel', {
deltaY: 200,
clientX: rect.x + rect.width / 2,
clientY: rect.y + rect.height / 2,
bubbles: true
}));"#,
Vec::new(),
)
.await?;
// 5. Zoom in
let zoom_in = driver
.query(By::Css("[aria-label='Zoom in']"))
.desc("Zoom in button")
.single()
.await?;
zoom_in.click().await?;
// 6. Toggle the traffic layer
let layers_button = driver
.query(By::Css("[aria-label='Layers']"))
.desc("Layers button")
.single()
.await?;
layers_button.click().await?;
let traffic_option = driver
.query(By::XPath("//*[contains(text(), 'Traffic')]"))
.desc("traffic layer toggle")
.single()
.await?;
traffic_option.click().await?;
driver.quit().await?;
Ok(())
}
Playwright
Playwright ships official bindings for JavaScript, Python, .NET, and Java. Rust is community-maintained: playwright-rs (actively developed by padamson/playwright-rust, pre-1.0) is the example below. The older octaltree/playwright-rust crate, published on crates.io as playwright, has been abandoned since 2022.
Playwright + JavaScript
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
// 1. Visit
await page.goto('https://www.google.com/maps');
// 2. Search
await page.fill('[aria-label="Search Google Maps"]', 'Cardiff Castle');
await page.keyboard.press('Enter');
// 3. Click the first result
await page.locator('[role="feed"] a').first().click();
// 4. Scroll: Playwright has a real, clean API for this — hover the
// canvas, then dispatch a mouse-wheel gesture over it. Far more
// ergonomic than the executeScript-wheel-event fallback the other
// tools needed.
await page.hover('canvas');
await page.mouse.wheel(0, 200);
// 5. Zoom in
await page.click('[aria-label="Zoom in"]');
// 6. Toggle the traffic layer
await page.click('[aria-label="Layers"]');
await page.click('text=Traffic');
await browser.close();
})();
Playwright + Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
# 1. Visit
page.goto('https://www.google.com/maps')
# 2. Search
page.fill('[aria-label="Search Google Maps"]', 'Cardiff Castle')
page.keyboard.press('Enter')
# 3. Click the first result
page.locator('[role="feed"] a').first.click()
# 4. Scroll: hover the canvas, then dispatch a mouse-wheel gesture —
# Playwright's real advantage here over the other tools' fallbacks.
page.hover('canvas')
page.mouse.wheel(0, 200)
# 5. Zoom in
page.click('[aria-label="Zoom in"]')
# 6. Toggle the traffic layer
page.click('[aria-label="Layers"]')
page.click('text=Traffic')
browser.close()
Playwright + Rust
use playwright_rs::Playwright;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pw = Playwright::launch().await?;
let browser = pw.chromium().launch().await?;
let page = browser.new_page().await?;
// 1. Visit
page.goto("https://www.google.com/maps", None).await?;
// 2. Search, then submit
let search_box = page.locator("[aria-label='Search Google Maps']");
search_box.fill("Cardiff Castle", None).await?;
search_box.press("Enter", None).await?;
// 3. Click the first result
let first_result = page.locator("[role='feed'] a");
first_result.click(None).await?;
// 4. Scroll: omitted here. playwright-rs is pre-1.0 and, unlike the
// JavaScript and Python bindings above, does not yet expose an
// ergonomic mouse-wheel API — see the JavaScript example for the
// pattern (hover the canvas, then page.mouse.wheel) this crate is
// expected to grow into.
// 5. Zoom in
let zoom_in = page.locator("[aria-label='Zoom in']");
zoom_in.click(None).await?;
// 6. Toggle the traffic layer
let layers_button = page.locator("[aria-label='Layers']");
layers_button.click(None).await?;
let traffic_option = page.locator("text=Traffic");
traffic_option.click(None).await?;
browser.close().await?;
Ok(())
}