Scrape Google Maps place details β rating, review count, address, phone, hours, coordinates, and more β without an API key.
Built with Playwright (Firefox) for reliable rendering and asyncio for high-throughput batch processing.
- π Scrape place details from any Google Maps URL or search query
- β Extract 20+ fields β rating, review count, address, phone, website, hours, coordinates, category, and more
- π Async batch processing β configurable concurrency for scraping thousands of URLs
- πΎ Crash recovery β auto-save with resume support; pick up where you left off
- π Multi-language β supports any Google Maps locale (
en,ja,zh-TW,ko, ...) - π Smart search handling β auto-clicks the first search result when a query returns multiple matches
- π€ Headless-ready β runs perfectly in CI/CD and headless environments
- π¦ CLI + Python API β use from the command line or import as a library
pip install google-maps-scraper
playwright install firefoxNote: If running on a server without GUI, use
playwright install firefox --with-depsto install browser dependencies.
For better anti-detection, install playwright-stealth:
pip install google-maps-scraper[stealth]# Scrape a single place
gmaps-scraper scrape "https://www.google.com/maps/search/?api=1&query=Eiffel+Tower"
# Scrape with language setting
gmaps-scraper scrape "https://www.google.com/maps/search/?api=1&query=ζ±δΊ¬γΏγ―γΌ" --lang ja
# Batch scrape from CSV
gmaps-scraper batch urls.csv -o results.json --concurrency 5
# Batch scrape to CSV
gmaps-scraper batch urls.csv -o results.csv --lang zh-TW --concurrency 3import asyncio
from gmaps_scraper import GoogleMapsScraper, ScrapeConfig
async def main():
config = ScrapeConfig(language="en", headless=True)
async with GoogleMapsScraper(config) as scraper:
result = await scraper.scrape(
"https://www.google.com/maps/search/?api=1&query=Machu+Picchu"
)
if result.success:
print(f"Name: {result.place.name}")
print(f"Rating: {result.place.rating}")
print(f"Reviews: {result.place.review_count}")
print(f"Address: {result.place.address}")
asyncio.run(main())from gmaps_scraper import scrape_place
result = scrape_place("https://www.google.com/maps/search/?api=1&query=Colosseum")
print(result.place.name, result.place.rating)import asyncio
from gmaps_scraper import scrape_batch, ScrapeConfig
async def main():
urls = open("urls.txt").read().splitlines()
config = ScrapeConfig(
concurrency=5,
delay_min=1.0,
delay_max=3.0,
headless=True,
save_interval=50,
)
results = await scrape_batch(
urls=urls,
config=config,
output_path="results.json",
resume=True, # Skip already-scraped URLs on restart
)
success = sum(1 for r in results if r.success)
print(f"Done: {success}/{len(results)} succeeded")
asyncio.run(main())Scrape a single Google Maps URL and output JSON.
| Option | Default | Description |
|---|---|---|
--lang |
β | Language code (e.g., en, ja, zh-TW) |
--no-headless |
β | Show the browser window (for debugging) |
-v, --verbose |
β | Enable debug logging |
Batch scrape URLs from a file. Output format is inferred from file extension (.json or .csv).
| Option | Default | Description |
|---|---|---|
-o, --output |
required | Output file path (.json or .csv) |
--concurrency |
5 |
Parallel browser tabs |
--lang |
β | Language code |
--proxy |
β | Proxy server URL (e.g., http://proxy:8080) |
--delay-min |
2.0 |
Min delay between requests (seconds) |
--delay-max |
5.0 |
Max delay between requests (seconds) |
--no-resume |
β | Start fresh, don't resume from existing output |
--save-interval |
50 |
Auto-save every N results |
CSV β the scraper looks for a column named url, URL, or link:
url,name
https://www.google.com/maps/search/?api=1&query=Eiffel+Tower,Eiffel Tower
https://www.google.com/maps/search/?api=1&query=Colosseum,ColosseumText β one URL per line:
https://www.google.com/maps/search/?api=1&query=Eiffel+Tower
https://www.google.com/maps/search/?api=1&query=Colosseum
[
{
"input_url": "https://www.google.com/maps/search/?api=1&query=Eiffel+Tower",
"success": true,
"place": {
"name": "Eiffel Tower",
"rating": 4.7,
"review_count": 344856,
"address": "Av. Gustave Eiffel, 75007 Paris, France",
"phone": "+33 8 92 70 12 39",
"website": "https://www.toureiffel.paris/",
"category": "Historical landmark",
"latitude": 48.8583701,
"longitude": 2.2944813,
"hours": ["Monday 09:30β23:45", "..."],
"google_maps_url": "https://www.google.com/maps/place/...",
"image_url": "https://lh3.googleusercontent.com/gps-cs-s/...",
"permanently_closed": false
},
"scraped_at": "2025-03-06T12:00:00"
}
]Flat structure with all place fields as columns. Ideal for data analysis.
| Field | Type | Description |
|---|---|---|
name |
str |
Place name |
rating |
float |
Star rating (1.0β5.0) |
review_count |
int |
Total number of reviews |
address |
str |
Full address |
phone |
str |
Phone number |
website |
str |
Website URL |
category |
str |
Place category (e.g., "Restaurant") |
hours |
list[str] |
Opening hours per day |
latitude |
float |
Latitude coordinate |
longitude |
float |
Longitude coordinate |
plus_code |
str |
Google Plus Code |
place_id |
str |
Google Maps Place ID |
url |
str |
Canonical Google Maps URL |
google_maps_url |
str |
Direct Google Maps link |
price_level |
str |
Price level indicator |
image_url |
str |
Main image URL |
description |
str |
Place description |
photos_count |
int |
Number of photos |
permanently_closed |
bool |
Whether permanently closed |
temporarily_closed |
bool |
Whether temporarily closed |
| Concurrency | Est. Throughput | Time for 10K URLs | Notes |
|---|---|---|---|
| 3 | ~1,200/hr | ~8.3 hrs | Conservative, stable |
| 5 | ~2,000/hr | ~5.0 hrs | Default |
| 10 | ~4,000/hr | ~2.5 hrs | Recommended with proxy |
Tips:
- Use
--proxywith rotating proxies for higher concurrency - The scraper auto-saves progress; if interrupted, just re-run and it will resume
- For large batches in CI (e.g., GitHub Actions with 6-hour limit), split into chunks
git clone https://github.com/noworneverev/google-maps-scraper.git
cd google-maps-scraper
pip install -e ".[dev]"
playwright install firefox
pytest tests/ -vThis tool is provided for educational and research purposes only. By using this software, you acknowledge and agree that:
- Google Maps Terms of Service: Web scraping may violate Google Maps' Terms of Service. You are solely responsible for ensuring your use complies with all applicable terms, laws, and regulations.
- No Warranty: This software is provided "as is", without warranty of any kind. The authors are not responsible for any consequences arising from the use of this tool.
- Rate Limiting: Excessive or aggressive scraping may result in your IP being temporarily or permanently blocked by Google. Use appropriate delays and concurrency settings.
- Data Privacy: Respect the privacy of individuals whose reviews or information may be collected. Handle all scraped data in accordance with applicable privacy laws (e.g., GDPR, CCPA).
- Personal Responsibility: The user assumes all responsibility for how the tool is used and the data it collects.
The authors and contributors of this project do not endorse or encourage any misuse of this software.
MIT Β© Yan-Ying Liao