Skip to main content
RTILA Marketplace

Local Business Profile Scraper - project configuration

Search a map category and collect phones, websites and ratings from every business profile page.

Lead Generation Intermediate Experimental v1.0.0 Updated Updated 2026-09-20 8 min read
Download
  • Windows x86_64

    BOT_local-business-profile-scraper.exe

    98.9 MB

    Download
  • macOS Apple Silicon chmod +x

    BOT_local-business-profile-scraper.sh

    87.5 MB

    Download
  • macOS Intel chmod +x

    BOT_local-business-profile-scraper.sh

    99.4 MB

    Download
  • Linux x86_64 chmod +x

    BOT_local-business-profile-scraper.sh

    122.5 MB

    Download

Do not rename the downloaded file - the filename carries the bot's configuration id.

This tab presents the complete project configuration exactly as published - the same JSON the launcher fetches at launch - ready to read, copy, or import into the desktop product. Back to the overview tab.

Project configuration

Full project JSON for local-business-profile-scraper

Canonical byte length: 5,046 - the length of the canonical form the digest covers.

Canonical digest: 2bd85667352855564dd6b8b289c96fac25e32786fadf693e04b3f6d85b132411

About the delivered configuration encoding

At launch the backend delivers this configuration to the launcher as plain JSON. The block below is the complete project definition the launcher receives.

What you are reading has machine-readable equivalents, and the JSON at those routes is the same document as the JSON rendered on this page: the listing overview, the per-listing detail document and the configuration document. The digest and byte count shown in the panel header are computed over that document's canonical JSON form - recursively sorted keys, two-space indentation, no trailing newline - which is the form the provisioning pipeline signs and the reconcile control re-digests; it differs from the highlighted block above in key order and by that trailing newline, so hashing the visible text will not reproduce the digest.

Download this JSON
{
  "name": "Google Maps Local Business Scraper",
  "settings": {
    "urls": [
      "https://www.google.com/maps/?hl=en"
    ],
    "headless": false,
    "humanoidEnabled": false,
    "proxyEnabled": false,
    "captchaEnabled": false,
    "matchTimezoneAndLocale": false,
    "block_resources": false,
    "get_urls_from_sitemap": false,
    "respect_robots_txt": false,
    "skip_initial_navigation": false,
    "variables": [
      {
        "name": "search_term",
        "value": "coffee shops",
        "required": true,
        "is_list": false
      }
    ]
  },
  "datasets": {
    "Business_Details": {
      "item_selector": "",
      "properties": [
        {
          "name": "Category",
          "type": "text",
          "selector": "css=button[jsaction*='category']"
        },
        {
          "name": "Title",
          "type": "text",
          "selector": "css=h1"
        },
        {
          "name": "Amenities",
          "type": "html",
          "selector": "css=div[role=\"region\"][aria-label*=\"About\"]",
          "transformations": [
            {
              "type": "script",
              "code": "const regex = /aria-label=\"([^\"]+)\"/g;\nconst matches = [...value.matchAll(regex)].map(match => match[1]);\nreturn matches .join(\"\\n\");"
            }
          ]
        },
        {
          "name": "Phone",
          "type": "attribute",
          "selector": "css=[data-item-id*='phone']",
          "attribute": "aria-label",
          "transformations": [
            {
              "type": "replace"
            }
          ]
        },
        {
          "name": "Website",
          "type": "attribute",
          "selector": "css=[data-item-id*='authority']",
          "attribute": "href",
          "transformations": [
            {
              "type": "replace"
            }
          ]
        },
        {
          "name": "Menu",
          "type": "attribute",
          "selector": "css=[data-item-id='menu']",
          "attribute": "href"
        },
        {
          "name": "Price_Range",
          "type": "text",
          "selector": "css=table[aria-label='Price range histogram'] tr:first-child td:first-child"
        },
        {
          "name": "Rating",
          "type": "attribute",
          "selector": "css=[role='img'][aria-label*='stars' i]",
          "attribute": "aria-label",
          "transformations": [
            {
              "type": "regex",
              "pattern": "([\\d.,]+)",
              "default_value": ""
            }
          ]
        },
        {
          "name": "Reviews_Count",
          "type": "text",
          "selector": "css=[aria-label*='reviews' i]",
          "transformations": [
            {
              "type": "regex",
              "pattern": "([\\d,]+)",
              "default_value": ""
            }
          ]
        },
        {
          "name": "Address",
          "type": "attribute",
          "selector": "css=[data-item-id*='address' i]",
          "attribute": "aria-label",
          "transformations": [
            {
              "type": "replace"
            }
          ]
        },
        {
          "name": "Plus_Code",
          "type": "attribute",
          "selector": "css=[data-item-id='oloc']",
          "attribute": "aria-label",
          "transformations": [
            {
              "type": "replace"
            }
          ]
        }
      ]
    },
    "Map_Search_URLs": {
      "item_selector": "css=div[aria-label*='Results for '] div[role='article']",
      "properties": [
        {
          "name": "PlaceURL",
          "type": "attribute",
          "selector": "css=a[href*='/maps/place/']",
          "attribute": "href"
        }
      ]
    }
  },
  "commands": [
    {
      "command": "fill",
      "params": {
        "clear_before_fill": true,
        "selector": "css=input[name='q']",
        "value": "${search_term}"
      }
    },
    {
      "command": "wait_for_selector",
      "params": {
        "selector": "css=div[aria-label*='Results for '] div[role='article']"
      }
    },
    {
      "command": "infinite_scroll",
      "params": {
        "do": [],
        "max_scrolls": 0,
        "scroll_delay": 1500,
        "selector": "css=div[aria-label*='Results for ']"
      }
    },
    {
      "command": "extract_data",
      "params": {
        "dataset": "Map_Search_URLs"
      }
    },
    {
      "command": "for_each",
      "params": {
        "source_type": "dataset",
        "as": "item",
        "dataset": "Map_Search_URLs",
        "do": [
          {
            "command": "goto",
            "params": {
              "url": "${item.PlaceURL}"
            }
          },
          {
            "command": "wait_for_selector",
            "params": {
              "selector": "css=h1"
            }
          },
          {
            "command": "extract_data",
            "params": {
              "dataset": "Business_Details"
            }
          }
        ]
      }
    }
  ],
  "authors": []
}

