<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roi Driscoll</title>
    <description>The latest articles on DEV Community by Roi Driscoll (@roi_driscoll_ef3c6a196a21).</description>
    <link>https://dev.to/roi_driscoll_ef3c6a196a21</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3550922%2Fa47b3813-f60e-40f3-b4a5-c55860244d17.png</url>
      <title>DEV Community: Roi Driscoll</title>
      <link>https://dev.to/roi_driscoll_ef3c6a196a21</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/roi_driscoll_ef3c6a196a21"/>
    <language>en</language>
    <item>
      <title>Scrape Google Maps Reviews for London's Best Roast Dinner! (Part 2)</title>
      <dc:creator>Roi Driscoll</dc:creator>
      <pubDate>Wed, 15 Oct 2025 14:23:16 +0000</pubDate>
      <link>https://dev.to/roi_driscoll_ef3c6a196a21/scrape-google-maps-reviews-for-londons-best-roast-dinner-part-2-3676</link>
      <guid>https://dev.to/roi_driscoll_ef3c6a196a21/scrape-google-maps-reviews-for-londons-best-roast-dinner-part-2-3676</guid>
      <description>&lt;p&gt;Welcome back my fellow gravy-drinker! Last time we saw how to use the Google Maps API to scrape Google Maps for a single location in a city, and pull in a collection of businesses, based on a simple query.&lt;/p&gt;

&lt;p&gt;In this instalment, we will continue our quest to find the best Sunday Roast dinner London has to offer, using SerpApi's powerful tools to scrape Google Maps Reviews and find out what folks are saying about the city's numerous pubs.&lt;/p&gt;

&lt;p&gt;But before we can scrape those reviews, we first need to find the pubs themselves. And while we know how to gather all the results for a single set of coordinates, covering the breadth of an entire city is a different challenge altogether.&lt;/p&gt;

&lt;p&gt;We're going to take a deep dive into the process of slicing a large area into a grid, and running a search over each section of that grid. This article is going to be quite image-heavy, so good news if you're a visual learner!&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking a City into a Grid
&lt;/h2&gt;

&lt;p&gt;Wouldn't it be great if we could just pick a zoom/elevation level that covers the entire city and scoop up all of the pubs in one fell swoop? Sadly that's not the Google Maps way.&lt;/p&gt;

&lt;p&gt;Here are a couple of screenshots at very different elevations/zoom levels - both the result of performing a single query directly on Google Maps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iyae1lohasqmoym67c5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iyae1lohasqmoym67c5.jpg" alt="Zoom out all you want, you're still only getting 20 results!&amp;lt;br&amp;gt;
" width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpv0bb9bvjh2pu3god7t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpv0bb9bvjh2pu3god7t.jpg" alt="The closer we zoom, the more detailed our results...&amp;lt;br&amp;gt;
" width="800" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that in both views, we still only get around 20 results: this tells us that the zoom level dictates how granular our results will be. Therefore, we need to settle on a zoom level close enough to give us a decent amount of results per 'view' (or Google Maps search).&lt;/p&gt;

&lt;p&gt;The size of the city you wish to traverse, will affect which zoom level you opt for. Once we've settled on this zoom level, we need to divide the map into a grid, then travel across the grid, pulling in pub listings for each section. We'll do this as follows:&lt;/p&gt;
&lt;h2&gt;
  
  
  Define a bounding box for London
&lt;/h2&gt;

&lt;p&gt;Where does London begin and end? For the sake of this example we'll pick two sets of latitude/longitude values to represent the top left, and bottom right corners of our grid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NW corner: 51.70, -0.50&lt;/li&gt;
&lt;li&gt;SE corner: 51.28, 0.30&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see that in context, on the actual map:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7fbj6xhz97zzkr8jnxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn7fbj6xhz97zzkr8jnxr.png" alt="Google Maps view of London, with our bounding box overlaid.&amp;lt;br&amp;gt;
" width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I don't want to start any turf wars - this is just an example. If you want to try this exercise yourself, you are of course welcome to define your own city limits.&lt;/p&gt;
&lt;h2&gt;
  
  
  Slicing that box into a grid
&lt;/h2&gt;

&lt;p&gt;If we want to scrape as many Google Maps reviews as possible across the city, we'll need to &lt;strong&gt;break our bounding box down into many smaller subsections of the city,&lt;/strong&gt; and &lt;em&gt;run an API query on each of them.&lt;/em&gt; This is because of the way Google Maps chooses to serve results based on zoom level and map position.&lt;/p&gt;

&lt;p&gt;If that's not intuitive for you, just think about the previous screenshots of Google Maps we looked at: depending on how close you're zoomed in (and where the centre of the map is on your screen), will determine which results Google Maps will serve you.&lt;/p&gt;

