← playbookhome →

Deeper · 04.A

The scraper, end to end.

Browser-based, headless, single profile, large viewport. Anything else gets you flagged.

01

The setup

One AdsPower profile (k1abvi20 for CTP work). Logged into the founder's Google account so the Maps results match what a real user would see. Connection to AdsPower over CDP via Playwright. All browser automation runs headless with viewport ≥ 1920×1080 — small viewports hide the Maps panels we need to scrape.

Never run two browser agents in parallel. Maps fingerprints collisions instantly. Sequential only.
02

The query loop

  1. 01
    Navigate to maps.google.com
    Logged-in session means real personalised results.
  2. 02
    Search "service in city"
    Match the same exact query the customer would type.
  3. 03
    Wait for the side panel to load
    Up to 4s. Don't race the DOM.
  4. 04
    Scroll the side panel to bottom
    Lazy-loaded results. Each scroll triggers more rows.
  5. 05
    Extract per business
    Name, phone, website, rating, review count, address, vertical hints.
  6. 06
    Deduplicate
    By phone first, by name+address second. Cross-vertical duplicates exist.
  7. 07
    Tag with vertical and city
    Stored in Provider Finder data with timestamps.
03

Quality filter

Below 4-star average and below 50 reviews → killed. No website → killed unless the vertical doesn't need one (street food, local fixers). Verifiable phone number is mandatory. The scraped row is provisional until a human eyeballs it once.

04

What it looks like

The Provider Finder dashboard groups scraped providers per site. For each provider: a wa.me link with a pre-filled, manually-reviewable message. The founder clicks one link at a time, sends from personal WhatsApp. Each click is a decision. No mass-send button exists. By design.