πŸš€ New: Try our EnrichmentAPI, Enrich leads with verified emails, company & person dataπŸš€ New: Try our EnrichmentAPITry it now
All articles

Analysing Footfall Near British Museum By Scraping Popular Times Data from Google Maps

Darshan Khandelwal Published Feb 25, 2025 Read 7 min
Analysing Footfall Near British Museum By Scraping Popular Times Data from Google Maps

TL;DR

  • Analyzes footfall near the British Museum using Google Maps β€œPopular Times”.
  • Flow: collect nearby businesses, extract hourly popularity, average by hour, visualize with heatmaps.
  • Stack: Python (requests, pandas), seaborn / matplotlib; sample code + images included.
  • Categories covered: restaurants, cafΓ©s, galleries, pubs, theatres (cafΓ©s peak at lunch).
  • Powered by Scrapingdog’s Google Maps API.

Google Maps is a great storehouse of information for businesses looking to extract phone numbers for lead generation, customer reviews for sentimental analysis, images for real-estate mapping, and footfall for analyzing the peak hours of certain places at a particular time.

In this article, we will scrape and analyze Google Maps’ Popular Times data for businesses in London during peak hours and visualize it using graphs and heat maps.

Process

In our first step, we would be scraping the list of businesses from Google Maps in a particular area of interest.

1 077Va5 Iw8KcGIH4 tO9IA

The area around the British Museum looks good for exploring the businesses of London. Here is the list of companies we are going to analyze:

  • Restaurants β€” Indian Restaurants, French Bistros

  • CafΓ©s

  • Art Galleries and Museums

  • Pubs and Bars

  • Theatres and Entertainment Venues

We will get the search listings for the above businesses by using our Google Maps API.

You can refer to thisΒ documentationΒ for more information on using Scrapingdog Google Maps API.

I am sharing a sample code of how I collected the businesses for each type.

1import requests
2import json
3import seaborn as sns
4import matplotlib.pyplot as plt
5import pandas as pd
6
7API_KEY = "APIKEY"
8BASE_URL = "https://api.scrapingdog.com/google_maps"
9PLACES_URL = "https://api.scrapingdog.com/google_maps/places"
10
11# Fetch business data
12def get_data():
13 response = requests.get(f"{BASE_URL}?ll=@51.5181901,-0.1525433,5z&query=Indian Restaurants Near The British Museum&api_key={API_KEY}")
14 data = response.json()
15 businesses = []
16
17 for place in data.get("search_results", []):
18 title = place.get("title")
19 place_id = place.get("place_id")
20
21 if not title or not place_id:
22 continue
23
24 try:
25 place_response = requests.get(f"{PLACES_URL}?place_id={place_id}&api_key={API_KEY}")
26 place_data = place_response.json()
27 popularity_scores = place_data.get("place_results", {}).get("popular_times", {}).get("graph_results", {})
28 except Exception as e:
29 print(f"Error fetching details for {title}: {e}")
30 popularity_scores = {}
31
32 businesses.append({
33 "title": title,
34 "place_id": place_id,
35 "popularity_scores": popularity_scores
36 })
37
38 # Save data to JSON
39 with open("businessLists.json", "w") as f:
40 json.dump(businesses, f, indent=2)
41 print("File written successfully!")
42
43 return businesses

Step by Step Process

Importing Required Libraries

  • requestsΒ β†’ Used to make HTTP requests to the API.

  • jsonΒ β†’ Handles JSON data (parsing & saving).

  • seabornΒ &Β matplotlib.pyplotΒ β†’ Used for visualization (not used in this function but likely needed for later).

  • pandasΒ β†’ Used for data analysis (not used in this function but likely needed for later).

Setting API Constants

1API_KEY = "APIKEY"
2BASE_URL = "https://api.scrapingdog.com/google_maps"
3PLACES_URL = "https://api.scrapingdog.com/google_maps/places"
  • API_KEYΒ β†’ Placeholder for the API key (should be replaced with a real key).

  • BASE_URLΒ β†’ Endpoint for Google Maps search results.

  • PLACES_URLΒ β†’ Endpoint for fetching details of a specific place.

Making an API Request for Nearby Restaurants and Extracting the Search Results

