3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер
3 горизонтальные линии, бургер

3 горизонтальные линии, бургер
Удалить все
ЗАГРУЗКА ...

Содержание



    Парсинг динамических сайтов - способы и наглядные примеры

    Часы
    18.08.2026
    /
    Часы
    18.08.2026
    /
    Часы
    9 минут
    Глазик
    19
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0

    Введение про типы сайтов и их различие

    Раньше, во времена когда меня ещё не было даже в планах и после, во времена моего детства - все сайты были статическими. То есть состояли только из HTML, CSS и небольших вставок JavaScript кода. Тогда, если ты и получал страницу, то ты знал, что это всё что может предложить сайт (в плане содержимого).
    Статический сайт — это сайт, который состоит из заранее созданных файлов (HTML, CSS и JavaScript). Все страницы хранятся на сервере в готовом виде. Текст и картинки на таком сайте одинаковы для всех посетителей, а сервер не собирает страницы заново при каждом заходе.
    Парсить такие сайты одно удовольствие, только и знай что тебе нужно парсить и где это находится. Сейчас же, в сети большинство сайтов динамические.
    Так согласно данным от w3techs, 69.1% - всех вебсайтов используют CMS (да сайты с CMS могут считаться динамическими). И это только CMS, разумеется количество динамических сайтов гораздо больше, учитывая что сейчас так же сильно популярны различные Frontend-фреймворки, которые по своей сути делают только динамические сайты. Кстати, мой сайт тоже считается динамическим.
    Что значит динамический сайт? Это значит, что страницу которую ты видишь у себя в браузере не хранится отдельно где-то на сервере, а собирается на лету и выдаётся по готовности. То есть сайт не обязан пользоваться JS, чтобы стать динамическим, достаточно факта сборки этой страницы.
    Динамический сайт — это сайт, содержимое которого меняется и собирается заново для каждого пользователя в реальном времени. Сайт может использовать базу данных, шаблоны или специальный программный код, чтобы показывать страницу в зависимости от человека, его действий или времени суток.
    Парсить такие сайты заметно тяжелее, ибо предполагают:
    1. Либо точные тайминги - нельзя сразу скачивать страницу, только после того, как необходимое содержимое появится на странице
    2. Либо рендеринг JS - иногда требуется прямое взаимодействие с сайтом через клик, скролл или фокус на элементе, чтобы подгрузился необходимый контент, а грузится он через JS.

    Подготовка, что, у кого и как будем парсить

    В качестве примера парсинга, я буду использовать свой сайт. Будем парсить карточки статей (Заголовок, Ссылку и Описание).
    Мой сайт использует пагинатор и динамическую подгрузку по достижению определённой высоты. Так же, хоть и не всегда, сайты могут обновлять текущий URL при загрузке нового контента, как мой. Например, поменять номер текущей страницы или добавить фильтр.
    Всего существует 4 способа парсинга, а вернее получения необходимых данных с такого рода сайтов:
    1. Эмуляция браузеров и последующий рендеринг JS-скриптов
    2. Эмуляция браузера напрямую, через драйверы - Selenium
    3. Эмуляция браузера через специальные программный API - Playwright / Puppeteer / Cypress
    2. Реверс-инжиниринг сайта и отправляемых запросов
    1. Использование открытого, специально-разработанного API для сторонних парсеров
    2. Нахождение и использование скрытого API, который не предусмотрен разработчиком
    4. Получить доступ на прямую к базам данных и сайту (то есть хакнуть цель)
    Я разберу только первые 3, ибо 4-й незаконен. Не взламывайте чужие сайты, пожалуйста.
    Так мы напишем парсере, которые будут делать почти одно и тоже, но поразному и используя разный технологический стек. Так же рассмотрю преимущества и недостатки каждого из этих способов.
    Во всех последующих главах я сосредоточусь только на получении доступа к необходимому материалу для скрейпинга, то есть к HTML-страницам, без вдавания в подробности того, как извлекать данные из самих страниц при помощи BeautifulSoup.
    Так же я буду писать максимально простой и понятный код, без оптимизационных извращений и приблуд по типу мультипоточности, кеширования или ротации. Всё это само собой, используется при парсинге на реальных проектах, но для учебных статей это лишнее.
    А начнём мы с первого пункта и первого подпункта - то есть через Selenium

    Парсинг динамических сайтов разными способами

    Парсинг сайта используя Selenium

    Основным преимуществом парсинга сайта через Selenium является то, что он поддерживает огромное количество браузеров таких как: Chrome, Edge, Firefox, Safari и InternetExplorer. И не меньшее количество языков программирования: Python, Kotlin, JS, Java, C# и Ruby
    Но есть один существенный недостаток, он очень медленный из-за того, что всё взаимодействие происходит через специальные драйверы, которые и эмулируют поведение браузера.
    Этот парсер я буду писать на Python, соответственно и всё остальное окружение будет строиться вокруг данного языка. Создание директории и виртуальное окружение проекта, установка необходимых пакетов:
    mkdir dynamic-scraper; cd dynamic-scraper; python -m venv .venv; source .venv/bin/activate; pip install selenium;
    После того как была создана директория для проекта и всё настроено, добавим главный файл скрипта. В нём мы запустим веб-драйвер, сделаем первый запрос на первую страницу и сохраним её.
    from selenium import webdriver from selenium.webdriver.firefox.options import Options # Функция для сохраниения полученного HTML-кода def save_to_html(path, text): with open(path, 'w', encoding='utf-8') as file: file.write(text) file.close() TARGET_URL = "https://timthewebmaster.com/en/articles/" def run(): # Задаём опции работы тестового браузера options = Options() # Без графического интерфейса options.add_argument('--headless') driver = webdriver.Firefox(options=options) # Получаем саму страницу driver.get(TARGET_URL) # Сохраняем её save_to_html('index.html', driver.page_source) if __name__ == "__main__": run()
    В общем пока что без изменений, то есть мы могли бы получить точно такую же страницу и без Selenium, но не больше. Ведь у нас ещё есть 15 страниц, которые мы не сможем получить кроме как через Selenium.
    Чтобы спарсить все доступные страницы, нам нужно либо прокликать все 15 кнопок, либо пролистать вниз до конца. Сначала посмотрим как это будет выглядеть если парсить через нажатие по кнопкам:
    from selenium import webdriver from selenium.webdriver.firefox.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions def save_to_html(path, text): with open(path, 'w', encoding='utf-8') as file: file.write(text) file.close() TARGET_URL = "https://timthewebmaster.com/en/articles/" NEXT_PAGE_BUTTON_ID = "next_pagin_button" LAST_BUTTON_ID = "paginator_last_page_example" NEXT_LOCATOR = "scroll-sentinel-" def run(): # Настраиваем тестовый браузер options = Options() options.add_argument('--headless') driver = webdriver.Firefox(options=options) # Заходим на сайт driver.get(TARGET_URL) # Находим количество доступных страниц pages = int(driver.find_element(By.ID, "paginator_body").find_element(By.ID, LAST_BUTTON_ID).get_attribute("data-page")) for page in range(2, pages): # Находим и нажимаем на кнопку next_page_button = driver.find_element(By.ID, NEXT_PAGE_BUTTON_ID) next_page_button.click() # Ждём пока не подгрузится необходимый контент WebDriverWait(driver, 10).until( expected_conditions.presence_of_element_located((By.ID, f"{NEXT_LOCATOR}{page+1}")) ) print(f"STATUS: On {page} page") # Так как последний анкорный тег не возвращается нужно ещё раз кликнуть next_page_button = driver.find_element(By.ID, NEXT_PAGE_BUTTON_ID) next_page_button.click() # Сохраняем страницу (т.к. новые статьи добавляются на ту же страницу, можно скачать её всего лишь один раз) save_to_html(f'index.html', driver.page_source) if __name__ == "__main__": run()
    Изначально мы можем не знать сколько нам доступно страниц, поэтому после того как мы зашли на сайт смотрим на количество доступных страниц на пагинаторе.
    После создаём цикл и прокликиваем условную кнопку "Дальше", чтобы получить следующую страницу.
    Но как узнать загрузилась ли страница или ещё грузиться? Есть множество вариантов если ты пользуешься Selenium, это либо указать до секунд сколько ты готов ждать, либо ждать появления, отсутствия или видимости конкретного элемента на странице, то есть использовать особый конструкт с WebDriverWait.
    Почему я сохранил страницу только один раз? Тут вообще всё зависит от конкретного сайта. В моём случае, мой сайт просто вставляет статьи на ту же страницу если двигаться вперёд. Некоторые при переходе на новую страницу, перезагружают всю страницу и в таком случае потребовалось бы сохранять каждую страницу.
    Без --headless флага, выглядеть это будет как-то так:

    Парсинг сайта используя Playwright

    Хотя Playwright, поддерживает работу с Python, я всё-таки предпочитаю работать с ним как npm-проект. В отличие от Selenium, он гораздо быстрее. Но поддерживает не все браузеры, только: Chromium(Google Chrome, Edge), WebKit(Safari), Gecko(Firefox) и их деривативы.
    Но на самом деле для парсера не имеет значения через какой браузер парсить. Парсеру нужно только получить желаемую HTML-страницу, а дальше дело техники ...
    Так после установки playwright, чтобы спарсить все доступные статьи со страницы пагинации, можно написать следующий скрипт:
    import { test } from '@playwright/test' import { writeFileSync } from 'fs'; let URL = "https://timthewebmaster.com/en/articles/" function saveToJson(data, filename){ const articlesArray = Array.from(data.values()); writeFileSync(filename, JSON.stringify(articlesArray, null, 2), 'utf-8'); } test('main', async ({ page }) => { let collectedArticles = new Map(); await page.goto(URL); // Находим количество доступных страниц let pages = parseInt(await page.locator('#paginator_last_page_example:not([data-page=""])').innerText()) // Прокликиваем страницы пагинации for (let i = 0; i < pages; i++){ console.log(`page=${i}`) await page.locator('#next_pagin_button').click() // Ждём пока этот элемент не загрузится и будет виден if (i < pages - 2) await page.locator(`#scroll-sentinel-${i+3}`).waitFor({ state: 'attached' }); } // Собираем все прогрузившиеся статьи const articles = page.locator('#page>div:not(.scroll-sentinel)'); const currentCount = await articles.count(); for (let j = 0; j < currentCount; j++){ const article = articles.nth(j); // Извлекаем уникальный маркер (например, ссылку или заголовок) const title = await article.locator('h2').innerText(); const link = await article.locator('h2>a').getAttribute('href'); collectedArticles.set(`article-${j}`, {"title": title, "link": link}) } saveToJson(collectedArticles, 'results.json') });
    Данный парсер прокликивает кнопку "Дальше" и лишь в конце парсит всю прогрузившуюся страницу. Можно это было делать и в цикле, если бы пагинатор этого сайта просто подменял одни статьи на другие. Но так как он просто добавляет новые статьи к существующим то будет вот так.
    Парсинг такой страницы без многопоточного кода займёт где-то 10-30 секунд. По умолчанию, любой тест в playwright длиться только 30 секунд поэтому не забудь снять ограничение на время проведения теста. timeout: 0 в playwright.config.js
    В парсинге динамических сайтов главное усвоить такое правило. Всё требует времени, особенно если это касается пользовательского интерфейса, поэтому ожидания и обработка событий по готовности тех или иных элементов это наше всё.

    Парсинг сайта используя существующий API

    Это наверное самый надёжный и быстрый способ сбора данных с целевого сайта. Хотя это довольно большая редкость и занимаются этим либо крупные сайты по типу Google, либо очень маленькие сайты энтузиастов и гиков вроде меня :)
    Да, если ты не знал то Гугл официально позволяет парсить поисковую выдачу и многое другое, и условно бесплатно. Так у меня есть целая статья посвящённая парсингу SERP выдаче от Google.
    Обычно о существовании такого API, говорят прямо. Ведь это должно по идее снизить нагрузку на сервер, ведь что проще? Запрашивать 1000 HTML-страниц сайта целиком, или 100 лёгких JSON-файлов, которые уже содержат всю существующую информацию просто бери и копируй.
    Вот и на моём сайте есть открытый API находящийся по вот этому адресу: https://timthewebmaster.com/api/v1/public/
    Так, поигравшись немного можно выяснить что чтобы получить все необходимые статьи можно создать GET-запрос на адрес https://timthewebmaster.com/api/v1/public/article/?page=1&page_size=100&format=json и получить все статьи разом. В скрипте это будет выглядеть вот так:
    import json import requests from bs4 import BeautifulSoup TARGET_URL = "https://timthewebmaster.com/api/v1/public/article/?page=1&page_size=10&format=json" def save_to_json(path, list): with open(path, 'w', encoding='utf-8') as file: json.dump(list, file, indent=4, ensure_ascii=False) def run(): # Делаем инициирующий запрос response = requests.get(TARGET_URL) all_data = json.loads(response.text) data = all_data # Обходим все while(data['next']): print(f"PAGE: {data['next']}") response = requests.get(data['next']) data = json.loads(response.text) # Сохраняем результат в один общий список all_data['results'].append(data['results']) save_to_json('data.json', all_data) # Дальше логика группировки и дальнейшей обработки данных # ... if __name__ == "__main__": run()
    Потом остаётся пройтись по полученому списку и отобрать только те данные, которые нужны. Когда такой API есть это очень сильно упрощает работу, сводя её по факту к сортировке и упаковке в более удобные и нужные форматы для заказчика.
    Но опять же такой вариант очень редко встречается. Его либо не будет вообще, либо такой API будет не явным. О том, как отыскать такой API и будет следующая глава.

    Парсинг сайта используя скрытый API

    Скрипт будет идентичен предыдущему. Разница лишь в том, чтобы найти такой АПИ. А чтобы обнаружить такой API можно использовать консоль разработчика. В консоли зайди на вкладку Сети/Network и выбери XHR и JS. Дальше нужно повзаимодействовать со страницей (кликнуть по кнопке, навестись мышкой, проскролить вниз) и следить за перехвачеными запросами.
    Данные POST-запросы, вернее ответы на них, сервер может выдавать в разных форматах. Как обычный кусок HTML-кода, так и полноценный JSON файл. Всё зависит от конкретного сайта. В моём же случае возвращается отрендеренный HTML-фрагмент, который потом придётся парсить через BeautifulSoup или другие парсеры.
    Так же, неформально сам адрес может быть скрытым API, который позволит спарсить все статьи с сайта.

    Парсинг сайта через расширение для браузеров

    Данный способ отлично подходит если нужно спарсить данные с сайтов под любой, даже самой крутой защитой, быстро и прямо сейчас, но заходить на такие сайты придётся реальному пользователю. Можно взять ради примера моё недавнее расширение для парсинга карточек с Avito. Этот сайт считается очень тяжёлым для парсинга, но использовав это расширение можно получить хоть и полуатоматический парсер, зато работающий.
    Конечно же это полностью негилирует особенность любого парсинга в его автоматизации. Но всё же, такое тоже имеет место быть.
    Да, ты можешь начать извращаться и устанавливать такие расширения на Selenium или Playwright, но толку от этого будет не много. В обходе защитных механизмов от ботов это не сильно поможет.
    Единственным плюсом в таком применении расширений, может быть то, что я называю "свопингом тулсета". То есть тебе удобно пробиваться через защиту например используя Python/Selenium, но парсить и собирать данные используя JS.

    Заключение

    Вот так вот можно парсить динамические сайты. То есть есть два путя либо имитировать поведение браузера и рендерить JS, либо искать скрытый (и не очень) АПИ этих сайтов. Конечно же и быстрее, и дешевле, и проще будет парсинг именно через такие вот АПИ, но иногда создатели сайтов сильно запариваются и делают всё возможное чтобы их рессурс не парсили.
    Их можно понять, непонятно кто и зачем делает сотни и сотни запросов к веб рессурсу нагружая тем самым его и иногда даже перегружая.
    По этому перед тем как начать парсинг сайта убедись в том, что не будешь слишком сильно и заметно долбить по их серверам. И соберёшь лишь самое необходимое. А если нужно потренироваться, то милости прошу долбить мой сайт :) Хорошего дня.

    Не забудь поделиться, лайкнуть и оставить комментарий)

    Комментарии

    (0)

    captcha
    Отправить
    ЗАГРУЗКА ...
    Сейчас тут пусто. Буть первым (o゚v゚)ノ

    Другое

    Похожие статьи


    Как написать простой парсер на python + уже готовый парсер

    Часы
    10.12.2024
    /
    Часы
    11.03.2026
    Глазик
    10192
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Как написать простой парсер на python, на примере собора изображений с сайта. Данный парсер представляет из себя пример того, как парсить статические и динамические сайты. С исходным кодом и архивом …

    Собственный парсер поисковой выдачи гугл

    Часы
    15.02.2025
    /
    Часы
    13.04.2026
    Глазик
    3124
    Сердечки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Как сделать процесс парсинга google быстрым и бесплатным. Про написание собственного парсера на Python и его настройку через Google API

    Как мой сайт агрессивно парсили из Китая и как я их заблокировал через htaccess

    Часы
    22.09.2025
    /
    Часы
    11.03.2026
    Глазик
    1528
    Сердечки
    1
    Соединённые точки
    0
    Соединённые точки
    0
    Соединённые точки
    0
    Про то, как кто-то активно парсил мой сайт из Китая, в графиках. Что могло послужить причиной этого(DDoS, Парсер, Кликер). Какие цели преследовал атакующий и как от этого можно защититься - …