Zillow является одним из самых посещаемых сайтов о недвижимости в сети. Страницы объявлений содержат именно те структурированные данные, которые необходимы для отслеживания цен, исследования рынка и инвестиционного анализа: запрашиваемую цену, количество спален, ванных комнат, площадь, тип объекта и адрес. Для тех, кто изучает местный рынок, эти публичные данные объявлений являются основным сырьём, а собирать их вручную по десяткам объектов медленно и чревато ошибками.
В этом руководстве показано, как парсить Zillow с помощью Python надёжным способом. Вы создадите небольшой работоспособный скрапер, который получает отрендеренные страницы Zillow через Crawling API, собирает ссылки на объекты с поисковой страницы, извлекает нужные поля с помощью BeautifulSoup, обрабатывает пагинацию и экспортирует чистые JSON и CSV. Весь пример ограничен публичными данными объявлений, а раздел о правовом аспекте в конце не является формальностью, поэтому прочитайте его перед тем, как нацелить скрапер на реальные объёмы.
Что вы создадите
Скрипт Python, который берёт публичный URL поиска Zillow для конкретной локации, собирает ссылки на страницы объектов, получает каждое отрендеренное объявление через Crawling API и извлекает структурированную запись по каждому объекту. В качестве примера используются объекты на продажу в Columbia Heights, Washington, DC. Извлекаем следующие поля:
- Price запрашиваемая цена объекта.
- Beds количество спален.
- Baths количество ванных комнат.
- Size общая площадь дома.
- Address почтовый адрес, указанный в объявлении.
- Type тип объекта: кондоминиум, таунхаус или дом на одну семью.
- Link канонический URL страницы объекта.
Почему обычный запрос не работает на Zillow
Если вы запрашиваете поисковый URL Zillow или страницу объявления с помощью обычного HTTP-клиента, вы получаете ответ со статусом 200, но с лишь частью данных в теле. Против вас работают два фактора. Во-первых, Zillow загружает большинство результатов поиска и деталей объявлений в браузере через JavaScript и Ajax, поэтому исходный HTML является лишь тонкой оболочкой, которая заполняется только после выполнения скриптов страницы. Извлечёте ссылки на объекты из этого первого ответа, и вы получите лишь несколько карточек вместо полного набора. Во-вторых, Zillow быстро выявляет автоматизированный трафик: IP-адреса дата-центров и паттерны запросов, не похожие на настоящий браузер, подвергаются ограничению скорости, блокировке IP или вызовам до того, как они вообще достигают отрендеренного контента.
Таким образом, рабочий скрапер Zillow требует в одном запросе двух вещей: браузера, который действительно рендерит страницу, и IP-адреса, который платформа воспринимает как реального посетителя. Вы можете собрать это самостоятельно с помощью headless-браузера плюс пул ротирующих резидентских прокси, но поддержание этой связки в рабочем состоянии и составляет основную часть работы. Crawling API объединяет оба компонента в один вызов: вы отправляете ему URL с JavaScript-токеном, он рендерит страницу за надёжным IP и возвращает готовый HTML для парсинга.
Crawlbase предлагает два типа токенов. Обычный токен получает статический HTML; токен JavaScript (JS) сначала рендерит страницу в настоящем браузере. Zillow заполняет результаты поиска и поля объявлений на стороне клиента, поэтому здесь нужен JS-токен. Обычный токен возвращает ту же тонкую оболочку, что и простой запрос, и из неё практически нечего парсить.
Предварительные требования
Перед написанием кода необходимо иметь несколько вещей. Ни одна из них не занимает много времени.
Базовый Python. Вы должны уметь писать и запускать скрипты Python и устанавливать пакеты с помощью pip. Если парсинг для вас нов, руководство по BeautifulSoup станет хорошим дополнением к этому материалу.
Python 3.8 или выше. Проверьте свою версию командой python --version. Если у вас её нет, установите с python.org или через дистрибутив, например Anaconda, и убедитесь, что Python добавлен в PATH.
Аккаунт Crawlbase и JS-токен. Зарегистрируйтесь, откройте панель управления и скопируйте токен JavaScript (JS) со страницы документации аккаунта. Crawlbase включает 1000 бесплатных запросов для старта, чего вполне достаточно для работы с этим руководством. Обращайтесь с токеном как с паролем: он аутентифицирует ваши запросы, поэтому не добавляйте его в систему контроля версий.
Настройка проекта
Создайте виртуальное окружение, чтобы зависимости проекта оставались изолированными, затем установите библиотеки, необходимые скраперу.
python --version python -m venv zillow_env source zillow_env/bin/activate pip install crawlbase beautifulsoup4
На Windows активируйте окружение командой zillow_env\Scripts\activate вместо строки с source. Две зависимости выполняют всю работу: crawlbase является официальным клиентом для Crawling API, а beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать отдельные поля по CSS-селектору. Модули json и csv входят в стандартную библиотеку, поэтому для шага экспорта ничего дополнительно устанавливать не нужно.
Шаг 1: получение отрендеренной страницы Zillow
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с помощью JS-токена и запросите поисковый URL Zillow. Zillow загружает результаты асинхронно, поэтому передайте ajax_wait и page_wait, чтобы дождаться динамического контента перед захватом страницы. Проверка cb_status (legacy pc_status) Crawlbase перед парсингом делает сбои заметными, а не скрытыми.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } def crawl(page_url): response = api.get(page_url, OPTIONS) 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__": serp_url = "https://www.zillow.com/columbia-heights-washington-dc/sale/" html = crawl(serp_url) print(html[:500] if html else "No HTML returned")
Два параметра ожидания важны для такой цели с клиентским рендерингом, как Zillow. ajax_wait указывает API дождаться завершения загрузки асинхронного контента, а page_wait удерживает фиксированное количество миллисекунд после загрузки, чтобы карточки с поздним рендерингом появились перед захватом страницы. Пять секунд является разумным стартом; увеличьте значение, если результаты возвращаются неполными. Запустите скрипт командой python zillow_scraper.py, и вы должны увидеть реальную разметку поиска Zillow, а не оболочку, которую возвращает обычный запрос. Это подтверждает работу рендеринга ещё до написания единого селектора.
Zillow требует отрендеренную страницу за надёжным IP в одном вызове, именно это настраивают опции ajax_wait и page_wait выше. Crawling API принимает JS-токен, запускает страницу в настоящем браузере, ротирует резидентские IP на стороне сервера и передаёт готовый HTML, поэтому вам не нужно самостоятельно запускать парк headless-браузеров и пул прокси. Сначала попробуйте на публичной поисковой странице в рамках бесплатного тарифа.
Шаг 2: сбор ссылок на объекты с поисковой страницы
Поисковая страница Zillow представляет собой сетку карточек объектов, каждая из которых ссылается на полное объявление. Загрузите отрендеренный HTML в BeautifulSoup и извлеките href из ссылки каждой карточки. Zillow вкладывает их в сетку результатов, поэтому селектор идёт от контейнера сетки до ссылки на карточку объекта.
from bs4 import BeautifulSoup CARD_SELECTOR = ( 'div[id="grid-search-results"] > ul > li[class^="ListItem-"] ' 'article[data-test="property-card"] a[data-test="property-card-link"]' ) def get_property_urls(html): soup = BeautifulSoup(html, "html.parser") return [a["href"] for a in soup.select(CARD_SELECTOR) if a.get("href")]
Совпадение class^="ListItem-" является префиксным селектором: Zillow добавляет хэш к сгенерированным именам классов, поэтому ListItem- соответствует каждому элементу списка независимо от суффикса. Применение этого к отрендеренному поисковому HTML возвращает чистый список URL страниц объектов:
[ "https://www.zillow.com/homedetails/1429-Girard-St-NW-101-Washington-DC-20009/2053968963_zpid/", "https://www.zillow.com/homedetails/1439-Euclid-St-NW-APT-301-Washington-DC-20009/68081615_zpid/", "https://www.zillow.com/homedetails/1362-Newton-St-NW-Washington-DC-20010/472850_zpid/", "https://www.zillow.com/homedetails/1458-Columbia-Rd-NW-APT-300-Washington-DC-20009/82293130_zpid/" ]
Сгенерированные имена классов Zillow и атрибуты data-test меняются без предупреждения. Считайте селекторы здесь стартовым шаблоном, а не контрактом. Когда список возвращается пустым, заново проверьте живую страницу в инструментах разработчика браузера и обновите селектор. Периодическое обслуживание селекторов является нормальным для любого производственного скрапера, а не признаком поломки.
Шаг 3: обработка пагинации по поисковым страницам
Одна поисковая страница является фрагментом результирующего набора. Zillow использует пагинацию с сегментом пути {pageNo}_p, поэтому вы получаете первую страницу для чтения общего количества страниц, а затем обходите каждую страницу, собирая ссылки. Небольшая обёртка с повторными попытками вокруг запроса не позволяет одной медленной странице прервать весь прогон.
import time def fetch_html(page_url, max_retries=2): for attempt in range(max_retries + 1): html = crawl(page_url) if html: return html if attempt < max_retries: print(f"Retrying ({attempt + 1}/{max_retries})...") time.sleep(1) print(f"Unable to fetch {page_url}") return None def collect_all_urls(base_url, max_pages): first_html = fetch_html(f"{base_url}1_p/") if not first_html: return [] soup = BeautifulSoup(first_html, "html.parser") last = soup.select_one("div.search-pagination > nav > li:nth-last-child(3)") total_pages = int(last.text) if last else 1 pages = min(total_pages, max_pages) all_urls = get_property_urls(first_html) for page in range(2, pages + 1): html = fetch_html(f"{base_url}{page}_p/") if html: all_urls.extend(get_property_urls(html)) time.sleep(2) return all_urls
fetch_html повторяет неудачный запрос до двух раз с небольшой паузой, возвращая HTML при успехе и None после исчерпания попыток. collect_all_urls считывает наибольший номер страницы из навигационного элемента пагинации (Zillow размещает его ближе к концу списка, поэтому используется nth-last-child(3)), ограничивает обход вашим потолком max_pages, чтобы большой рынок не вышел из-под контроля, и собирает ссылки с каждой страницы. time.sleep(2) между страницами задаёт темп прогона, чтобы не перегружать сайт.
Шаг 4: парсинг каждой страницы объекта
Имея полный список URL объектов, получите каждое объявление и извлеките поля. Zillow группирует основные сведения внутри блока macro-data-view, поэтому приведённые ниже селекторы сопоставляют цену, количество спален, ванных комнат, площадь, адрес и тип с отдельными элементами. Каждый запрос защищён, чтобы отсутствующее поле возвращало None, а не прерывало прогон.
VIEW = 'div[data-testid="macro-data-view"]' FACTS = ( f'{VIEW} > div[data-renderstrat="inline"]:nth-child(2) ' 'div[data-testid="bed-bath-sqft-facts"]' ) def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def scrape_property(html, url): soup = BeautifulSoup(html, "html.parser") return { "link": url, "price": text_of(soup, f'{VIEW} span[data-testid="price"] > span'), "address": text_of(soup, f'{VIEW} div[class^="styles__AddressWrapper-"] > h1'), "beds": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:first-child > span:first-child'), "baths": text_of(soup, f'{FACTS} > button > div[data-testid="bed-bath-sqft-fact-container"] > span:first-child'), "size": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:last-child > span:first-child'), "type": text_of(soup, f'{VIEW} > div[data-renderstrat="inline"]:nth-child(3) div.dBmBNo:first-child > span'), }
Вспомогательная функция text_of запрашивает один элемент и возвращает его очищенный текст или None при отсутствии элемента, поэтому объявление, в котором отсутствует поле, не прерывает цикл. Селекторы взяты прямо из разметки страницы объявления Zillow: price считывает основной ценовой span, address считывает H1 внутри обёртки адреса, а количество спален, ванных комнат и площадь находятся в общем контейнере bed-bath-sqft-facts, различаясь по своей позиции. Количество ванных комнат в разметке Zillow находится внутри button, поэтому соответствующий селектор немного отличается от двух других.
Шаг 5: сборка полного скрипта
Теперь свяжите части в один работоспособный скрипт: соберите URL по страницам, обработайте каждый объект и экспортируйте записи в JSON и CSV.
import csv import json import time from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 5000, } CARD_SELECTOR = ( 'div[id="grid-search-results"] > ul > li[class^="ListItem-"] ' 'article[data-test="property-card"] a[data-test="property-card-link"]' ) VIEW = 'div[data-testid="macro-data-view"]' FACTS = ( f'{VIEW} > div[data-renderstrat="inline"]:nth-child(2) ' 'div[data-testid="bed-bath-sqft-facts"]' ) def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def fetch_html(page_url, max_retries=2): for attempt in range(max_retries + 1): html = crawl(page_url) if html: return html if attempt < max_retries: time.sleep(1) return None def text_of(soup, selector): el = soup.select_one(selector) return el.get_text(strip=True) if el else None def get_property_urls(html): soup = BeautifulSoup(html, "html.parser") return [a["href"] for a in soup.select(CARD_SELECTOR) if a.get("href")] def collect_all_urls(base_url, max_pages): first_html = fetch_html(f"{base_url}1_p/") if not first_html: return [] soup = BeautifulSoup(first_html, "html.parser") last = soup.select_one("div.search-pagination > nav > li:nth-last-child(3)") total_pages = int(last.text) if last else 1 pages = min(total_pages, max_pages) all_urls = get_property_urls(first_html) for page in range(2, pages + 1): html = fetch_html(f"{base_url}{page}_p/") if html: all_urls.extend(get_property_urls(html)) time.sleep(2) return all_urls def scrape_property(html, url): soup = BeautifulSoup(html, "html.parser") return { "link": url, "price": text_of(soup, f'{VIEW} span[data-testid="price"] > span'), "address": text_of(soup, f'{VIEW} div[class^="styles__AddressWrapper-"] > h1'), "beds": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:first-child > span:first-child'), "baths": text_of(soup, f'{FACTS} > button > div[data-testid="bed-bath-sqft-fact-container"] > span:first-child'), "size": text_of(soup, f'{FACTS} > div[data-testid="bed-bath-sqft-fact-container"]:last-child > span:first-child'), "type": text_of(soup, f'{VIEW} > div[data-renderstrat="inline"]:nth-child(3) div.dBmBNo:first-child > span'), } def save_outputs(records): with open("zillow_properties.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("zillow_properties.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): serp_url = "https://www.zillow.com/columbia-heights-washington-dc/sale/" urls = collect_all_urls(serp_url, max_pages=2) records = [] for url in urls: html = fetch_html(url) if html: records.append(scrape_property(html, url)) time.sleep(2) save_outputs(records) print(f"Saved {len(records)} properties") if __name__ == "__main__": main()
Скрипт собирает ссылки на объекты с двух поисковых страниц, получает каждое объявление с помощью обёртки с повторными попытками, парсит его в запись и задаёт темп цикла с помощью двухсекундного ожидания. save_outputs записывает JSON-файл и CSV, используя ключи первой записи в качестве заголовка, поэтому данные будут в том формате, который удобен вашему нижестоящему инструменту. Настройте max_pages и URL поиска под свою целевую локацию.
Как выглядят результаты
Запустите полный скрипт командой python zillow_scraper.py, и вы получите чистую структурированную запись по каждому объекту, готовую для анализа, базы данных или таблицы.
[ { "link": "https://www.zillow.com/homedetails/1008-Fairmont-St-NW-Washington-DC-20001/473889_zpid/", "price": "$850,000", "address": "1008 Fairmont St NW, Washington, DC 20001", "beds": "3", "baths": "4", "size": "1,801", "type": "Townhouse" }, { "link": "https://www.zillow.com/homedetails/1438-Meridian-Pl-NW-APT-106-Washington-DC-20010/467942_zpid/", "price": "$385,000", "address": "1438 Meridian Pl NW APT 106, Washington, DC 20010", "beds": "2", "baths": "2", "size": "634", "type": "Condominium" } ]
Соответствующий CSV содержит те же столбцы, по одной строке на объект, что позволяет напрямую загрузить его в pandas или любую таблицу для фильтрации по ценовому диапазону, количеству спален или типу объекта.
Поддержание доступности при масштабировании
Даже при наличии обработки рендеринга Zillow следит за трафиком, похожим на работу скраперов. Несколько привычек помогают поддерживать более длительный прогон в рабочем состоянии, и они применимы к любой сложной коммерческой цели.
- Задавайте темп запросов. Обработка объявлений в плотном цикле является самым быстрым способом попасть под ограничение или вызов. Увеличьте паузы для больших задач и варьируйте цели вместо того, чтобы обходить один путь на полной скорости.
- Используйте ротацию. Пул резидентских IP распределяет запросы по многим реальным пользовательским адресам, поэтому ни один из них не превышает ограничение скорости. Crawling API обрабатывает это за вас; если вы создаёте собственный стек, именно это нужно сделать правильно.
-
Читайте коды статусов. Прогон, который начинает возвращать значения
cb_status, отличные от 200, сигнализирует, что текущая скорость или уровень IP больше не подходят. Воспринимайте это как сигнал к снижению нагрузки, а не как помеху.
Для более крупных обходов асинхронный Crawler ставит запросы в очередь и доставляет результаты на вебхук, что подходит для выполнения многих поисковых страниц без удержания открытых соединений. Более широкое руководство по работе можно найти в статье как парсить сайты, не попадая в блокировку. Если вы хотите сравнить рыночные данные по нескольким порталам, тот же подход применим к парсингу Redfin, Realtor.com и Trulia.
Законно ли парсить Zillow?
Допустимость парсинга Zillow зависит от условий использования Zillow, вашей юрисдикции и того, что вы делаете с данными. Условия использования Zillow ограничивают автоматизированный доступ и сбор данных, поэтому парсинг может противоречить этим условиям вне зависимости от тщательности вашего инструментария. Zillow также исторически склонен к судебным разбирательствам по вопросам парсинга, преследовав стороны, собиравшие его объявления в большом масштабе, поэтому это не гипотетический риск. Ни один из кодов здесь не меняет этого; он лишь обеспечивает работоспособность технической части. Прочитайте Условия использования Zillow и его robots.txt и рассматривайте оба документа как границу того, что можно собирать.
Несколько правил, которых стоит придерживаться. Собирайте только публичные данные объявлений: запрашиваемую цену, количество спален и ванных комнат, площадь, тип объекта и адрес, которые доступны любому без аккаунта. Избегайте всего, что связано с идентифицируемыми физическими лицами, включая контактные данные агентов, владельцев или других людей, указанных на странице, поскольку это персональные данные, выходящие за рамки публичных объявлений. Соблюдайте ожидания Zillow относительно скорости запросов и поддерживайте объём запросов достаточно низким, чтобы не перегружать серверы. Помните также, что большая часть базовых данных об объектах и продажах на порталах недвижимости поступает из MLS-фидов, которые, как правило, являются лицензированными и накладывают собственные ограничения на повторное распространение, поэтому их сбор не даёт вам права на публикацию.
Данное руководство намеренно ограничено публичными страницами объявлений, поскольку именно эта граница делает работу защищённой. Оно не охватывает ничего за логином, данные сохранённых поисков или аккаунтов, личные или контактные данные физических лиц или любые попытки обойти аутентификацию. Только публичные данные объявлений. Если вашему проекту нужно больше, правильный путь, лицензионное соглашение: Zillow предлагает официальные API и партнёрские программы для разрешённых случаев использования, а лицензированные MLS или поставщики данных о недвижимости охватывают остальное. Это правильный путь для коммерческого или объёмного использования, а не более умный скрапер.
Ключевые выводы
- Zillow использует клиентский рендеринг. Обычный запрос возвращает тонкую оболочку лишь с частью результатов, поэтому необходимо рендерить страницу перед парсингом.
-
Требуются рендеринг и надёжный IP вместе. Crawling API с JS-токеном обеспечивает оба в одном вызове;
ajax_waitиpage_waitуправляют временем ожидания контента. -
Работайте в два уровня. Собирайте ссылки на объекты с каждой поисковой страницы с помощью селектора
property-card-link, затем получайте и парсите каждое объявление на цену, количество спален, ванных комнат, площадь, адрес и тип. -
Используйте пагинацию и экспортируйте данные. Обходите страницы Zillow
{pageNo}_pдо потолка, задавайте темп прогона с короткими паузами и записывайте записи в JSON и CSV. - Оставайтесь в рамках публичных данных. Соблюдайте ToS и robots.txt Zillow, учтите, что он судился из-за парсинга и что данные MLS часто лицензированы, и никогда не прикасайтесь к логинам, аккаунтам или персональным данным физических лиц.
Часто задаваемые вопросы
Почему обычный запрос возвращает только часть результатов Zillow?
Потому что Zillow загружает результаты поиска и детали объявлений на стороне клиента с помощью JavaScript и Ajax. Исходный HTML является оболочкой, которая заполняется только после выполнения скриптов страницы в браузере, поэтому необработанный HTTP-запрос возвращает статус 200, но большинство карточек и полей объявлений отсутствуют. Чтобы получить полный набор, необходимо сначала отрендерить страницу, что и обеспечивает JS-токен Crawling API.
Нужен ли обычный токен или JS-токен для Zillow?
JS-токен. Обычный токен получает статический HTML, который на Zillow является той же тонкой оболочкой, что возвращает обычный запрос. JS-токен рендерит страницу в настоящем браузере перед возвратом HTML, поэтому карточки поиска и поля объявлений присутствуют при парсинге BeautifulSoup.
Какие данные можно парсить со страницы объявления Zillow?
Публичные поля объявления: запрашиваемую цену, количество спален и ванных комнат, площадь, тип объекта, почтовый адрес и ссылку на объявление. Придерживайтесь данных, видимых любому посетителю без аккаунта, и избегайте личных контактных данных агентов или владельцев, которые выходят за рамки публичных объявлений, рассматриваемых в данном руководстве.
Мои селекторы возвращают None. Что изменилось?
Скорее всего, разметка Zillow. Сгенерированные имена классов и атрибуты data-test (префикс ListItem-, блок macro-data-view, контейнер bed-bath-sqft-facts) меняются без предупреждения, поэтому селекторы, работавшие в прошлом месяце, могут сломаться. Заново проверьте живую страницу в инструментах разработчика браузера и обновите селекторы. Периодическое обслуживание селекторов является нормальным для любого производственного скрапера.
Как обрабатывать пагинацию по объявлениям локации?
Zillow добавляет сегмент {pageNo}_p к поисковому пути. Получите первую страницу для чтения общего количества страниц из навигации пагинации, ограничьте обход потолком max_pages, затем обойдите каждую страницу, собирая ссылки на объекты. Функция collect_all_urls выше показывает полный цикл с коротким ожиданием между страницами.
Можно ли использовать спарсенные данные Zillow в коммерческих целях?
Рассматривайте это как юридический вопрос, а не технический. Большая часть данных об объектах Zillow поступает из лицензированных MLS-фидов с собственными условиями повторного распространения, а собственные Условия использования Zillow ограничивают повторное использование, поэтому коммерческое или объёмное использование, как правило, требует разрешения. Изучите условия, рассмотрите официальный API или партнёрскую программу Zillow и проконсультируйтесь с юристом перед созданием продукта на основе этих данных.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
