Вход Регистрация

Кейсы

Как найти все URL-адреса на домене: сканирование сайта и составление списка всех страниц

Разбираем 4 способа собрать все URL домена: XML-карта сайта, SEO-инструменты, поиск Google и Python-скрипт. Логика обхода, фильтрация ссылок и работа с прокси.

Команда Froxy 26 мая 2026 7 мин
Как найти все URL-адреса на домене: сканирование сайта и составление списка всех страниц

Масштабный парсинг — это не просто извлечение данных из HTML-кода. Это процесс, чем-то напоминающий движение по улицам и домам. Чтобы исследовать всю местность, вам нужно пройтись по каждой улице (категории и разделу целевого сайта) и заглянуть в каждый двор (на отдельные страницы). Но где взять список всех URL-адресов сайта и как потом получить содержимое всех страниц?

В этом материале подробно рассказываем о том, какие существуют подходы для поиска всех ссылок сайта, а также о том, как можно организовать их правильный обход.

Лучшие способы найти все URL-адреса на одном домене

Почему только на одном домене? Дело в том, что на страницах целевого сайта могут содержаться ссылки на внешние ресурсы. И если вы не ограничите получение всех страниц сайта доменом, то парсер может войти в рекурсию: он будет находить всё время новые URL, уже на новых сайтах, ставить их в очередь на извлечение, снова находить на них ссылки, и так до бесконечности, а точнее до тех пор, пока скрипт не обойдёт весь Интернет. И то, при условии, что вы запретите ему посещение найденных адресов дважды.

К слову, существуют готовые инструменты, которые реализуют задачу парсинга сайта для получения списка всех его URL, такие как Screaming Frog.

Но в контексте нашей статьи нас больше интересует логика формирования очереди парсинга. Это фундаментальная задача при построении любых скрейперов. Жалко, что она имеет множество решений, и ни одно из них нельзя назвать самым лучшим.

Итак, как вы можете найти все URL с конкретного сайта:

Способ 1. Обратиться к XML-картам сайта (файлы sitemap.xml)

Парсер обращается напрямую к карте сайта и извлекает из неё форматированные данные. Такие карты движок сайта в норме формирует автоматически — специально для поисковых ботов (пауков). Формат хранимой информации строго определён — это XML-разметка. Казалось бы — It's easy!

Но не всё может быть так гладко:

  • Карты сайта может не быть.
  • В карте может быть устаревшая информация, особенно если она не формируется автоматически.
  • В карте может не быть важных разделов и страниц.
  • Сама карта может быть разбита на несколько файлов — под разные разделы и типы материалов.

Чтобы не напороться на санкции и блокировки, нужно дополнительно изучить правила для ботов, изложенные в директивах robots.txt.

Способ 2. Использовать готовые профильные инструменты и программы

Основная идея такая — указать нужный целевой домен в программе или в онлайн-сервисе, профильный инструмент сам обойдёт страницы, а на выходе отдаст только итоговый список всех URL сайта в удобном формате, например, в CSV или XML. Уже с ним сможет работать ваш парсер. К нишевым инструментам можно отнести такие решения, как Netpeak Spider, Screaming Frog, Ahrefs, Semrush и пр.

Выглядит идеально. Но есть нюансы:

  • Многие программные SEO-краулеры распространяются платно, как и аналогичные SEO-сервисы.
  • Stand-alone программы могут не работать с определёнными типами сайтов. А ещё для них могут потребоваться качественные ротируемые прокси, чтобы ваш IP не забанили после нескольких автоматических запросов. Всё из-за того, что многие современные сайты не спарсить без headless-браузеров.
  • Пока программа не закончит сбор URL, вы не сможете взять их в дальнейшую работу. А если на целевом сайте десятки тысяч страниц, то вам придётся ждать ОЧЕНЬ долго.
  • Внешние сервисы и программы тяжело интегрируются с самописными скриптами автоматизации.

Способ 3. Обратиться к поиску Google

Вместо Google может выступать любой другой поисковик. Но Google традиционно имеет больший охват, а значит данные в нём с большей вероятностью будут актуальными. Логика простая — вы используете для поиска специальные операторы, например, site:тут-целевой-домен.com, и поисковик возвращает вам список всех страниц, которые значатся в его индексе. Хитро и даже вполне жизнеспособно для определённых ситуаций, например, когда не получается обойти антибот-защиту на нужном сайте.

