Proxy-Cheap
Proxies & Business
October 13, 2026
5 min

How to Scrape Yellow Pages Business Data with Python and Proxies

Alex Sadovskij
Alex Sadovskij
CEO Proxy-Cheap
How to Scrape Yellow Pages Business Data with Python and Proxies
Summary
Learn how to scrape Yellow Pages business data with Python. Build a requests and BeautifulSoup scraper, route it through proxies, and export clean leads to CSV.

The fastest developer path is Python: requests to fetch, BeautifulSoup to parse, pandas to export to CSV.

Yellow Pages results paginate at 30 listings per page, so pagination handling is essential for full city or category coverage.

Routing requests through a pool of proxies distributes them across many IPs, keeping collection stable and consistent on larger, multi-city jobs.

Match the proxy type to the job: datacenter for small crawls, rotating residential for high-volume multi-city work, static residential or ISP for resumable jobs.

To scrape Yellow Pages, send requests to yellowpages.com category result pages, parse each business listing with a library like BeautifulSoup, and page through the results to collect fields such as name, phone, address, and website. Export the dataset to CSV with pandas. For multi-city collection, route requests through proxies so sessions stay reliable while you gather publicly available business data.

How to scrape Yellow Pages with Python (step by step)

Install requests, beautifulsoup4, and pandas. Send a GET request to a Yellow Pages category URL, parse the HTML with BeautifulSoup, extract each listing's name, phone, address, and website, then paginate through results and export the data to CSV with pandas.

Setup

Use Python 3.11+ and install the required libraries:

pip install requests beautifulsoup4 pandas

Requests downloads pages, BeautifulSoup parses them, and pandas builds the output table. Send a standard browser User-Agent with each request.

URL structure

Yellow Pages organizes directories by city and category:

https://www.yellowpages.com/{city-state}/{category}?page={n}

For example, page 2 of Austin plumbers is https://www.yellowpages.com/austin-tx/plumbers?page=2. Avoid /search?search_terms=... URLs because robots.txt disallows /search* paths. City and category directory pages are open to crawlers.

Fetch and parse

Reuse a requests.Session, set a timeout, and check the status before parsing:

response = session.get(url, headers=HEADERS, proxies=PROXIES, timeout=30) if response.status_code != 200:    print(f"Got status {response.status_code} for {url}")

Parse organic listings inside div.search-results.organic so sponsored cards are excluded. Useful selectors include a.business-name, div.phones, div.street-address, div.locality, a.track-visit-website, and div.categories a. See the BeautifulSoup documentation for CSS selectors.

Use a helper for optional fields:

def get_text(card, selector):    element = card.select_one(selector)    return element.get_text(" ", strip=True) if element else None

Pagination and pacing

Loop through page numbers and stop when a page has no listings. Keep a max_pages limit while testing. Add a random 2- to 5-second delay between requests to keep traffic reasonable.

Export

Store each listing as a dictionary, create a pandas DataFrame, remove duplicate profile URLs, and export with to_csv(). For JSON, use df.to_json("leads.json", orient="records").

Full script

Replace the proxy placeholder with your credentials, or set PROXY_URL = None for a small test without a proxy.