1response = requests.get(f"{BASE_URL}?ll=@51.5181901,-0.1525433,5z&query=Indian Restaurants Near The British Museum&api_key={API_KEY}")
2data = response.json()
3
4businesses = []
5for place in data.get("search_results", []):
6 title = place.get("title")
7 place_id = place.get("place_id")
  • Sends aΒ GET requestΒ toΒ BASE_URL.

  • Initializes an empty listΒ businessesΒ to store extracted data.

  • Loops through theΒ search resultsΒ (list of restaurants) and extractsΒ title(name of the restaurant) andΒ place_id(unique identifier for each restaurant).

Fetching Details for Each Restaurant

1try:
2 place_response = requests.get(f"{PLACES_URL}?place_id={place_id}&api_key={API_KEY}")
3 place_data = place_response.json()
4 popularity_scores = place_data.get("place_results", {}).get("popular_times", {}).get("graph_results", {})
5except Exception as e:
6 print(f"Error fetching details for {title}: {e}")
7 popularity_scores = {}
  • Sends a second requestΒ toΒ PLACES_URLΒ using the extractedΒ place_idΒ to fetchΒ detailed informationΒ about the restaurant.

  • Extracts popularity dataΒ (graph_results) from the response.

  • Handles errorsΒ β†’ If the request fails, prints an error message and assigns anΒ empty dictionaryΒ {}Β toΒ popularity_scores.

Finally, we fetched the details for each business in theΒ businessesΒ array and saved the data into a JSON file.

You might be thinking what isΒ popularity_scoresΒ stored here? If you check out theΒ Google Maps Places APIΒ at Scrapingdog, you will find the below response for each API call on a business. This is none other than popular times for a business on different weekdays.

1"popular_times": {
2 "graph_results": {
3 "sunday": [
4 {
5 "time": "6 AM",
6 "info": "",
7 "busyness_score": 0
8 },
9 {
10 "time": "7 AM",
11 "info": "Usually not busy",
12 "busyness_score": 7
13 },
14 {
15 "time": "8 AM",
16 "info": "Usually not busy",
17 "busyness_score": 18
18 },
19 {
20 "time": "9 AM",
21 "info": "Usually not too busy",
22 "busyness_score": 37
23 },
24 {
25 "time": "10 AM",
26 "info": "Usually not too busy",
27 "busyness_score": 39
28 },
29 {
30 "time": "11 AM",
31 "info": "Usually not too busy",
32 "busyness_score": 37
33 },
34 {
35 "time": "12 PM",
36 "info": "Usually not too busy",
37 "busyness_score": 27
38 },
39 {
40 "time": "1 PM",
41 "info": "Usually not too busy",
42 "busyness_score": 30
43 },
44 {
45 "time": "2 PM",
46 "info": "Usually not too busy",
47 "busyness_score": 48
48 },
49 {
50 "time": "3 PM",
51 "info": "Usually a little busy",
52 "busyness_score": 51
53 },
54 {
55 "time": "4 PM",
56 "info": "Usually not too busy",
57 "busyness_score": 43
58 },
59 {
60 "time": "5 PM",
61 "info": "",
62 "busyness_score": 0
63 },
64 {
65 "time": "6 PM",
66 "info": "",
67 "busyness_score": 0
68 },
69 {
70 "time": "7 PM",
71 "info": "",
72 "busyness_score": 0
73 },
74 {
75 "time": "8 PM",
76 "info": "",
77 "busyness_score": 0
78 },
79 {
80 "time": "9 PM",
81 "info": "",
82 "busyness_score": 0
83 },
84 {
85 "time": "10 PM",
86 "info": "",
87 "busyness_score": 0
88 },
89 {
90 "time": "11 PM",
91 "info": "",
92 "busyness_score": 0
93 }
94 ],
95 ....
96 }
97 }

Next, we will call this function and run our pipeline.

1businesses = get_data()
2df = prepare_heatmap_data(businesses)
3if not df.empty:
4 generate_heatmap(df)

TheΒ prepare_heatmap_dataΒ andΒ generate_heatmapΒ data will help us in organizing the data for heatmap and generating the graphic respectively.