&lt;p&gt;So in order to get as many results as possible, we need to have several different 'views' to scrape from. To accomplish this, we can break down the bounding box we declared in the previous step, into a grid of smaller 'views' and scrape the results of each of them, and collect the results to cover the whole city.&lt;/p&gt;
&lt;h3&gt;
  
  
  Latitude/Longitude
&lt;/h3&gt;

&lt;p&gt;Let's take a quick trip back to Geography class and remind ourselves what &lt;a href="https://en.wikipedia.org/wiki/Geographic_coordinate_system" rel="noopener noreferrer"&gt;latitude and longitude&lt;/a&gt; actually looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6xpl76ylb9vygkygsga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6xpl76ylb9vygkygsga.png" alt=" " width="652" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When looking at coordinates, &lt;strong&gt;latitude is the first set of numbers&lt;/strong&gt; which means &lt;strong&gt;longitude is the last set.&lt;/strong&gt; When we &lt;em&gt;increase&lt;/em&gt; latitude, we head North. When we &lt;em&gt;decrease,&lt;/em&gt; we head South.&lt;/p&gt;

&lt;p&gt;Similarly, when we &lt;em&gt;increase&lt;/em&gt; longitude we head East - and therefore &lt;em&gt;decreasing&lt;/em&gt; steers us toward the West.&lt;/p&gt;

&lt;p&gt;If you have confusion between lat/long coordinates, don't worry - you're not the only one! Personally I think of it this way:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Latitude looks kind of like a ladder, on which you climb up (N) or down (S)"&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This little mnemonic helps me keep a strong visual in my mental model. OK, now we've revisited the basics of lat &amp;amp; long, we can continue.&lt;/p&gt;

&lt;p&gt;We have an area we will consider 'London' for the scope of this project, reflected in latitude/longitude points. Let's consider those for a moment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latitude range = &lt;strong&gt;51.28 → 51.70&lt;/strong&gt; → difference = &lt;strong&gt;0.42°&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Longitude range = &lt;strong&gt;-0.50 → 0.30&lt;/strong&gt; → difference = &lt;strong&gt;0.80°&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We know that the distance of 1 degree of latitude remains relatively constant at roughly &lt;strong&gt;111 kilometres&lt;/strong&gt; (69 miles) anywhere on Earth because lines of latitude are parallel to each other.&lt;/p&gt;

&lt;p&gt;However, longitude works slightly differently. The distance of 1 degree of longitude is not a constant value; it varies based on your location on Earth, specifically your latitude. This is because lines of longitude, known as meridians, are farthest apart at the &lt;strong&gt;equator&lt;/strong&gt; and converge at the &lt;strong&gt;poles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What this means for us, is that the simplest approach is to slice our bounding box into a grid of rectangles (instead of squares) using a single number - or 'step size':&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;GRID_STEP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will carve our bounding box into grid sections of &lt;strong&gt;5.5 km in latitude&lt;/strong&gt; (moving north to south), and &lt;strong&gt;3.5km in longitude&lt;/strong&gt; (moving east to west). With the measurements we're currently working with, we'll end up slicing London into a grid of 8 rows, by 16 columns.&lt;/p&gt;

&lt;p&gt;Let's try and visualise that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flr7pfbgpf2l5u57osr9k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flr7pfbgpf2l5u57osr9k.png" alt="We can see that each cell is more rectangle than square in our current model&amp;lt;br&amp;gt;
" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it's possible to instead make the grid cells square, this is the simplest approach mathematically, so we'll go with it to keep the focus on scraping.&lt;/p&gt;
&lt;h3&gt;
  
  
  Search Points - Where Are They?
&lt;/h3&gt;

&lt;p&gt;Each dot in the diagram below, marks a set of coordinates, and therefore &lt;strong&gt;shows us where on the map our script will trigger a search,&lt;/strong&gt; to scrape reviews &amp;amp; information from Google Maps. As we can see, this is a lot of searches:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5xy95o1dc2ntmhqlmmn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk5xy95o1dc2ntmhqlmmn.png" alt="Each intersection on the grid (marked by a dot) shows where an API call will happen&amp;lt;br&amp;gt;
" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our grid is 8 rows × 16 columns (128 cells), which means there are 9 × 17 = &lt;strong&gt;153 grid intersections.&lt;/strong&gt; So our script will make &lt;strong&gt;153 total API calls&lt;/strong&gt; — one per intersection point.&lt;/p&gt;

&lt;p&gt;While it's not &lt;em&gt;strictly&lt;/em&gt; correct to think of a Google Maps search as a circle, you can approximately think of it as a circular area centred on that coordinate, because Google’s ranking algorithm tends to prioritise results closest to the map centre, and the visible map region is roughly circular in effect when zoomed out.&lt;/p&gt;