import random import time import pandas as pd import requests from bs4 import BeautifulSoup BASE_URL = "https://www.yellowpages.com" HEADERS = {    "User-Agent": (        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "        "(KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"    ),    "Accept-Language": "en-US,en;q=0.9", } PROXY_URL = "http://USERNAME:PASSWORD@HOST:PORT" PROXIES = {"http": PROXY_URL, "https": PROXY_URL} if PROXY_URL else None RATING_WORDS = {"one": 1, "two": 2, "three": 3, "four": 4, "five": 5} def fetch_page(session, url):    response = session.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)    if response.status_code != 200:        print(f"Got status {response.status_code} for {url}")        return None    return response.text def get_text(card, selector):    element = card.select_one(selector)    return element.get_text(" ", strip=True) if element else None def get_rating(card):    element = card.select_one(".ratings .result-rating")    if not element:        return None    classes = element.get("class", [])    stars = next((RATING_WORDS[c] for c in classes if c in RATING_WORDS), None)    if stars is not None and "half" in classes:        stars += 0.5    return stars def parse_listings(html):    soup = BeautifulSoup(html, "html.parser")    rows = []    for card in soup.select("div.search-results.organic div.result"):        name_link = card.select_one("a.business-name")        if not name_link:            continue        website = card.select_one("a.track-visit-website")        reviews = get_text(card, ".ratings .count")        rows.append({            "name": name_link.get_text(strip=True),            "phone": get_text(card, "div.phones"),            "street": get_text(card, "div.street-address"),            "locality": get_text(card, "div.locality"),            "website": website.get("href") if website else None,            "categories": ", ".join(                a.get_text(strip=True) for a in card.select("div.categories a")            ),            "rating": get_rating(card),            "review_count": int(reviews.strip("()")) if reviews else None,            "profile_url": BASE_URL + name_link.get("href", ""),        })    return rows def scrape(city, category, max_pages=5):    all_rows = []    with requests.Session() as session:        for page in range(1, max_pages + 1):            url = f"{BASE_URL}/{city}/{category}?page={page}"            html = fetch_page(session, url)            if html is None:                break            rows = parse_listings(html)            if not rows:                print(f"No listings on page {page}, stopping.")                break            all_rows.extend(rows)            print(f"Page {page}: {len(rows)} listings")            time.sleep(random.uniform(2, 5))    return all_rows if __name__ == "__main__":    data = scrape("austin-tx", "plumbers", max_pages=3)    df = pd.DataFrame(data).drop_duplicates(subset="profile_url")    df.to_csv("yellowpages_austin_plumbers.csv", index=False)    print(f"Saved {len(df)} businesses to yellowpages_austin_plumbers.csv")

Run python yp_scraper.py. The CSV contains nine columns: name, phone, street, locality, website, categories, rating, review_count, and profile_url.

Yellow Pages selectors can change, so inspect a listing and update selectors if fields suddenly become empty. Email addresses usually aren't shown on results pages, so collecting them requires a second pass over profile_url.

Get script credentials from the Proxy-Cheap dashboard. For browser testing, use the Chrome proxy extension. For programmatic proxy management, see the Proxy-Cheap proxy API.

How to route your Yellow Pages scraper through proxies

A scraper using one IP can hit rate limits on larger, multi-city jobs. A proxy pool spreads requests across multiple IPs, keeping collection more stable. Pass a proxies dictionary to requests, then rotate through the pool.

The proxies dict. Use http and https keys with a proxy URL containing your credentials:

proxy = "http://USERNAME:PASSWORD@HOST:PORT" proxies = {"http": proxy, "https": proxy} response = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)

See the requests proxy documentation. Keep credentials out of version-controlled files and load them from environment variables.

Rotating across a pool. The simplest option is rotating residential proxies. Enable Random IP in the dashboard to get a new IP on each request without changing your code.

You can also manage a pool yourself by generating multiple Session IP credentials and rotating between them:

import time from itertools import cycle import requests PROXY_POOL = cycle([    "http://USERNAME:PASSWORD_1@HOST:PORT",    "http://USERNAME:PASSWORD_2@HOST:PORT",    "http://USERNAME:PASSWORD_3@HOST:PORT", ]) def fetch_page(session, url, retries=3):    for attempt in range(1, retries + 1):        proxy = next(PROXY_POOL)        proxies = {"http": proxy, "https": proxy}        try:            response = session.get(url, headers=HEADERS, proxies=proxies, timeout=30)            if response.status_code == 200:                return response.text            print(f"Attempt {attempt}: status {response.status_code}")        except requests.RequestException as error:            print(f"Attempt {attempt}: {error}")        time.sleep(5 * attempt)    return None

Replace the original fetch_page() with this version. Failed or 429 requests wait before retrying through the next proxy.

Sticky vs rotating sessions. Per-request rotation works well for independent jobs, such as pulling page 1 from many categories. Sticky sessions are better when requests depend on continuity, such as crawling one category in order. Proxy-Cheap Session IPs keep the same IP for about 30 minutes. For longer persistence, residential proxies also offer static IPs.

Keep the delay. Proxies distribute traffic but don't replace pacing. Keep the 2 to 5 second delay and modest concurrency for steadier collection across multiple cities.

Which proxy type fits your Yellow Pages scrape

Match the proxy type to the job. Datacenter proxies suit small crawls of public listings, rotating residential proxies fit high-volume multi-city collection, static residential and ISP proxies provide a consistent IP for resumable jobs, and mobile proxies suit the most demanding targets.

Job profileRecommended Proxy-Cheap productWhy it fits
Small, one city or categoryDatacenter proxiesHigh throughput, dedicated IPs, and unlimited bandwidth
Multi-city, high volumeRotating residential proxiesResidential pool across 180+ locations, pay-as-you-go billing, unlimited concurrency
Resumable jobs, consistent identityStatic residential proxies or ISP proxiesSame IP for the subscription, with country and ISP targeting
Hardest targetsRotating mobile proxiesCarrier-grade IPs from 100+ countries
Very large, continuous jobsUnlimited bandwidth proxiesNo per-GB metering for sustained collection

A simple rule: estimate your page count. Austin plumbers takes about 17 pages, which a few datacenter IPs can handle. Fifty categories across 30 cities can mean thousands of pages, where rotating residential proxies make more sense.

Budget also matters. Rotating residential and mobile proxies use pay-as-you-go GB billing with no monthly commitment or setup costs. Run a few hundred pages first to measure your real bandwidth cost per 1,000 listings.

Static products are billed per IP per month, and static residential includes a 7-day trial. See our guide to the different proxy types for a broader comparison.

For larger jobs, start with a small pay-as-you-go rotating residential plan, test one city, then scale up.

How to scrape Yellow Pages responsibly

Collect only publicly available business data, keep request rates reasonable, and add delays between requests. Review Yellow Pages' terms and applicable data-protection laws before running large or repeated jobs.

Use public pages only. Don't collect data behind logins.

Respect robots.txt and terms. The scraper avoids /search* paths because they are disallowed by robots.txt. Review the Yellow Pages terms of service, which prohibit bots and scrapers from collecting site data without prior written consent. For commercial or repeated use, get permission or legal advice.

Throttle requests. Keep the 2- to 5-second delay, limit concurrency, and stop if errors increase.

Handle personal data carefully. Business listings can include personal data, especially for sole proprietors. Collect only what you need, secure it, and honor removal requests.

What is Yellow Pages and what data does it hold

Yellow Pages, operated by Thryv, is a long-running US business directory covering categories such as plumbers, dentists, restaurants, and law firms.

Listings typically include business name, phone, address, city and ZIP, website, categories, rating, review count, and profile URL. Some also include years in business, opening hours, or email addresses.

Results are organized by location and category. Each page shows 30 organic listings, with sponsored cards in a separate container. A header such as "Showing 1-30 of 491" shows the total, helping you estimate the job size.

Common uses for Yellow Pages business data

Structured local business data feeds plenty of everyday workflows:

B2B lead generation. Build call or outreach lists by category and city, for example, every HVAC contractor in the Dallas area.

CRM enrichment. Fill in missing phone numbers and websites for accounts you already have.

Local market research. Count competitors in a category and compare their ratings to spot underserved neighborhoods. Proxy-Cheap's market research page covers proxy setups for this kind of work.

Sales territory planning. Map business density per city to split territories fairly between reps.

Local SEO and citation research. Check that a business's contact details match across directories.

Each one starts with the same CSV the script produces. For projects that pull from several sites, see our overview of web data collection and our sales and e-commerce use cases.

Frequently Asked Questions

Yellow Pages listings are publicly available business data, but the site's terms of service prohibit bots and scrapers without prior written consent. Whether a project is lawful depends on your jurisdiction, how you collect the data, and what you do with it. For commercial or repeated collection, get permission or legal advice first, and never collect anything behind a login.

Yes. Browser extensions and hosted scrapers can capture business names, addresses, and phone numbers without scripts. A Python script gives you more control over what you collect and how you export it. It also lets you route requests through proxies for reliable multi-city collection.

Common fields are business name, address, phone number, website, category, rating, review count, and the profile link. Some profile pages also show an email address. Not every listing has every field, so handle missing values gracefully.

Yellow Pages shows 30 organic businesses per results page, so full category or city coverage means paging through multiple results pages. Layouts change over time, so check the "Showing" line on a live page before you plan a large job.

For a small, one-off scrape, you may not. For multi-city or repeated collection, routing requests through a proxy pool distributes them across many IPs, keeping sessions stable and consistent.

It depends on scale. Datacenter proxies fit small crawls, rotating residential proxies fit high-volume multi-city collection, and static residential or ISP proxies fit resumable jobs that need a consistent IP.

High request volume from a single IP can trigger rate limits that interrupt collection. Adding delays between requests and routing traffic through a pool of proxies keeps sessions stable. If requests succeed but columns come back empty, the page markup has probably changed, so update your CSS selectors.

Collect each listing into a list of dictionaries, load it into a pandas DataFrame, and call to_csv(). From there, you can open it in Excel or import it into a CRM.