Но снова есть нюансы:

  • В Google вы сможете получить только те страницы, о которых «знает» поисковик. Покрытие никогда не бывает 100%-ным, так как часть страниц и разделов для поисковика могут оставаться недоступными.
  • Глубина поиска Google не является бесконечной. А если учесть, что с недавних пор в поисковой выдаче больше нельзя настроить показ 100 результатов, то вы сможете собрать лишь малую часть из существующих URL одного домена.
  • Выдача поисковика давно персонализируется, поэтому результаты могут быть условно «хаотичными» — с разных IP и локаций можно получать уникальные списки страниц по одному домену.
  • Google тоже защищается от парсеров и может периодически показывать капчу или блокировать отдельные IP-адреса.

Смотрите также: Как парсить SERP Google

Способ 4. Найти все URL на домене самописным скриптом

Все перечисленные выше проблемы и недостатки можно решить, если написать такой скрипт, который будет извлекать URL-адреса и обходить страницы по нужной вам логике: в закрытых и открытых разделах, с учётом директив robots.txt или без, с фильтрацией неподходящих адресов и т.д.

Но и здесь есть свои подводные камни:

  • Этот скрипт нужно написать. Без знаний программирования вы даже не сможете адаптировать готовые решения под свои задачи.
  • На страницах сайта могут быть ловушки (honeypots), а сам сайт может защищаться веб-файрволлом. Например, WAF от Cloudflare.
  • Сами страницы могут формироваться динамически. Поэтому парсинг потребует сложных и ресурсоёмких решений, например, на основе антидетектов или headless-браузеров.
  • Важно сохранять прогресс или статус парсинга, чтобы можно было вернуться к точке, в которой вы получили ошибку. И не начинать всё заново.
  • Всегда существует вероятность комбинаторного взрыва — это когда внутри URL-адресов используются дополнительные параметры, которые не несут полезной нагрузки. Но из-за них URL получаются уникальными, соответственно, итоговый список может разрастаться до бесконечности, заполняясь техническими дублями.
  • В список URL-адресов могут попадать не только сами страницы, но и все дополнительные файлы — CSS-стили, JS-скрипты, картинки, документы и пр.

Как просканировать сайт на наличие всех URL-адресов

Как просканировать сайт на наличие всех URL-адресов

Здесь опишем то, как должна выглядеть общая логика парсера, который ищет все URL на одном домене:

Определение начальных точек (точек входа)

В качестве стартового адреса обычно используется главная страница (домен) или ссылка на карту сайта (обычно это что-то вида домен.com/sitemap.xml). Но в определённых ситуациях могут быть актуальны списки опорных категорий или маски (RegExp-выражения). Это позволяет сразу задать структуру обхода и не начинать с одной-единственной точки.

Инициализация очереди обхода

Скрипт должен получить HTML-содержимое стартовой страницы или страниц, чтобы собрать с них следующие URL-адреса. Именно так формируется очередь, на основе которой ищутся все URL домена. Наиболее распространённая логика обхода — FIFO (первый пришёл, первый ушёл, то есть пошёл на парсинг). Но могут быть и другие подходы: последовательный обход категорий, рандомизация и т.п.

Создание структуры для хранения прогресса и посещённых URL

Чтобы сохранить прогресс и статус парсинга отдельных страниц, создаётся структура для хранения данных и признаков посещённых URL. Это может быть хеш-таблица или полноценная база данных с задействованием механизма полей.

Исключение внешних ссылок и нормализация URL

Технически никто не запрещает вам искать URL, ведущие на другие сайты (имеющие сторонние домены), но включать их в обход нельзя — так вы не сможете остановиться никогда. А ещё в базу будут попадать ненужные (мусорные) URL — ссылки на скрипты, картинки, CSS-таблицы, фильтры с параметрами и т.п. Чтобы исключить их из базы, нужна настройка правил фильтрации.