&lt;p&gt;Let's take a look at the grid, with a single search area visualised:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frstrlzdbaijl4n4e88jc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frstrlzdbaijl4n4e88jc.png" alt="The dotted line shows our search radius, originating from the central point&amp;lt;br&amp;gt;
" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might be wondering: &lt;em&gt;"wouldn't we get less results outside of our boundary if we searched from the centre of each cell?"&lt;/em&gt; And it's true, there would be less overspill. You would also perform less searches. However, for the grid we have, and with the intention of being as thorough as possible, this approach will net us more results.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overlapping Results
&lt;/h2&gt;

&lt;p&gt;Let's take a quick look at the whole grid with search areas visualised so that we can see the overlap:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclg5pb9w2kvmxg1xo95j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fclg5pb9w2kvmxg1xo95j.png" alt="Visualising our search radii, we can see our searches will require de-duplication of results&amp;lt;br&amp;gt;
" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the first things we notice, is that because the coordinates on the edges of our grid are actually the centre of a search, we end up reaching outside of our grid for those particular searches. This could be remedied by making our grid smaller, if we were concerned with reducing our search area.&lt;/p&gt;

&lt;p&gt;As we can see from those overlapping dotted circles, we're going to be performing searches that overlap, so we'll need to make sure that once we've pulled in results from Google Maps, that we're removing duplicates.&lt;/p&gt;

&lt;p&gt;As mentioned in the first part of this series - there are many pubs in the UK that share the same name, even though they are completely unrelated. So we'll need to make sure that when we identify a result as a duplicate, that we are using a &lt;strong&gt;unique identifier&lt;/strong&gt; to do so.&lt;/p&gt;

&lt;p&gt;Fortunately part of SerpApi's Google Maps API JSON response, is a &lt;code&gt;data_id&lt;/code&gt; which we will need when we want to scrape review information from the Google Maps Reviews API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adapting the Code
&lt;/h2&gt;

&lt;p&gt;We've taken a really deep dive into the ideas behind performing a grid search, so if you're still with us: well done!&lt;/p&gt;

&lt;p&gt;Now we're going to adapt the code from last time (you can find the files &lt;a href="https://github.com/serpapi/tutorials/tree/master/ruby_projects/roast_finder/part_1" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In the last article, we searched for pubs at one set of coordinates. Now we're going to extend our application to iterate through the grid we defined above, to pull in a list of pubs from across the city.&lt;/p&gt;

&lt;p&gt;Once we have a list of candidate pubs, we'll be ready for part 3 of the series where we'll use SerpApi to scrape Google Maps reviews to figure out where the fluffiest Yorkshire Puddings are hiding out!&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;LONDON_GRID&lt;/code&gt; Constant
&lt;/h3&gt;

&lt;p&gt;This constant contains a Ruby Hash object, with the boundary coordinates we've chosen, along with the &lt;code&gt;grid_step&lt;/code&gt;, which dictates the size of our grid cells. For a smaller city we'd want a smaller grid step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bounding box and grid step for London&lt;/span&gt;
&lt;span class="no"&gt;LONDON_GRID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;lat_min:  &lt;/span&gt;&lt;span class="mf"&gt;51.28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lat_max:  &lt;/span&gt;&lt;span class="mf"&gt;51.70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lng_min:  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lng_max:   &lt;/span&gt;&lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;grid_step: &lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll pass the &lt;code&gt;LONDON_GRID&lt;/code&gt; hash into our &lt;code&gt;CityGrid&lt;/code&gt; when we initialise it. Let's look at that next:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;CityGrid&lt;/code&gt; Class
&lt;/h3&gt;

&lt;p&gt;For the code relating to breaking the city into a grid, let's group this related logic into a &lt;code&gt;CityGrid&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Handles dividing a city bounding box into a grid of points&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CityGrid&lt;/span&gt;
  &lt;span class="c1"&gt;# Define getter methods for the following properties&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:lat_min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lat_max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lng_min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:lng_max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:grid_step&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@lat_min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:lat_min&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@lat_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:lat_max&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@lng_min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:lng_min&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="vi"&gt;@lng_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:lng_max&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;        
    &lt;span class="vi"&gt;@grid_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:grid_step&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Generate all grid points (lat, lng) within the bounding box&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;points&lt;/span&gt;
    &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lat_min&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;lat_max&lt;/span&gt;
      &lt;span class="n"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lng_min&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;lng_max&lt;/span&gt;
          &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="n"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;grid_step&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;grid_step&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;points&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CityGrid&lt;/code&gt; is set up to accept a hash object which dictates all the boundaries and grid step size. With this information, the &lt;code&gt;points&lt;/code&gt; method can be called to generate an array of grid points, for our &lt;code&gt;RoastFinder&lt;/code&gt; class to iterate through and call the Google Maps API for each pair of coordinates.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;RoastFinder&lt;/code&gt; Class (Updated)