1def normalize_hour_string(hour):
2 # Replace non-breaking spaces and standardize to the format used in 'columns'
3 return hour.replace('\u202f', ' ').strip()
4
5def prepare_heatmap_data(businesses):
6 columns = ["6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 PM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM", "12 AM", "1 AM", "2 AM", "3 AM", "4 AM", "5 AM"]
7 heatmap_data = []
8
9 for business in businesses:
10 title = business["title"]
11 daily_scores = {hour: [] for hour in columns} # Initialize all hours with empty lists
12
13 for day, hours in business["popularity_scores"].items():
14 for hour_data in hours:
15 hour_index = normalize_hour_string(hour_data['time'])
16 if 'busyness_score' in hour_data:
17 daily_scores[hour_index].append(hour_data['busyness_score'])
18
19 # Calculate the average popularity per hour across the week or fill with zero
20 daily_avg = [(sum(scores) / len(scores) if scores else 0) for scores in daily_scores.values()]
21 heatmap_data.append({"business": title, **dict(zip(columns, daily_avg))})
22
23 return pd.DataFrame(heatmap_data)

Step-by-step explanation

  • Normalize Hour Strings:Β ReplacesΒ non-breaking spaces (u202f) in hour strings.

Prepare Heatmap Data:

  • Defines hourly time slots (6 AM β€” 5 AM the next day).

  • InitializesΒ heatmap_dataΒ list to store processed business popularity scores.

  • Loops through each business.

  • Initializes an empty list for each hourΒ to store multiple days’ data.

  • Iterates through popularity scores andΒ appends busyness valuesΒ for each hour.

  • ComputesΒ average busynessΒ per hour.

  • Converts the result into aΒ Pandas DataFrame.

Finally, we generate the heatmap.

1# Generate heatmap
2def generate_heatmap(df):
3 plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # for unicode characters
4 plt.rcParams['axes.unicode_minus'] = False # for minus sign characters
5 columns = ["6 AM", "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 PM", "1 PM", "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM", "9 PM", "10 PM", "11 PM", "12 AM", "1 AM", "2 AM", "3 AM", "4 AM", "5 AM"]
6 heat_data = df.set_index("business").reindex(columns=columns)
7
8 plt.figure(figsize=(15, 8))
9 sns.heatmap(heat_data, cmap='YlOrRd', annot=True, fmt=".0f", linewidths=.5)
10 plt.title('Popularity by Hour')
11 plt.xlabel('Hour of Day')
12 plt.ylabel('Business')
13 plt.xticks(rotation=45)
14 plt.tight_layout()
15 plt.savefig("heatmap.png")
16 plt.show()

Step-by-step explanation:

  • Sets up MatplotlibΒ for Unicode support.

  • Formats DataFrameΒ for heatmap plotting.

  • Creates the heatmapΒ with Seaborn.

  • UsesΒ β€˜YlOrRd’ color gradient (yellow-to-red).

  • Annotates heatmap cellsΒ with numerical values.

  • Saves the heatmap as an imageΒ (heatmap.png).

Running this program will give you the following heatmap.

1 PJhgtKbzqmg56g8BJKQHg

Similarly, you can run the programs for other queries also.

French Bistros

1 SjwuKrPEbj5SAv8Zddm xg

Cafes

1 ZgJzlFKOBVBN66033nDzlA

Most of these cafes peak near lunchtime.

Art Galleries and Museums

1 mvPty8PWRUNFA tEcODDBA

Pubs and Bars

1 A0wpPIer8l3w9zkqzYZ53Q

Theatres and Entertainment Venues

1 1KBriRL6YL7 SIZiiWZqHg

Conclusion

Google Maps generates interesting data on businesses that can be used for creating studies and research.

In this article, we analyzed the popular times for various businesses in London using Scrapingdog Google Maps API. I hope you found it helpful! If you did, please consider sharing it on social media.

If you want to analyze free local business data, you can use this tool, extract 20 local businesses' data from a location.

Thanks for reading! πŸ‘‹

Additional Resources

Darshan Khandelwal

Chief of Technology at Scrapingdog. Every new bug comes under my to-do list. Writing is not something I'm into, but I share my insights through the blogs I write. Coding is what I love to do. Happy to connect with you on my social handle!