Наиболее логичные правила:

  • В очередь на обход или в базу заносятся только URL, начинающиеся с целевого домена. С учётом или без учёта поддоменов — тут уже по желанию и в зависимости от структуры сайта.
  • Найденные URL должны приводиться к каноническому виду — удаляются якоря (после символа «#») и параметры (они объединяются знаком «&», а сами параметры передаются после «?»). В некоторых случаях нужно добавлять или удалять завершающие слеши «/», чтобы привести URL к одному виду. Логично также очистить страницы фильтров. Правила фильтрации чаще всего задаются конструкциями ?sort=, ?filter= и т.п.
  • Из списка на обход удаляются ненужные паттерны — URL-адреса скриптов, картинок, CSS, документов и т.п.

Первая полноценная итерация обхода

Здесь вам нужно описать логику парсинга отдельно взятой страницы — это ваш основной цикл. В норме: берётся URL из очереди, который не имеет признака «посещён», к этому URL выполняется HTTP-запрос, из тела страницы извлекается весь HTML-код, внутри кода ищутся все URL, они очищаются и нормализуются на основе требований и правил, описанных в 4 шаге, проверяются на наличие в базе. Если ссылки новые, то они попадают в очередь на обход.

Цикл завершается только тогда, когда он не может найти новые страницы

Здесь «новые» — это те страницы, которые не имеют признака «посещён».

Даже если скрипт остановить или прервать, на основе системы статусов обхода процесс всегда можно возобновить с того места, где он остановился.

Резидентные прокси

Идеальные прокси-серверы для доступа к ценным данным со всего мира.

Попробовать триал $1.99, 100Mb

На что ещё можно обратить внимание:

  • Нагрузка. При большом объёме страниц важно соблюдать баланс между глубиной и шириной обхода. Следует правильно продумать архитектуру многопоточности и скорости обращений. Частоту запросов можно регулировать за счёт времени задержек. Но их не стоит делать одинаковыми, чтобы не вызывать подозрений у антибот-систем.
  • Запреты и ограничения. Подумайте про правила и логику обхода на основе директив robots.txt и политики использования. Парсинг должен быть этичным.
  • Обработка ошибок и нестандартных ответов. Стандартный ответ сервера — код 200. Но не все ссылки, найденные на страницах сайта, ведут на существующие объекты. Плюс антибот-система может в любой момент заблокировать ваши запросы или отдать страницы-заглушки. Чтобы обезопасить себя от таких проблем, нужно правильно обрабатывать коды ошибок и мониторить нестандартное содержимое страниц. Наиболее важные коды: 404 (страница не существует), 403 (доступ запрещён), 301/302 — это редиректы на правильные конечные URL и 500 (сервер недоступен).
  • Логика повторных попыток. Это блок кода, который позволяет быстро настраивать поведение парсера при обнаружении ошибок — когда сайт или сервер недоступен или когда вам заблокировали доступ. Например, можно задействовать ротацию прокси.
  • Логирование и мониторинг. При очень больших объёмах парсинга можно подумать о внедрении метрик и показателей, которые облегчат мониторинг прогресса: сколько URL уже обработано, сколько в очереди, сколько ошибок найдено и каких, текущая скорость парсинга и т.п.
  • Особая логика завершения обхода. Например, можно завершать работу парсера не только когда очередь пуста, но и когда достигнуты лимиты по времени или по глубине обхода, по количеству страниц и пр.

Типичные проблемы поиска всех URL на домене:

  • Зацикливание. Парсер будет ходить «по кругу».
  • Экспоненциальный рост количества URL из-за неправильной очистки параметров (или при полном отсутствии очистки).
  • Недоступность контента без JS-рендеринга. Вам буквально неоткуда будет брать список всех URL сайта, так как конечное содержимое не формируется без полноценного браузера.
  • Блокировки со стороны сайта. Чаще всего это баны IP-адресов, которые легко обходятся с помощью прокси.
  • Неэффективная логика обхода. Наиболее важные страницы могут всё время отодвигаться в конец списка.
  • Повышение сложности архитектуры при росте масштаба.

В общем-то, даже «простой» обход сайта может легко превратиться в задачу корпоративного уровня: с построением распределённых очередей, балансировкой нагрузки, дашбордами, графами и т.п.

Пример использования Python для поиска всех URL-адресов на домене

Пример простого скрипта, который обходит заданное количество страниц целевого сайта. Если указать очень большое число, то он обойдёт все URL:

import time
import os
import csv
import requests
from urllib.parse import urljoin, urlparse, urldefrag, parse_qs, urlencode
from bs4 import BeautifulSoup
import xml.etree.ElementTree as ET

# -----------------------------
# Конфигурация
# -----------------------------
START_URLS = ["https://example.com"]
ALLOWED_DOMAIN = "example.com"
STATE_FILE = "crawler_state.csv"   # можно поменять на .xml
SAVE_EVERY = 20                   # сохранять состояние каждые N URL
REQUEST_DELAY = 0.5
MAX_URLS = 1000
TIMEOUT = 10

BLACKLIST_PARAMS = {"utm_source", "utm_medium", "utm_campaign", "sort", "filter", "page"}
HEADERS = {
    "User-Agent": "Mozilla/5.0 (compatible; SimpleCrawler/1.1)"
}
# -----------------------------
# Нормализация URL
# -----------------------------
def normalize_url(url):
    url, _ = urldefrag(url)
    parsed = urlparse(url)
    if ALLOWED_DOMAIN not in parsed.netloc:
        return None
    query = parse_qs(parsed.query)
    filtered_query = {k: v for k, v in query.items() if k not in BLACKLIST_PARAMS}
    query_string = urlencode(filtered_query, doseq=True)
    normalized = parsed._replace(query=query_string).geturl()
    if normalized.endswith("/"):
        normalized = normalized[:-1]
    return normalized
# -----------------------------
# Извлечение ссылок
# -----------------------------
def extract_links(html, base_url):
    soup = BeautifulSoup(html, "html.parser")
    links = set()
    for tag in soup.find_all("a", href=True):
        full_url = urljoin(base_url, tag.get("href"))
        normalized = normalize_url(full_url)
        if normalized:
            links.add(normalized)
    return links
# -----------------------------
# Сохранение состояния (CSV)
# -----------------------------
def save_state_csv(queue, visited):
    with open(STATE_FILE, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["type", "url"])
        for url in visited:
            writer.writerow(["visited", url])
        for url in queue:
            writer.writerow(["queue", url])
def load_state_csv():
    queue = []
    visited = set()
    with open(STATE_FILE, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            if row["type"] == "visited":
                visited.add(row["url"])
            elif row["type"] == "queue":
                queue.append(row["url"])
    return queue, visited
# -----------------------------
# Сохранение состояния (XML)
# -----------------------------
def save_state_xml(queue, visited):
    root = ET.Element("crawler")
    visited_el = ET.SubElement(root, "visited")
    for url in visited:
        ET.SubElement(visited_el, "url").text = url
    queue_el = ET.SubElement(root, "queue")
    for url in queue:
        ET.SubElement(queue_el, "url").text = url
    tree = ET.ElementTree(root)
    tree.write(STATE_FILE, encoding="utf-8", xml_declaration=True)

def load_state_xml():
    tree = ET.parse(STATE_FILE)
    root = tree.getroot()
    visited = set()
    queue = []
    for url in root.find("visited"):
        visited.add(url.text)
    for url in root.find("queue"):
        queue.append(url.text)
    return queue, visited
# -----------------------------
# Универсальная загрузка/сохранение
# -----------------------------
def save_state(queue, visited):
    if STATE_FILE.endswith(".xml"):
        save_state_xml(queue, visited)
    else:
        save_state_csv(queue, visited)

def load_state():
    if not os.path.exists(STATE_FILE):
        return None, None
    if STATE_FILE.endswith(".xml"):
        return load_state_xml()
    else:
        return load_state_csv()
# -----------------------------
# Краулер
# -----------------------------
def crawl():
    queue, visited = load_state()
    if queue is None:
        print("[i] State file not found. Starting fresh.")
        queue = list(START_URLS)
        visited = set()
    else:
        print(f"[i] Loaded state. Queue: {len(queue)}, Visited: {len(visited)}")
    processed_since_save = 0
    while queue and len(visited) < MAX_URLS:
        url = queue.pop(0)
        if url in visited:
            continue
        print(f"[+] Visiting: {url}")
        visited.add(url)
        processed_since_save += 1
        try:
            response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
            if response.status_code != 200:
                continue
            links = extract_links(response.text, url)
            for link in links:
                if link not in visited and link not in queue:
                    queue.append(link)
        except requests.RequestException:
            continue
        # периодическое сохранение
        if processed_since_save >= SAVE_EVERY:
            print("[i] Saving state...")
            save_state(queue, visited)
            processed_since_save = 0
        time.sleep(REQUEST_DELAY)
    print("[i] Final save...")
    save_state(queue, visited)
    return visited

# -----------------------------
# Запуск
# -----------------------------
if __name__ == "__main__":
    urls = crawl()
    print(f"\nTotal URLs collected: {len(urls)}")

Скрипт работает без headless-браузера и использует анализатор Beautiful Soup. Если рядом со скриптом при запуске не существует файла с URL-адресами, то он создаёт его и наполняет с нуля. Если файл есть, то в нём подхватываются статусы посещения, чтобы процесс не начинался каждый раз сначала.

При желании вы можете задать свой целевой URL и ограничения, задержки и параметры, которые следует удалять из структуры адреса.

Заключение и рекомендации

Выше мы рассмотрели наиболее вероятные способы того, как проверить все страницы сайта и собрать итоговый список URL-адресов одного домена. Самый простой — использовать готовые решения. Но самый правильный и функциональный — написать свой скрипт парсинга.

Ни профильные инструменты, ни масштабные парсеры не смогут долго работать без качественных прокси. Froxy — это миллионы ротируемых прокси с оплатой за потребляемый трафик. Число параллельных потоков может быть до 1000. С нашей помощью вы сможете закрыть даже самые сложные задачи по сбору данных.

Получайте уведомления о новых функциях и обновлениях Froxy

Узнайте первыми о новых функциях Froxy, чтобы оставаться в курсе событий происходящих на рынке цифровых технологий и получать новости о новых функциях Froxy.

Статьи по Теме