&lt;/h3&gt;

&lt;p&gt;OK time to upgrade our &lt;code&gt;RoastFinder&lt;/code&gt; to include the power of &lt;code&gt;CityGrid&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'serpapi'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'pub'&lt;/span&gt;
&lt;span class="c1"&gt;# Require our city_grid.rb file (to have access to the CityGrid class)&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'city_grid'&lt;/span&gt;

&lt;span class="no"&gt;LONDON_GRID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="ss"&gt;lat_min:  &lt;/span&gt;&lt;span class="mf"&gt;51.28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lat_max:  &lt;/span&gt;&lt;span class="mf"&gt;51.70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lng_min:  &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;lng_max:   &lt;/span&gt;&lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;grid_step: &lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;
&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoastFinder&lt;/span&gt;
  &lt;span class="c1"&gt;# adds a getter method for city_grid&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:pubs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:city_grid&lt;/span&gt;

  &lt;span class="no"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your api key"&lt;/span&gt;

  &lt;span class="c1"&gt;# Instantiates a CityGrid object and assigns it to @city_grid&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@city_grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CityGrid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LONDON_GRID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Adds call to grid_search&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
    &lt;span class="n"&gt;pub_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_search&lt;/span&gt;
    &lt;span class="vi"&gt;@pubs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_pubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output_pubs&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Generate all grid points for the bounding box using CityGrid&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grid_points&lt;/span&gt;
    &lt;span class="n"&gt;city_grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;points&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Perform a grid search over the bounding box&lt;/span&gt;
  &lt;span class="c1"&gt;# and deduplicate pubs by data_id&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;grid_search&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;all_pubs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;grid_points&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;pubs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch_pubs_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data_id&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
        &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pub&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data_id&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
        &lt;span class="n"&gt;all_pubs&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pub&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;all_pubs&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_pubs_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@%.4f,%.4f,13z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"Fetching pub at &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;maps_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;api_key: &lt;/span&gt;&lt;span class="no"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;engine: &lt;/span&gt;&lt;span class="s1"&gt;'google_maps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;q: &lt;/span&gt;&lt;span class="s1"&gt;'pub sunday roast'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;google_domain: &lt;/span&gt;&lt;span class="s1"&gt;'google.co.uk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;gl: &lt;/span&gt;&lt;span class="s1"&gt;'uk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;ll: &lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;hl: &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SerpApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maps_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:local_results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_pubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="no"&gt;Pub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;address: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:rating&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;place_id: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Outputs count of pubs scraped and first 5 in collection&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;output_pubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Pubs Found"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"First &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; examples:"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pretty_generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_h&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vg"&gt;$PROGRAM_NAME&lt;/span&gt;
  &lt;span class="no"&gt;RoastFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the &lt;code&gt;RoastFinder&lt;/code&gt; class was started in the &lt;a href="https://serpapi.com/blog/scrape-google-maps-reviews-for-londons-best-roast-dinner-part-1/" rel="noopener noreferrer"&gt;last part of this series,&lt;/a&gt; we'll just concentrate on what has changed since it was introduced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds the &lt;code&gt;LONDON_GRID&lt;/code&gt; hash to the file&lt;/li&gt;
&lt;li&gt;Adds a getter method for &lt;code&gt;city_grid&lt;/code&gt; (the instance variable which contains our &lt;code&gt;CityGrid&lt;/code&gt; object)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initialize&lt;/code&gt; now creates a &lt;code&gt;CityGrid&lt;/code&gt; object (passing in the &lt;code&gt;LONDON_GRID&lt;/code&gt; hash)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt; calls the new &lt;code&gt;grid_search&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grid_points&lt;/code&gt; wraps the points method of &lt;code&gt;CityGrid&lt;/code&gt; (which generates a list of coordinates)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;grid_search&lt;/code&gt; calls &lt;code&gt;CityGrid&lt;/code&gt; to get an array of grid coordinates for a given city area, and then over each set of coordinates, calls &lt;code&gt;fetch_pubs_at&lt;/code&gt; to scrape pubs at each location, before building an array of &lt;code&gt;Pub&lt;/code&gt; objects in the &lt;code&gt;@pubs&lt;/code&gt; instance variable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output_pubs&lt;/code&gt; has now been updated to display the first 5 items in &lt;code&gt;@pubs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As before, you can run this app by running the following from inside the directory containing your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install dependencies:&lt;/span&gt;
gem &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"serpapi"&lt;/span&gt;
&lt;span class="c"&gt;# run script:&lt;/span&gt;
ruby roast_finder.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the code for this section on our GitHub &lt;code&gt;Tutorials&lt;/code&gt; repo &lt;a href="https://github.com/serpapi/tutorials/tree/master/ruby_projects/roast_finder/" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it for part two of our gastronomic quest! Not only did we extend our script to scrape Google Maps over a large area, to pull in business information from nearly 1,000 businesses - but we went into thorough detail on the theory of how this technique works, giving you the tools to adapt the code for your own purposes.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we’ll make use of the &lt;code&gt;place_id&lt;/code&gt; of each &lt;code&gt;Pub&lt;/code&gt; object to head to SerpApi's Google Maps Reviews to start scraping customer reviews.&lt;/p&gt;