Frequently asked questions

How do I scrape business details from map search results?
Run the launcher, type a category and location such as coffee shops in Springfield into the single search prompt, and the bot fills the map search box, scrolls the results pane to the end, collects every listing's profile URL, then visits each one and extracts the eleven Business_Details fields. No selector work is required from you.
What data does the business profile scraper collect?
Eleven fields per profile: Category, Title, Amenities, Phone, Website, Menu, Price_Range, Rating, Reviews_Count, Address and Plus_Code. Everything comes from the publicly rendered profile page - no logged-in views, no review text, no photos.
Why does the bot visit every listing page one by one?
Search-result cards only carry a fraction of the data. Phone numbers, websites, menus, amenities, price ranges and plus codes live on the individual profile pages, so the run collects profile URLs first, then iterates through them with a for_each loop, waiting for each page's heading before extracting. That is also why run time scales with result count.
How long does a full profile run take?
Budget roughly one page visit per listing on top of the scroll phase: a search returning 100 results typically takes 15-30 minutes at human-paced navigation, and large city-wide searches can run an hour or more. Narrow the search term or add a district qualifier to keep runs short.
Does it collect review text?
No, deliberately. The dataset captures the aggregate Rating and Reviews_Count only. Review text is personal data with a much higher compliance bar, and this configuration does not touch it.
Where does the collected business data go?
Rows are stored under the two dataset names from the configuration - Map_Search_URLs for the profile links and Business_Details for the eleven-field records - in the launcher's local results store on your machine. This release sets no fixed export filename, so you export from the results view; nothing is uploaded anywhere except the configuration fetch and download telemetry listed on this page.
Is collecting business profile data allowed?
Automated collection may conflict with the map platform's terms of service, and a sole trader's name, phone and address are personal data in most jurisdictions. You are responsible for reviewing the platform terms and establishing a lawful basis under GDPR, UK GDPR or your local equivalent before running the bot or contacting anyone from the output.
Does the scraper work offline?
No. The search results and profile pages are live web content, so the run needs a network connection throughout. The only other call the launcher makes is the configuration fetch at startup.
Can I limit how many profiles are visited?
This release ships with unbounded scrolling - it collects until the results pane stops growing - and then visits every collected profile. The practical control is your search term: add a neighbourhood, district or narrower category to shrink the result set before the run starts.
How are missing phone numbers or websites handled?
Empty is normal and expected. Not every business publishes a phone number, website or menu, and the transformations fall back to empty values rather than failing the row. Business_Details has no deduplication key in this release, so if a listing appears twice in the results pane it can produce two rows - check Title plus Address when merging.
Does the bot need an internet connection to run?
Yes on first run: the launcher fetches its configuration by id from the backend at launch, so an internet connection is required at least the first time it starts.
Can I rename the downloaded file?
No. The downloaded file must keep its exact name - the filename carries the per-bot configuration id, and renaming it breaks the configuration fetch at launch.