Yellow Pages это один из старейших бизнес-каталогов в интернете, и он по-прежнему остаётся плотным источником данных о местных компаниях: название, публичный номер телефона, почтовый адрес, категории, под которыми числится бизнес, и ссылка на его собственный сайт. Для поиска потенциальных клиентов, картирования рынка или построения регионального набора данных о поставщиках услуг эти публичные листинги это именно та структурированная информация, которая вам нужна. Трение операционное, а не концептуальное: Yellow Pages следит за трафиком, похожим на парсер, и быстро притормозит или выдаст проверку наивному циклу.
Это руководство показывает, как парсить бизнес-листинги Yellow Pages на Python надёжным способом. Вы получаете отрисованные страницы результатов поиска через Crawling API, разбираете каждый результат с помощью BeautifulSoup, чтобы извлечь название, телефон, адрес, категорию и сайт, затем проходите пагинацию, чтобы охватить полный набор результатов. Всё здесь ограничено публичными данными бизнес-каталога, а раздел о законности ближе к концу это не формальность, поэтому прочитайте его, прежде чем направлять это на реальный объём.
Что вы создадите
Небольшой парсер на Python, который принимает поисковый запрос и местоположение, получает отрисованную страницу результатов поиска Yellow Pages через Crawling API и извлекает структурированную запись для каждой компании на странице. Сквозной пример это компании сферы «Information Technology» в «Los Angeles, CA», и для каждого листинга мы собираем следующие поля:
- Название компании основной идентификатор листинга.
- Телефон публичный контактный номер, показанный на карточке.
- Адрес публичный почтовый адрес, используемый для любого географического анализа.
- Категория категории бизнеса, под которыми числится листинг.
- Сайт ссылка на собственный сайт компании, когда он указан.
Как устроены страницы поиска Yellow Pages
Поиск Yellow Pages управляется двумя параметрами URL: search_terms для запроса и geo_location_terms для местоположения. Отправьте поиск, и вы попадёте на страницу результатов, где каждая компания это самодостаточная карточка. Карточка несёт название в виде заголовка-ссылки, телефон и адрес в собственных блоках, категории в виде списка и, для компаний, оплативших или подтвердивших листинг, исходящую ссылку на сайт.
Результаты охватывают несколько страниц. Yellow Pages использует параметр URL page для перехода между ними, что делает пагинацию вопросом увеличения целого числа, а не погони за динамическим поведением «загрузить ещё». Именно эта предсказуемость позволяет одному и тому же парсеру работать по каждой странице без изменений, как только вы заставите его работать на одной.
Почему простой запрос испытывает трудности
Вы можете обратиться к URL поиска Yellow Pages с библиотекой requests и в удачный день получить HTML обратно. Проблема проявляется на объёме. Yellow Pages применяет защиту против парсинга: он ограничивает частоту по IP, выдаёт CAPTCHA трафику, который выглядит автоматизированным, и блокирует адреса центров обработки данных, которые запрашивают страницы в плотном, машинном паттерне. Один запрос с вашего ноутбука может пройти; несколько сотен с того же IP нет.
Поэтому парсеру, который действительно доводит дело до конца, нужны запросы, читающиеся как настоящий посетитель, приходящий с доверенного IP. Вы можете построить это сами с пулом ротируемых резидентных прокси и обвязкой для поддержания их в рабочем состоянии, но обслуживание этого стека и составляет основную часть работы. Crawling API сворачивает это в один вызов: вы отправляете ему URL, он направляет запрос через резидентные IP на стороне сервера и обрабатывает антибот-слой, после чего возвращает HTML для разбора.
Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML; токен JavaScript (JS) сначала отрисовывает страницу в настоящем браузере. Yellow Pages отдаёт данные листингов в исходном HTML, поэтому обычный токен здесь правильный выбор и делает каждый запрос дешевле. Тянитесь за токеном JS, только если цель начинает отрисовывать листинги на стороне клиента.
Предварительные требования
Несколько вещей, которые нужно подготовить сначала. Ни одна из них не займёт много времени.
Базовый Python. Вы должны уверенно запускать скрипт и устанавливать пакеты через pip. Если селекторы для вас новы, вводный материал как использовать BeautifulSoup в Python подробно охватывает сторону разбора.
Python 3.8 или выше. Подтвердите командой python --version. Если её нет, установите с python.org или через дистрибутив вроде Anaconda.
Учётная запись и токен Crawlbase. Зарегистрируйтесь, откройте панель управления и скопируйте обычный токен со страницы документации учётной записи. Вы получаете до 20 000 бесплатных запросов, и карта не требуется. Относитесь к токену как к паролю и держите его вне системы контроля версий.
Настройка проекта
Создайте виртуальное окружение, чтобы зависимости оставались изолированными, затем установите две библиотеки, нужные парсеру.
python --version python -m venv yellowpages_env source yellowpages_env/bin/activate pip install crawlbase beautifulsoup4
В Windows активируйте окружение командой yellowpages_env\Scripts\activate вместо строки source. Работу делают две зависимости: crawlbase это официальный клиент для Crawling API, а beautifulsoup4 разбирает возвращённый HTML, чтобы вы могли извлечь каждое поле по CSS-селектору.
Шаг 1: получение отрисованной страницы поиска
Начните с получения одной страницы результатов. Постройте URL поиска из вашего запроса и местоположения, импортируйте класс CrawlingAPI, инициализируйте его своим токеном и запросите URL. Проверка статуса перед разбором делает сбои громкими, а не тихими.
from urllib.parse import urlencode from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def build_url(query, location, page=1): base = "https://www.yellowpages.com/search?" params = {"search_terms": query, "geo_location_terms": location, "page": page} return base + urlencode(params) def crawl(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None if __name__ == "__main__": url = build_url("Information Technology", "Los Angeles, CA") html = crawl(url) print(html[:500] if html else "No HTML returned")
Обратите внимание, что проверка статуса считывает cb_status (legacy pc_status) из заголовков ответа, это статус Crawlbase для запроса, отличный от вышестоящего кода HTTP. Запустите скрипт командой python scraper.py, и вы должны увидеть реальную разметку результатов, а не страницу проверки. Это подтверждает, что путь получения работает, ещё до того, как вы напишете хоть один селектор.
Yellow Pages ограничивает частоту по IP и выдаёт проверки трафику, похожему на парсер. Crawling API направляет каждый запрос через ротируемые резидентные IP на стороне сервера, обрабатывает CAPTCHA и блокировки и отдаёт готовый к разбору HTML, так что вы избегаете содержания собственного пула прокси и слоя повторов. Сначала направьте его на публичную страницу поиска на бесплатном тарифе.
Шаг 2: разбор листингов с BeautifulSoup
Имея на руках страницу результатов, загрузите её в BeautifulSoup и пройдите карточки результатов. Каждая карточка находится под предсказуемым контейнером, и внутри неё название, телефон, адрес, категории и сайт сопоставляются с собственными селекторами. Чтение каждого поля защитно, с возвратом None, когда элемент отсутствует, удерживает одно недостающее значение от обрушения запуска.
from bs4 import BeautifulSoup def text_of(node): return node.get_text(strip=True) if node else None def extract_listings(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select("div.search-results.organic div.result") listings = [] for card in cards: name = card.select_one("a.business-name") phone = card.select_one("div.phone") address = card.select_one("div.adr") category = card.select_one("div.categories") website = card.select_one("a.track-visit-website") listings.append({ "name": text_of(name), "phone": text_of(phone), "address": text_of(address), "category": text_of(category), "website": website["href"] if website else None, }) return listings
Вспомогательная функция text_of запрашивает узел и возвращает None, когда он отсутствует, вместо того чтобы выбросить исключение при вызове .get_text() против ничего. Это сохраняет извлечение устойчивым: не у каждого листинга есть ссылка на сайт или чистый блок телефона, и недостающее поле должно оставить None в записи, а не остановить цикл. Сайт считывается из href якоря, а не из его текста, поэтому обрабатывается отдельно.
Имена классов выше (result, business-name, adr, categories, track-visit-website) отражают текущую разметку Yellow Pages, и эта разметка меняется без уведомления. Относитесь к селекторам как к стартовому шаблону, а не к контракту. Когда поле возвращается как None по каждому листингу, заново изучите живую страницу результатов в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов нормально для любого промышленного парсера.
Шаг 3: соберите всё вместе
Теперь свяжите получение и разбор в один готовый к запуску скрипт для одной страницы. Постройте URL, получите HTML, передайте его парсеру и выведите структурированные записи.
import json from urllib.parse import urlencode from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def build_url(query, location, page=1): base = "https://www.yellowpages.com/search?" params = {"search_terms": query, "geo_location_terms": location, "page": page} return base + urlencode(params) def crawl(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def text_of(node): return node.get_text(strip=True) if node else None def extract_listings(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select("div.search-results.organic div.result") listings = [] for card in cards: website = card.select_one("a.track-visit-website") listings.append({ "name": text_of(card.select_one("a.business-name")), "phone": text_of(card.select_one("div.phone")), "address": text_of(card.select_one("div.adr")), "category": text_of(card.select_one("div.categories")), "website": website["href"] if website else None, }) return listings def main(): url = build_url("Information Technology", "Los Angeles, CA") html = crawl(url) if not html: return data = extract_listings(html) print(json.dumps(data, indent=2)) if __name__ == "__main__": main()
Как выглядит результат
Запустите полный скрипт командой python scraper.py, и вы получите аккуратный список структурированных записей, готовый к записи в JSON, CSV или базу данных.
[ { "name": "L. A. Computer Works", "phone": "(310) 277-9799", "address": "2355 Westwood Blvd, Los Angeles, CA 90064", "category": "Computer Technical Assistance and Support Services", "website": "http://lacomputerworks.com" }, { "name": "Desktop Conquest", "phone": "(213) 321-1869", "address": "Los Angeles, CA 90057", "category": "Computer System Designers and Consultants", "website": null } ]
Листинги без подтверждённого сайта возвращаются с "website": null, что ожидаемо и именно поэтому парсер читает каждое поле защитно, а не предполагает, что каждый ключ присутствует.
Шаг 4: обработка пагинации по страницам результатов
Одна страница это демонстрация; реальная задача охватывает полный набор результатов. Поскольку Yellow Pages отдаёт страницу результатов через параметр URL page, проход по страницам это цикл по целочисленному диапазону. Те же функции build_url и extract_listings переносятся без изменений, так что пагинация это просто внешний цикл, который сам регулирует темп между запросами.
import time def scrape_all_pages(query, location, max_pages): all_listings = [] for page in range(1, max_pages + 1): url = build_url(query, location, page) html = crawl(url) if not html: print(f"Stopping at page {page}: no HTML") break listings = extract_listings(html) if not listings: print(f"No results on page {page}; reached the end") break all_listings.extend(listings) print(f"Page {page}: {len(listings)} listings") time.sleep(2) return all_listings if __name__ == "__main__": rows = scrape_all_pages("Information Technology", "Los Angeles, CA", max_pages=5) with open("yellow_pages.json", "w") as f: json.dump(rows, f, indent=2) print(f"Saved {len(rows)} listings")
Две детали делают этот цикл пригодным для промышленного использования. Он останавливается раньше, когда страница не возвращает листингов, так что вы не тратите запросы за пределами последней реальной страницы, и он спит две секунды между запросами, чтобы запуск не пришёл одним плотным всплеском. Подстройте max_pages и паузу под ваш объём; чем медленнее вы идёте, тем меньше внимания привлекаете.
Как оставаться незаблокированным
Даже когда Crawling API берёт на себя ротацию IP и антибот-слой, несколько привычек поддерживают запуск здоровым, и они применимы к любой каталожной цели.
- Регулируйте темп запросов. Пауза выше не косметика. Плотный цикл это самый быстрый способ получить троттлинг; разнесение запросов читается гораздо больше как нормальный трафик.
- Опирайтесь на ротацию. Пул резидентных IP распределяет запросы по множеству адресов реальных пользователей, так что ни один из них не срабатывает ограничение частоты. Crawling API делает это за вас; если вы строите собственный стек, именно эту часть нужно сделать правильно. Более глубокий фон в руководстве по ротации IP-адресов.
- Читайте коды состояния. Запуск, который начинает возвращать проверки или ошибки, говорит вам, что текущая частота слишком агрессивна. Относитесь к этому как к сигналу сбавить темп, а не как к шуму, который можно игнорировать.
Более широкий план смотрите в материалах как парсить сайты, не попадая в блокировки и более глубоком разборе как обходить CAPTCHA при веб-парсинге. Когда вы масштабируете это на множество запросов и местоположений, паттерны в материале крупномасштабный веб-парсинг охватывают очереди и хранение. Если вы предпочитаете направлять собственный трафик через ротируемый пул, а не использовать управляемый API, Smart AI Proxy даёт вам ту же резидентную ротацию в виде встраиваемой прокси-точки.
Законно ли парсить Yellow Pages?
Разрешён ли парсинг Yellow Pages зависит от условий обслуживания сайта, вашей юрисдикции и того, что вы делаете с данными. Ничего из кода здесь это не меняет; он лишь заставляет работать техническую часть. Прочитайте Условия обслуживания Yellow Pages и его robots.txt и относитесь к обоим как к границе того, что вы собираете и как быстро.
Несколько правил, которых стоит держаться. Собирайте только публичные данные бизнес-каталога: название компании, публичный номер телефона, публичный адрес и категорию, которые любой видит без входа в систему. Уважайте заявленные сайтом ожидания по частоте и держите объём запросов разумным, чтобы не нагружать его серверы. Это руководство намеренно ограничено той публичной поверхностью, потому что это та черта, которая делает работу защитимой.
То, что этот подход не охватывает, столь же важно. Он не трогает ничего за входом в систему и не обходит аутентификацию или любой контроль доступа, чтобы добраться до закрытого содержимого; это вне области здесь и противоречит условиям сайта. И учтите, что агрегирование контактных данных компаний может нести отдельные юридические обязательства в зависимости от вашей юрисдикции, даже когда каждое поле по отдельности публично, поэтому если вы планируете хранить, обогащать или коммерчески переиспользовать набор контактных данных, проверьте применимые к вам правила, а не считайте, что публичное означает неограниченное.
Ключевые выводы
-
Yellow Pages это структурированный каталог. Каждый результат поиска это карточка с названием, публичным телефоном, публичным адресом, категорией и необязательным сайтом, управляемая параметрами URL
search_termsиgeo_location_terms. - Простой запрос испытывает трудности на объёме. Ограничения частоты, CAPTCHA и блокировки IP останавливают наивный цикл; Crawling API направляет через резидентные IP и возвращает готовый к разбору HTML в одном вызове.
- Извлечением занимается BeautifulSoup. Сопоставьте название, телефон, адрес, категорию и сайт с текущими селекторами, читайте каждое поле защитно и ожидайте, что эти селекторы будут дрейфовать.
-
Пагинация это цикл по параметру
page. Переиспользуйте один и тот же парсер по страницам, останавливайтесь раньше на пустой странице и спите между запросами, чтобы регулировать темп запуска. - Оставайтесь на публичных данных. Уважайте Условия обслуживания и robots.txt, никогда не трогайте содержимое за входом в систему и помните, что агрегирование контактных данных может нести собственные обязательства по юрисдикции.
Часто задаваемые вопросы
Нужен ли мне обычный токен или токен JS для Yellow Pages?
Обычный токен. Yellow Pages отдаёт данные листингов в исходном HTML, поэтому получение с обычным токеном возвращает разбираемую разметку и делает каждый запрос дешевле. Токен JS сначала отрисовывает страницу в настоящем браузере, что нужно только тогда, когда цель загружает свои листинги на стороне клиента после прихода страницы. Начните с обычного токена и переключайтесь, только если поля повсеместно возвращаются пустыми.
Как мне обработать пагинацию на Yellow Pages?
Yellow Pages отдаёт страницу результатов через параметр URL page, поэтому вы проходите по целочисленному диапазону, строите URL на каждую страницу и запускаете один и тот же парсер на каждой. Останавливайтесь, когда страница возвращает ноль листингов, что отмечает конец набора результатов, и спите пару секунд между запросами, чтобы запуск не пришёл одним всплеском.
Мои селекторы возвращают None. Что изменилось?
Почти наверняка разметка Yellow Pages. Имена классов вроде result, business-name и track-visit-website меняются без уведомления, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Заново изучите живую страницу результатов в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов нормально для любого промышленного парсера, а не признак того, что что-то сломано.
Почему у некоторых листингов нет сайта?
Не каждая компания подтверждает или связывает свой собственный сайт на Yellow Pages, поэтому якорь сайта просто отсутствует на этих карточках. Парсер читает поле защитно и хранит None, а не выбрасывает исключение, так что недостающий сайт оставляет чистый null в записи, и цикл продолжается к следующему листингу.
Как мне избежать блокировки при парсинге Yellow Pages?
Держите частоту запросов на один IP низкой, регулируйте темп запросов задержкой и направляйте через ротируемые резидентные IP, чтобы ни один адрес не срабатывал ограничение частоты. Crawling API управляет ротацией и антибот-слоем за вас; если вы строите собственный стек, именно в эту часть стоит вложиться. Следите за кодами состояния и сбавляйте темп в тот момент, когда начинаете видеть проверки.
Могу ли я экспортировать спарсенные данные в Excel?
Да. Парсер производит список словарей, который pandas превращает в таблицу в две строки: pd.DataFrame(rows).to_excel("yellow_pages.xlsx", index=False). Поскольку каждая запись разделяет одни и те же ключи, столбцы выстраиваются аккуратно, и та же структура экспортируется столь же легко в CSV или таблицу базы данных.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