&lt;p&gt;Once we have those reviews, we'll feed the results into sentiment analysis tools. We'll take a look at techniques we can use to weigh and score the various opinions on each establishment so our code can confidently identify the roast with the most. See you next time!&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>ruby</category>
      <category>programming</category>
      <category>mapping</category>
    </item>
    <item>
      <title>Scrape Google Maps Reviews for London's Best Roast Dinner! (Part 1)</title>
      <dc:creator>Roi Driscoll</dc:creator>
      <pubDate>Tue, 07 Oct 2025 10:39:29 +0000</pubDate>
      <link>https://dev.to/roi_driscoll_ef3c6a196a21/scrape-google-maps-reviews-for-londons-best-roast-dinner-part-1-nle</link>
      <guid>https://dev.to/roi_driscoll_ef3c6a196a21/scrape-google-maps-reviews-for-londons-best-roast-dinner-part-1-nle</guid>
      <description>&lt;p&gt;Many visitors to the UK arrive believing that Fish &amp;amp; Chips are the country's crowning culinary achievement. But ask most natives what they actually consider more essential, and a great number will vote in favour of the humble &lt;strong&gt;Sunday Roast.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Given the dish's popularity, any pub worth its salt will offer a Sunday Roast (many with a dizzying array of options), and pubs can live or die by their reputation on this matter.&lt;/p&gt;

&lt;p&gt;However, with quality varying considerably between establishments, how do you know who to trust with your dining experience?&lt;/p&gt;

&lt;p&gt;We're going to use SerpApi to scrape Google Maps reviews to find the best dish out there - hooray!&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan of Action
&lt;/h2&gt;

&lt;p&gt;In this blog series, we're going to tackle the complicated task of breaking down a large area into a grid, and iterate over that grid to scrape business information from Google Maps.&lt;/p&gt;

&lt;p&gt;We'll break it down into a series of detailed steps, so if you've struggled in the past to understand how to perform a grid search of an area, this series will be perfect for you!&lt;/p&gt;

&lt;p&gt;Once we have that list of businesses (in our case, pubs), we will iterate through them to scrape Google Maps Reviews and find out what people are actually saying about the quality of meals being served in each establishment.&lt;/p&gt;

&lt;p&gt;With that data, we'll perform some analysis to score each pub (based on the contents of those reviews), and then we'll be able to output a list of the best places to find a roast dinner in London.&lt;/p&gt;

&lt;p&gt;By the end of this series, you'll have enough knowledge to follow this pattern for any number of businesses and understand who rates highly for a specific product or service.&lt;/p&gt;

&lt;p&gt;So, in summary, we plan to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrape Google Maps to find all of the pubs in London&lt;/li&gt;
&lt;li&gt;Scrape Google Maps Reviews for each pub&lt;/li&gt;
&lt;li&gt;Analyse each review searching for:

&lt;ul&gt;
&lt;li&gt;Keywords relating to Sunday Roasts&lt;/li&gt;
&lt;li&gt;Perform sentiment analysis on relevant scraped Google Maps Reviews&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Return a list of pubs with recommended Sunday Roast offerings&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Language &amp;amp; Coding Style
&lt;/h2&gt;

&lt;p&gt;The code used for this article will be written in Ruby, but even if you're not a Rubyist, hopefully you'll find that the code is easy to understand - and you can translate the concepts to whatever language you prefer to work in.&lt;/p&gt;

&lt;p&gt;We'll also be using OOP to organise our code into classes, since there will be quite a few moving parts by the end of the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Source
&lt;/h2&gt;

&lt;p&gt;SerpApi’s &lt;a href="https://serpapi.com/google-maps-api" rel="noopener noreferrer"&gt;Google Maps API&lt;/a&gt; is perfect for the first part of this project. Using this API will allow us to search for businesses (e.g., “Pubs” or "Restaurants") in a given area.&lt;/p&gt;

&lt;p&gt;Once we have those businesses, we'll need a second source: the SerpApi &lt;a href="https://serpapi.com/google-maps-reviews-api" rel="noopener noreferrer"&gt;Google Maps Reviews API&lt;/a&gt;. With this, we can scrape Google Maps reviews for each of the businesses we've collected (along with ratings, opening hours, and metadata).&lt;/p&gt;

