Regional Preferences

Language

Nothing in the list matches what you typed there.

Scraping basics · 2026-06-13 · Updated

Scrape Google Maps listings with Python and Playwright

Google Maps builds its pages with JavaScript, so this beginner walkthrough drives a real browser with Playwright to read public business names, ratings and links.

In plain English: Google Maps is a common first stop for anyone looking up a local business, and each listing shows a name, a star rating and a link. Say you're putting together a list of cafés for a neighbourhood guide, or you run a small shop and want to see how nearby businesses are rated. Copying that by hand gets old quickly. The catch is that Maps builds its results with JavaScript, code that runs in the browser after the page arrives, so a plain page download gives you almost nothing. The answer is Playwright, a Python library that drives a real browser for you: it opens Maps, scrolls the results and reads each listing. A proxy places that browser in the city you're researching. For anything bigger than a personal project, Google's official Places API is the better route, and we'll come back to that.

Quick start in 3 steps

  1. Install Playwright and its browser with the two commands in Step 1.
  2. Fill in the PROXY block in Step 2 with your own details.
  3. Run the finished script from Step 6 with your own search and open the maps.csv file it saves.

Is this allowed? Know the limits

Before you write any code, read Google's robots.txt file and its Terms of Service. Collect business information only, never personal details about individuals, and don't try to get around CAPTCHAs or sign-in pages. Keep the pace slow with real pauses, and save each run's results rather than repeating the same search. Google also offers the Places API for approved, structured access to this kind of data. For production or commercial use, look at that first. Reading the public Maps pages suits small, personal research where the API isn't a good match.

Why Playwright, and which proxy?

Playwright can drive Chromium, Firefox or WebKit, and it waits sensibly for content to appear. That makes it a far better fit than plain requests for a page like Maps, which keeps changing as you scroll. Google also shapes results by location and looks out for automation, so pushing every search through one address soon brings CAPTCHAs. A residential proxy uses a household broadband address, and choosing one in the city you're searching makes the results more local. For longer sessions, our residential premium option adds steadiness. The proxies page compares every type, and our page on proxies for SEO covers local search in more depth.

What you need

  • A computer with Python 3 and room for a browser download.
  • Your proxy's host, port, username and password.
  • One search, written the way you'd type it into Maps, such as a type of business plus a town.

Step 1: Install Playwright

The first command installs the library. The second downloads a copy of Chromium, the open-source browser that Chrome is built on, for Playwright to control:

pip install playwright
playwright install chromium

Step 2: Start a browser that uses your proxy

Playwright accepts proxy settings directly, login included, so our host:port details drop straight in. Headless means the browser runs without a visible window. The locale and window size are set to ordinary values:

from playwright.sync_api import sync_playwright

PROXY = {
    "server": "http://HOST:PORT",
    "username": "USER",
    "password": "PASS",
}

def launch(playwright):
    browser = playwright.chromium.launch(
        headless=True,
        proxy=PROXY,
    )
    context = browser.new_context(
        locale="en-US",
        viewport={"width": 1280, "height": 900},
    )
    return browser, context

Step 3: Run the search

Your search words go straight into the Maps search address, with spaces turned into plus signs. After the page opens, the function waits for the results list (the "feed") to appear before doing anything else:

def open_search(context, query):
    page = context.new_page()
    url = f"https://www.google.com/maps/search/{query.replace(' ', '+')}"
    page.goto(url, wait_until="domcontentloaded")
    page.wait_for_selector('div[role="feed"]', timeout=30000)
    return page

Step 4: Scroll until every listing has loaded

Maps adds results in batches as you scroll down the side panel, which is called lazy loading. So the script scrolls the list to the bottom, pauses for 2 seconds while new listings arrive, and measures the list's height. When the height stops growing, the end has been reached. It gives up after 15 rounds at most:

import time

def scroll_results(page, max_rounds=15):
    feed = page.query_selector('div[role="feed"]')
    last_height = 0
    for _ in range(max_rounds):
        page.evaluate(
            "(el) => el.scrollTo(0, el.scrollHeight)", feed
        )
        time.sleep(2)  # let new results load
        new_height = page.evaluate("(el) => el.scrollHeight", feed)
        if new_height == last_height:
            break
        last_height = new_height

