Using Google Maps Photos API from SerpApi
Tags: webscraping, tutorial, python, programming
Published: 2022-12-28 23:48:18

- <a href="#intro">Intro</a>
- <a href="#what_will_be_scraped">What will be scraped</a>
- <a href="#why_use_api">Why using API?</a>
- <a href="#fullcode">Full Code</a>
- <a href="#preparation">Preparation</a>
- <a href="#code_explanation">Code Explanation</a>
- <a href="#output">Output</a>
- <a href="#links">Links</a>


<h2 id="intro">Intro</h2>

In this blog post, we'll go through the process of extracting data from Google Maps Place results using Python. You can [look at the complete code in the online IDE (Replit)](https://replit.com/@chukhraiartur/blog-google-maps-photos-api#main.py).

In order to successfully extract Google Maps Photos, you will need to pass the `data_id` parameter, this parameter is responsible for photos from a specific place. You can extract this parameter from local results. Have a look at the [Using Google Maps Local Results API from SerpApi](https://serpapi.com/blog/using-google-maps-local-results-from-serpapi) blog post, in which I described in detail how to extract all the needed data.

If you prefer video format, we have a dedicated video that shows how to do that: [Web Scraping all Google Maps Place Photos with Python and SerpApi](https://www.youtube.com/watch?v=XM9d8zTYp_U).


<h2 id="what_will_be_scraped">What will be scraped</h2>

![wwbs-google-maps-photos-api](https://user-images.githubusercontent.com/64033139/180219601-87a8971d-4ced-4ed5-841b-7e1a38383694.png)


<h2 id="why_use_api">Why using API?</h2>

There're a couple of reasons that may use API, ours in particular:

- No need to create a parser from scratch and maintain it.
- Bypass blocks from Google: solve CAPTCHA or solve IP blocks.
- Pay for proxies, and CAPTCHA solvers.
- Don't need to use browser automation.

SerpApi handles everything on the backend with fast response times under ~2.5 seconds (~1.2  seconds with Ludicrous speed) per request and without browser automation, which becomes much faster. Response times and status rates are shown under [SerpApi Status page](https://serpapi.com/status).

![serpapi-status](https://user-images.githubusercontent.com/81998012/206190150-29c779a8-9378-4526-a4ef-2941731487ab.png)


<h2 id="fullcode">Full Code</h2>

If you just need to extract all available data about the place, then we can create an empty `list` and then `append` extracted data to it:

```python
from serpapi import GoogleSearch
from urllib.parse import urlsplit, parse_qsl
import os, json

params = {
    # https://docs.python.org/3/library/os.html#os.getenv
    'api_key': os.getenv('API_KEY'),        # your serpapi api
    'engine': 'google_maps',                # SerpApi search engine	
    'q': 'Starbucks',                       # query
    'll': '@40.7455096,-74.0083012,15.1z',  # GPS coordinates
    'type': 'search',                       # list of results for the query
    'hl': 'en',                             # language
    'start': 0,                             # pagination
}

search = GoogleSearch(params)               # where data extraction happens on the backend
results = search.get_dict()                 # JSON -> Python dict

local_data_results = [
    (result['title'], result['data_id']) 
    for result in results['local_results']
]

photo_results = []

for title, data_id in local_data_results:
    params = {
        # https://docs.python.org/3/library/os.html#os.getenv
        'api_key': os.getenv('API_KEY'),    # your serpapi api
        'engine': 'google_maps_photos',     # SerpApi search engine
        'hl': 'en',                         # language
        'data_id': data_id                  # place result
    }

    search = GoogleSearch(params)
    
    photos = []

    # pagination
    while True:
        new_page_results = search.get_dict()
        
        if 'next' in new_page_results.get('serpapi_pagination', {}):
            search.params_dict.update(dict(parse_qsl(urlsplit(new_page_results.get('serpapi_pagination', {}).get('next')).query)))
        else:
            break

        photos.extend(new_page_results['photos'])
    
    photo_results.append({
        'title': title,
        'photos': photos
    })

print(json.dumps(photo_results, indent=2, ensure_ascii=False))
```


<h2 id="preparation">Preparation</h2>

Install library:

```lang-none
pip install google-search-results
```

[`google-search-results`](https://github.com/serpapi/google-search-results-python) is a SerpApi API package.


<h2 id="code_explanation">Code Explanation</h2>

Import libraries:

```python
from serpapi import GoogleSearch
from urllib.parse import urlsplit, parse_qsl
import os, json
```

| Library       | Purpose                                                            |
|---------------|--------------------------------------------------------------------|
| `GoogleSearch` | to scrape and parse Google results using [SerpApi](https://serpapi.com/) web scraping library. |
| [`urlsplit`](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlsplit)| this should generally be used instead of [urlparse()](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse) if the more recent URL syntax allowing parameters to be applied to each segment of the path portion of the URL (see [RFC 2396](https://datatracker.ietf.org/doc/html/rfc2396.html)) is wanted. |
| [`parse_qsl`](https://docs.python.org/3/library/urllib.parse.html#urllib.parse.parse_qsl)| to parse a query string given as a string argument. |
| [`os`](https://docs.python.org/3/library/os.html)| to return environment variable (SerpApi API key) value.  |
| [`json`](https://docs.python.org/3/library/json.html)| to convert extracted data to a JSON object. |

At the beginning of the code, you need to make the request in order to get local results. Then place results will be extracted from them.

The parameters are defined for generating the URL. If you want to pass other parameters to the URL, you can do so using the `params` dictionary:

```python
params = {
    # https://docs.python.org/3/library/os.html#os.getenv
    'api_key': os.getenv('API_KEY'),        # your serpapi api
    'engine': 'google_maps',                # SerpApi search engine	
    'q': 'Starbucks',                       # query
    'll': '@40.7455096,-74.0083012,15.1z',  # GPS coordinates
    'type': 'search',                       # list of results for the query
    'hl': 'en',                             # language
    'start': 0,                             # pagination
}
```

Then, we create a `search` object where the data is retrieved from the SerpApi backend. In the `results` dictionary we get data from JSON:

```python
search = GoogleSearch(params)     # where data extraction happens on the backend
results = search.get_dict()       # JSON -> Python dict
```

At the moment, the first 20 local results are stored in the `results` dictionary. If you are interested in all local results with pagination, then check out the [Using Google Maps Local Results API from SerpApi](https://serpapi.com/blog/using-google-maps-local-results-from-serpapi) blog post.

Data such as `title` and `data_id` are extracted from each local result. These data will be needed later:

```python
local_data_results = [
    (result['title'], result['data_id']) 
    for result in results['local_results']
]
```

Declaring the `photo_results` list where the extracted data will be added:

```python
photo_results = []
```

Next, you need to access each place's photos separately by iterating the `local_data_results` list:

```python
for title, data_id in local_data_results:
    # data extraction will be here
```

These parameters are defined for generating the URL for place results:

```python
params = {
    # https://docs.python.org/3/library/os.html#os.getenv
    'api_key': os.getenv('API_KEY'),    # your serpapi api
    'engine': 'google_maps_photos',     # SerpApi search engine
    'hl': 'en',                         # language
    'data_id': data_id                  # place result
}
```

|Parameters|Explanation|
|----------|-----------|
|[`api_key`](https://serpapi.com/google-maps-photos-api#api-parameters-serpapi-parameters-api-key)| Parameter defines the SerpApi private key to use.|
|[`engine`](https://serpapi.com/google-maps-photos-api#api-parameters-serpapi-parameters-enginee)| Set parameter to `google_maps_photos` to use the Google Maps Photos API engine.|
|[`hl`](https://serpapi.com/google-maps-photos-api#api-parameters-localization-hl)| Parameter defines the language to use for the Google Maps Photos search. It's a two-letter language code, for example, `en` for English (default), `es` for Spanish, or `fr` for French). Head to the [Google languages page](https://serpapi.com/google-languages) for a full list of supported Google languages.|
|[`data_id`](https://serpapi.com/google-maps-photos-api#api-parameters-search-query-data-id)| Parameter defines the Google Maps data ID. Find the data ID of a place using our [Google Maps API](https://serpapi.com/google-maps-api).|

Then, we create a `search` object where the data is retrieved from the SerpApi backend:

```python
search = GoogleSearch(params)
```

Declaring the `photos` list where the extracted data from the current place will be added:

```python
photos = []
```

In order to get all photos from a specific location, you need to apply pagination while the next data packet is present. Therefore, an endless loop is created:

```python
while True:
    # pagination from current page
```

In the `new_page_results` dictionary we get a new data packet of the data in JSON format:

```python
new_page_results = search.get_dict()
```

There is a condition inside the loop. If the next data packet is present, then the `search` object is updated. Else, the cycle is stopped.

```python
if 'next' in new_page_results.get('serpapi_pagination', {}):
    search.params_dict.update(dict(parse_qsl(urlsplit(new_page_results.get('serpapi_pagination', {}).get('next')).query)))
else:
    break
```

Expanding the `photos` list with new data from this page:

```python
photos.extend(new_page_results['photos'])
# thumbnail = new_page_results['photos']['thumbnail']
# image = new_page_results['photos']['image']
# user_link = new_page_results['photos']['user']['link']
# user_id = new_page_results['photos']['user']['user_id']
# user_name = new_page_results['photos']['user']['name']
```

📌Note: If you want to extract some specific fields, then in the comment above I gave an example of how this can be implemented. If some data is missing, the extraction will fail. In this case, it is better to use the [`dict.get()` method](https://docs.python.org/3/library/stdtypes.html#dict.get).

Append `title` and `photos` from this place in the `photo_results` list:

```python
photo_results.append({
    'title': title,
    'photos': photos
})
```

After the all data is retrieved, it is output in JSON format:

```python
print(json.dumps(photo_results, indent=2, ensure_ascii=False))
```


<h2 id="output">Output</h2>

```json
[
  {
    "title": "Starbucks",
    "photos": [
      {
        "thumbnail": "https://lh5.googleusercontent.com/p/AF1QipOuxvgz55TI_eqJCrv_vrsZQs-YcRy2tIf_x97l=w203-h114-k-no",
        "image": "https://lh5.googleusercontent.com/p/AF1QipOuxvgz55TI_eqJCrv_vrsZQs-YcRy2tIf_x97l=w5312-h2988-k-no"
      },
      {
        "thumbnail": "https://lh5.googleusercontent.com/p/AF1QipNf9jWD_l0f6AAdhBK6RhzYIxyVFpA2_yOJodGC=w203-h114-k-no",
        "image": "https://lh5.googleusercontent.com/p/AF1QipNf9jWD_l0f6AAdhBK6RhzYIxyVFpA2_yOJodGC=w512-h289-k-no"
      },
      {
        "thumbnail": "https://lh5.googleusercontent.com/p/AF1QipOtdpwp3O8OANB5Jyz8RuGNuYdM1sM4NQqdBrRf=w203-h270-k-no",
        "image": "https://lh5.googleusercontent.com/p/AF1QipOtdpwp3O8OANB5Jyz8RuGNuYdM1sM4NQqdBrRf=w3024-h4032-k-no"
      },
      ... other photos
    ]
  },
  ... other results
]
```

📌Note: You can view [playground](https://serpapi.com/playground?engine=google_maps_photos&data_id=0x89c259a61c75684f%3A0x79d31adb123348d2&hl=en) or check the output. This way you will be able to understand what keys you can use in this JSON structure to get the data you need.


<h2 id="links">Links</h2>

- [Code in the online IDE](https://replit.com/@chukhraiartur/blog-google-maps-photos-api#main.py)
- [Google Maps Photos API](https://serpapi.com/google-maps-photos-api)
- [Google Maps Local Results API](https://serpapi.com/maps-local-results)
- [Google Maps API](https://serpapi.com/google-maps-api)

<p style="text-align: center;">Join us on <a href="https://twitter.com/serp_api">Twitter</a> | <a href="https://www.youtube.com/channel/UCUgIHlYBOD3yA3yDIRhg_mg">YouTube</a></p>

<p style="text-align: center;">Add a  <a href="https://github.com/serpapi/public-roadmap/issues">Feature Request</a>💫 or a <a href="https://github.com/serpapi/public-roadmap/issues">Bug</a>🐞</p>