&lt;p&gt;With the reviews data, we can parse each review to look for keywords like &lt;em&gt;roast, chicken, beef, Yorkshire pudding,&lt;/em&gt; etc.&lt;/p&gt;

&lt;p&gt;If you want to follow along yourself, it's an excellent time to sign up for a SerpApi account as now the free plan search limit has increased 150% to allow 250 searches per month.&lt;/p&gt;

&lt;p&gt;Just head on over to &lt;a href="https://serpapi.com/" rel="noopener noreferrer"&gt;https://serpapi.com&lt;/a&gt; and register for access to all of our APIs!&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Pubs that Offer Roasts
&lt;/h2&gt;

&lt;p&gt;Now, there are a lot of pubs in London - it's a big city (more on that in part 2 of this series). So we want to concentrate only on pubs that offer a Sunday Roast. For now, it should be sufficient to use the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;q: &lt;/span&gt;&lt;span class="s2"&gt;"pub sunday roast"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can play around with this later if required, but since a Sunday Roast is such a standard offering around the UK, we can be fairly confident that this simple query will pull in a lot of results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Scrape Some Pubs!
&lt;/h2&gt;

&lt;p&gt;Before we go sweeping across an entire city, let's first see how we can scrape all the pubs from a single set of coordinates. We'll use the following, as they are representative of a fairly central area of London:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latitude: 51.51&lt;/li&gt;
&lt;li&gt;Longitude: -0.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're wondering how I got those coordinates, you can easily find them for a given area by searching in Google Maps directly in your browser, and then extracting the following part from the url:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zkmbrt6nz22yxnqu5tk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zkmbrt6nz22yxnqu5tk.png" alt="Screenshot of Google Maps showing latitude/longitude information in the url bar of the browser." width="800" height="443"&gt;&lt;/a&gt;&lt;br&gt;
As is convention, the latitude value comes first, followed by longitude (and in this case I rounded the values down for simplicity).&lt;/p&gt;

&lt;p&gt;To keep results at a manageable amount, we'll avoid paginating beyond the first page of results for each set of coordinates. Google Maps returns around 20 results by default per query, so we'll have a good amount of pubs to look at by the end of the process (especially after we cover the whole city in later parts of this series).&lt;/p&gt;
&lt;h2&gt;
  
  
  The &lt;code&gt;Pub&lt;/code&gt; Class
&lt;/h2&gt;

&lt;p&gt;The first thing you'll notice is our &lt;code&gt;Pub&lt;/code&gt; class. Since we're going to be interested in pubs and their associated reviews, it makes sense to be able to create a representative pub object that can store data about each individual pub.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sidenote: Ruby's &lt;code&gt;attr_&lt;/code&gt; Methods
&lt;/h3&gt;

&lt;p&gt;Ruby has a nice way of making quick getter and setter methods for class instance variables (which look like this: &lt;code&gt;@variable_name&lt;/code&gt;). There are three options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;attr_reader :variable_name&lt;/code&gt; - this creates a getter for &lt;code&gt;variable_name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;attr_writer :variable_name&lt;/code&gt; - this creates a setter for &lt;code&gt;variable_name&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;attr_accessor :variable_name&lt;/code&gt; - this creates a getter &amp;amp; setter for &lt;code&gt;variable_name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On each &lt;code&gt;Pub&lt;/code&gt; object we will only need to be able to &lt;em&gt;read&lt;/em&gt; attribute values, as they are set during object initialisation (creation of the object).&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;Pub&lt;/code&gt; object literally has two jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store the &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;rating&lt;/code&gt;, and &lt;code&gt;place_id&lt;/code&gt; for each pub returned from Google Maps.&lt;/li&gt;
&lt;li&gt;Output a hash (key/value pair object) containing the stored data, when requested.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it! Super simple. If you want to try this on your machine, I recommend putting the following code in a file called &lt;code&gt;pub.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Represents a pub and its associated data&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pub&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:place_id&lt;/span&gt;

  &lt;span class="c1"&gt;# Assigns attribute values during creation of each pub object&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;place_id&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
    &lt;span class="vi"&gt;@rating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rating&lt;/span&gt;
    &lt;span class="vi"&gt;@place_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;place_id&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Returns a hash (key/value pair object) based on the object properties&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_h&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;address: &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;place_id: &lt;/span&gt;&lt;span class="n"&gt;place_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The RoastFinder Class
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;RoastFinder&lt;/code&gt; class is going to be the main orchestrator of everything that happens in this series. But for now, its main duties will be to send requests to SerpApi to scrape business data from Google Maps - and then translate that information into &lt;code&gt;Pub&lt;/code&gt; objects (to be stored in an array within the &lt;code&gt;RoastFinder&lt;/code&gt; class).&lt;/p&gt;