Step 5: Read the details of each listing

Each result is a link inside the results list. The business name comes from the link's aria-label, a text label that websites add for screen readers, the tools that read pages aloud for blind and partially sighted people. The star rating is read from a small image label on the same card. Google's page layout changes now and then, so the code checks each field and skips any listing that's incomplete:

def extract_listings(page):
    results = []
    cards = page.query_selector_all('div[role="feed"] a[href*="/maps/place/"]')
    for card in cards:
        name = card.get_attribute("aria-label")
        if not name:
            continue
        parent = card.evaluate_handle("el => el.parentElement")
        rating_el = parent.query_selector('span[role="img"]')
        rating = (
            rating_el.get_attribute("aria-label") if rating_el else None
        )
        results.append({
            "name": name,
            "rating": rating,
            "url": card.get_attribute("href"),
        })
    return results

Step 6: Put it all together

This last part opens the browser, runs the search, scrolls, reads the listings, closes the browser and writes a CSV file you can open in any spreadsheet app. Change the search text to your own:

import csv

def scrape(query):
    with sync_playwright() as pw:
        browser, context = launch(pw)
        page = open_search(context, query)
        scroll_results(page)
        data = extract_listings(page)
        browser.close()
    return data

if __name__ == "__main__":
    rows = scrape("coffee shops in Austin")
    with open("maps.csv", "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=["name", "rating", "url"])
        writer.writeheader()
        writer.writerows(rows)
    print(f"Saved {len(rows)} listings")

Tips for tidy, reliable runs

  • Set the proxy location to the place you're searching, so the ratings and the order of results match what people there see.
  • Vary your pauses a little rather than using exactly the same wait every time, because some pages load more slowly than others.
  • For several searches, switch residential addresses between them, and check the address you're on with our my IP tool.
  • If a CAPTCHA appears, stop. It means you're going too fast, so take a long break and come back later at a slower pace.
  • Check that the proxy is working before each run with the proxy checker.

Common mistakes

  • Running one search after another with no break. Even a careful script looks like a flood if it never rests.
  • Expecting phone numbers and opening hours. This script reads the name, the rating and the link. Other details live on each business's own panel, and the Places API is the cleaner way to get them.
  • Using a proxy in the wrong place. A proxy in another city gives you that city's view of "near me" results.
  • Giving up after one empty run. A slow connection can stop the scrolling early. Try once more with a slightly longer pause, then leave it.

Beginner questions

Why did I get fewer results than in my own browser?

The list loads in batches, and the loop ends when it stops growing. On a slow connection a batch may not arrive within the pause, so the script thinks it has reached the end. A slightly longer pause usually helps.

Do I need to see the browser to check it's working?

No, but it can help while you learn. Change headless=True to headless=False in Step 2 and a browser window opens so you can watch each step. Switch it back afterwards.

Is the Places API hard to start with?

It takes a little set-up in the Google Cloud Console, much like any Google API, but it hands back structured data without any scrolling or page reading. For anything beyond a small personal project, it's the route to take.

Playwright handles the moving page, and a residential proxy handles the location. With both in place, you can build a small Google Maps research tool in an afternoon. Run it rarely, stay within Google's terms and rate limits, and move to the official Places API when the project grows.

Keep reading

Ready to try one yourself?

Start small: buy a little data in the type that suits your task, check it with our free tools, and top up only if you need more. There’s no contract and no minimum spend.

View our proxies

New to proxies? Start with a small order

Datacenter proxies cost $2.49 per gigabyte (GB) of data you use, so trying one on the site you care about needn’t cost much. Your login details are ready within seconds, and if you get stuck, a real person answers questions 24/7.

Budget Proxies logo

or
Continue with Email

Continue and you're agreeing to our Terms and Privacy Policy.

Already set up with us?

First time with us?

🎉 Money-off code

Knock 10% off
your next order

Type your email below and we’ll send the code over. It works on any proxy plan, however small the order. At checkout, paste it into the Promo code box and press Apply.

You’ll get the code and, now and then, an offer by email. Changed your mind later? Ask us and we’ll take you off the list.

Sorted! It’s on its way 📬

Your 10% code is heading to your inbox. No need to wait for the email, though: copy it from here and paste it in when you check out.

See residential plans →