Парсинг сайта с разной вложенностью категорий: как найти URL товаров

    При парсинге интернет-магазинов часто встречается неоднородная структура: где-то товары лежат на 3-м уровне вложенности, а где-то - сразу в корневой категории. Разберём, как решить эту задачу на Python, используя requests и BeautifulSoup. Мы рассмотрим два подхода: обход sitemap и рекурсивный парсинг категорий.

    Почему стандартный подход не работает?

    Если сайт имеет структуру «Категория → Субкатегория → Субсубкатегория → Товар», но в некоторых разделах субкатегорий нет, то просто пройтись по всем ссылкам на странице - неэффективно. Вы либо пропустите товары, либо загрузите лишние страницы. Проблема усугубляется, если sitemap.xml содержит рекурсивные ссылки на страницы категорий, а не на конечные товары.

    Как найти sitemap и проверить его содержимое

    Для начала проверьте robots.txt по адресу https://example.com/robots.txt. Там может быть указан путь к sitemap. Однако, как в вашем случае, sitemap может содержать не прямые ссылки на товары, а ссылки на страницы категорий. Тогда нужно либо парсить каждую страницу из sitemap рекурсивно, либо использовать другой подход.

    Рекурсивный обход категорий: пошаговая инструкция

    Вот простой алгоритм на Python, который обходит все внутренние ссылки на странице и собирает только те, что ведут на товары (например, содержат /product/ или /item/):

    import requests
    from bs4 import BeautifulSoup
    from urllib.parse import urljoin
    
    def get_all_links(url):
        response = requests.get(url)
        soup = BeautifulSoup(response.text, 'html.parser')
        links = []
        for a in soup.find_all('a', href=True):
            href = urljoin(url, a['href'])
            if href.startswith('https://example.com'):
                links.append(href)
        return links
    
    # Начинаем с главной страницы
    start_url = 'https://spoilerstalenergo-96.ru'
    to_visit = [start_url]
    visited = set()
    products = []
    
    while to_visit:
        current = to_visit.pop()
        if current in visited:
            continue
        visited.add(current)
        print(f'Обрабатываю: {current}')
        try:
            links = get_all_links(current)
            for link in links:
                if '/product/' in link:  # замените на свой шаблон
                    products.append(link)
                elif link not in visited:
                    to_visit.append(link)
        except Exception as e:
            print(f'Ошибка: {e}')
    
    print(f'Найдено товаров: {len(products)}')
    

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

    Что делать, если sitemap содержит рекурсивные ссылки?

    Если sitemap.xml ведёт на страницы категорий, а те, в свою очередь, снова ссылаются на sitemap, попробуйте проигнорировать sitemap и использовать прямой обход с ограничением глубины. Например, установите максимальную глубину рекурсии (скажем, 5 уровней) и проверяйте, не зацикливается ли обход.

    Проверка на дубликаты

    Добавьте проверку, чтобы не посещать одни и те же страницы дважды. Используйте set() для хранения посещённых URL.

    Альтернативные методы: XPath и CSS-селекторы

    Если структура HTML однотипна, можно сразу извлекать ссылки на товары по CSS-классу (например, .product-item a). Это быстрее рекурсивного обхода. Пример:

    soup.select('.product-item a[href]')
    

    Такой подход подходит, если на странице категории товары уже отображены списком.

    Заключение

    Парсинг сайтов с неоднородной структурой требует гибкого подхода. Начните с проверки sitemap, затем используйте рекурсивный обход с ограничением глубины и фильтрацией по паттерну URL. Если сайт динамический (JavaScript), добавьте Selenium или Playwright. Главное - не забывайте про robots.txt и соблюдайте этикет парсинга.

    Часто задаваемые вопросы