&lt;p&gt;SerpApi has a Ruby library available which makes it nice and easy to make requests to all of our APIs using a single gem!&lt;/p&gt;

&lt;p&gt;To install it, simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;serpapi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then be sure to require the library at the top of the code file (you'll see an example in the following code for the &lt;code&gt;RoastFinder&lt;/code&gt; class).&lt;/p&gt;

&lt;p&gt;We'll need to access the &lt;code&gt;Pub&lt;/code&gt; objects later when we scrape Google Maps Reviews data to find out what people are saying about each pub's Sunday Roast!&lt;/p&gt;

&lt;p&gt;I've commented the code to explain what each method does, but essentially here are the main execution steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call the program from the terminal using &lt;code&gt;ruby roast_finder&lt;/code&gt; (from within the same directory as the files)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt; triggers, which in turn calls &lt;code&gt;fetch_pubs_at&lt;/code&gt; using the latitude/longitude values assigned in the &lt;code&gt;initialize&lt;/code&gt; method (which is executed automatically when &lt;code&gt;RoastFinder.new&lt;/code&gt; is called in the 'main entry point' at the bottom of the &lt;code&gt;RoastFinder&lt;/code&gt; file)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fetch_pubs_at&lt;/code&gt; uses SerpApi to scrape pub data from Google Maps, and stores the results in the local variable &lt;code&gt;pub_data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next in the &lt;code&gt;run&lt;/code&gt; method, &lt;code&gt;build_pubs&lt;/code&gt; is called, which iterates through the &lt;code&gt;pub_data&lt;/code&gt; to create &lt;code&gt;Pub&lt;/code&gt; objects, each representing a real pub pulled in, thanks to SerpApi.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pub&lt;/code&gt; objects are stored in the &lt;code&gt;@pubs&lt;/code&gt; instance variable&lt;/li&gt;
&lt;li&gt;The final method called inside &lt;code&gt;run&lt;/code&gt; is &lt;code&gt;output_pubs&lt;/code&gt;, which will display the properties of all the &lt;code&gt;Pub&lt;/code&gt; objects inside &lt;code&gt;pubs&lt;/code&gt; as you can see a bit later on in this article.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the code for &lt;code&gt;RoastFinder&lt;/code&gt;. Again, if you would like to follow along, I recommend putting this code in a file called &lt;code&gt;roast_finder.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You will need to assign your own API key to the constant &lt;code&gt;API_KEY&lt;/code&gt; otherwise you will receive an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Require the SerpApi Ruby library&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'serpapi'&lt;/span&gt;
&lt;span class="c1"&gt;# Require the Ruby JSON tools&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;
&lt;span class="c1"&gt;# Require our pub.rb file (to have access to the Pub class)&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'pub'&lt;/span&gt;

&lt;span class="c1"&gt;# Represents a 'roast finder', responsible for finding roast-serving pubs&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoastFinder&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:pubs&lt;/span&gt;

  &lt;span class="no"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your api key"&lt;/span&gt;

  &lt;span class="c1"&gt;# Assigns latitude/longitude values (hard-coded for Central London)&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;51.51&lt;/span&gt;
    &lt;span class="vi"&gt;@long&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.13&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Main workflow: search, build, analyze, score, and output pubs&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;
    &lt;span class="n"&gt;pub_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetch_pubs_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@long&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@pubs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_pubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output_pubs&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;  

  &lt;span class="c1"&gt;# Fetch pubs at a specific lat/lng using SerpApi&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_pubs_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@%.4f,%.4f,13z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="s2"&gt;"Fetching pub at &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;maps_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;api_key: &lt;/span&gt;&lt;span class="no"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;engine: &lt;/span&gt;&lt;span class="s1"&gt;'google_maps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;q: &lt;/span&gt;&lt;span class="s1"&gt;'pub sunday roast'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;google_domain: &lt;/span&gt;&lt;span class="s1"&gt;'google.co.uk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;gl: &lt;/span&gt;&lt;span class="s1"&gt;'uk'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;ll: &lt;/span&gt;&lt;span class="n"&gt;ll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;hl: &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SerpApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maps_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:local_results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Build Pub objects from pub data hashes&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_pubs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="no"&gt;Pub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;address: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:address&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;rating: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:rating&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;place_id: &lt;/span&gt;&lt;span class="n"&gt;pub_hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;output_pubs&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;pubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Pubs Found:"&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pretty_generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pubs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_h&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Main execution entry point.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kp"&gt;__FILE__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="vg"&gt;$PROGRAM_NAME&lt;/span&gt;
  &lt;span class="no"&gt;RoastFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Let's take a look at what we get from that search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"Fetching pub at @51.5100,-0.1300,13z"&lt;/span&gt;
20 Pubs Found:
&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Mayflower Pub"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"117 Rotherhithe St, London, SE16 4NF, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.7,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48760324328c71a3:0xcb470bee32fa422c"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Kings Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"251 Tooley St, London, SE1 2JX, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48760346814cdae3:0x47a29cd909c8d39f"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Victoria, Paddington"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"10A Strathearn Pl, Tyburnia, London, W2 2NH, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x4876054ca282a945:0x7517499701678b8a"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Old Shades"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"37 Whitehall, London, SW1A 2BX, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.7,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487604cfb044871f:0x9a37f113e95d776a"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Marquis Cornwallis"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"31 Marchmont St, Greater, London WC1N 1AP, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761b309b064dfd:0x7077a764e0b632a2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Lord Wargrave"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"40-42 Brendon St, London, W1H 5HE, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761ab598773367:0xa7aca0158085ae4e"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Pig and Butcher"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"80 Liverpool Rd, London, N1 0QD, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.4,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761b6812f60359:0x501729b45945c149"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Windmill, Mayfair"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"6-8 Mill St, London, W1S 2AZ, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x4876052a8aed9f7d:0x3865aedecde75c22"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Fox and Pheasant"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"1 Billing Rd, London, SW10 9UJ, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x4876057d60e01771:0xabaef6e4fb8e9248"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Fox &amp;amp; Anchor"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"115 Charterhouse St, Barbican, London, EC1M 6AA, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761b65333b78d5:0x26ffa85e8f7d336e"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Cadogan Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"298 King's Rd, London, SW3 5UG, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487605c427caa1eb:0x9711a4c66bec6aa1"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Hoop and Grapes"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"47 Aldgate High St, Greater, London EC3N 1AL, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x4876034b397428a9:0x95b223669cf43c23"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Hereford Arms, South Kensington"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"127 Gloucester Rd, South Kensington, London, SW7 4TE, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.4,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487605674f680181:0x2d34ca0f42aaa23e"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Lore of the Land"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"4 Conway St, London, W1T 6BB, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761b783c613277:0x9979279b7986df20"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Queens Head"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"15 Denman St, London, W1D 7HN, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487604d40beb0e95:0x175fabf07e4d14b2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Duchy Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"63 Sancroft St, London, SE11 5UG, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487604947d77f0bb:0xe66d85ee32c3ac1a"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Queens Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"30 Queen's Gate Mews, South Kensington, London, SW7 5QL, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x4876055bf5b22663:0x3343c17c9459ff57"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Ladbroke Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"54 Ladbroke Rd, London, W11 3NW, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.6,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48760fe47d0fd707:0x6d0614eb03a8df49"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Scolt Head"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"107A Culford Rd, London, N1 4HT, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.5,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x48761c909a936dbd:0x25780105113aaac2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"The Queens Arms"&lt;/span&gt;,
    &lt;span class="s2"&gt;"address"&lt;/span&gt;: &lt;span class="s2"&gt;"11 Warwick Wy, Pimlico, London, SW1V 1QT, United Kingdom"&lt;/span&gt;,
    &lt;span class="s2"&gt;"rating"&lt;/span&gt;: 4.4,
    &lt;span class="s2"&gt;"place_id"&lt;/span&gt;: &lt;span class="s2"&gt;"0x487604e03fe7dbc5:0xbe438016446d92bf"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in this single area-based sample we already see that there are two pubs with the same name: &lt;strong&gt;"The Queens Arms"&lt;/strong&gt; - but they are different establishments.&lt;/p&gt;

&lt;p&gt;In later parts of this series, we may need to deal with duplication as coordinates might return overlapping results when we traverse the city using our grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s our first step toward uncovering London’s finest Sunday Roasts! We’ve now got a working script that can scrape pub data straight from Google Maps, and we’ve wrapped it up neatly into Ruby classes we can build on.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we’ll move beyond a single set of coordinates and scale this up to cover the whole city. Londoners are spoiled for choice, and we want to make sure we don’t miss any contenders.&lt;/p&gt;

&lt;p&gt;Stick around: next we’ll start carving up the city into manageable bite-sized chunks so that we can cover every borough on our search for the ultimate Yorkshire Pudding. See you next time!&lt;/p&gt;

&lt;p&gt;(Originally posted on the &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fserpapi.com%2Fblog%2Fscrape-google-maps-reviews-for-londons-best-roast-dinner-part-1%2F" rel="noopener noreferrer"&gt;SerpApi blog.&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>ruby</category>
      <category>programming</category>
      <category>mapping</category>
    </item>
  </channel>
</rss>
