DrissionPage
DrissionPage (алсо Дрищ, Дракон, Тот-самый-китайский-скрапер, Убийца Селениума) — эпичный и чуть менее, чем полностью богоугодный инструмент для веб-скрапинга и автоматизации браузера, написанный на расово верном Python. Создан сумрачным китайским гением под ником g1879. Сабж является гибридом бульдога с носорогом, объединяя в себе возможности прямого управления браузером на базе Chromium (без использования костыльного и детектируемого WebDriver) и высокоскоростной отправки HTTP-запросов в стиле библиотеки requests.
В 2026 году, когда Cloudflare и прочие антифрод-системы научились банить 99 процентов парсеров еще до того, как те успевают скачать 1-й байт HTML, DrissionPage стал настоящим спасением для мамкиных хакеров, дата-сайентистов и прочих анонимусов, желающих скачать весь интернет на свой жесткий диск.
Историческая справка[править]
В далеких 2010-х годах, когда деревья были большими, а защита от ботов состояла из 1 жалкой капчи с кривыми буквами, типичный красноглазик парсил сайты с помощью urllib или BeautifulSoup. Это было золотое время. Ты отправлял 1 GET-запрос, получал 1 кусок сырого HTML, парсил его регулярками и радовался жизни.
Но потом пришел Web 2.0. Сайты стали динамическими. Появился React, Angular, Vue и прочие модные фреймворки, которые рендерили страницу на стороне клиента. Обычный GET-запрос возвращал лишь пустой div с надписью Loading.... Быдлокодеры взвыли. На сцену вышел он — Selenium. Тяжелый, неповоротливый, жрущий оперативку гигабайтами, но способный рендерить JS. Однако радость была недолгой. Кровавые энтерпрайзы вроде Cloudflare, Akamai и Datadome быстро смекнули, что Selenium оставляет за собой цифровые следы размером со слона (привет, переменная navigator.webdriver). Забанить Selenium стало задачей на 5 минут.
Потом были Puppeteer и Playwright. Они работали через протокол CDP (Chrome DevTools Protocol) и казались глотком свежего воздуха. Но и их научились палить по фингерпринтам, специфичным заголовкам и паттернам поведения мыши. Анонимусы жаловались, что 15 процентов их скриптов отваливаются каждую неделю из-за изменений в DOM или апдейтов Cloudflare Turnstile.
И тут, откуда ни возьмись, из глубин китайского сегмента GitHub выныривает g1879 и выкатывает сабж. DrissionPage изначально вообще был надстройкой над Selenium, но к версии 3.0 автор выкинул Selenium на мороз, полностью переписал ядро на чистом CDP и добавил киллер-фичу — бесшовное переключение между режимом браузера и режимом сессии. К версии 4.1 сабж стал абсолютным вином.
2 режима одной сущности[править]
Главная фича Дрища, из-за которой тысячи скраперов удалили Playwright со своих пекарен — это дуализм. Сабж позволяет в 1 клик переключаться между 2 режимами работы:
- D-режим (Driver/Dynamic) — полноценное управление браузером. Жрет память, работает медленно, зато выполняет JS, рендерит кнопочки, обходит Turnstile-капчи, двигает мышкой и делает вид, что ты — обычный кожаный мешок, сидящий за компом.
- S-режим (Session/Speed) — управление через HTTP-запросы а-ля requests. Работает со скоростью света, не тратит такты процессора на отрисовку свистоперделок. Идеален для выкачивания 100500 страниц через API сайта.
Гениальность заключается в методе change_mode(). Анонимус может запустить D-режим, пройти авторизацию, решить капчу, сгенерировать 100 уникальных токенов, а затем сказать tab.change_mode() — и скрипт моментально перенесет все куки, хидеры и токены в S-режим, продолжая выкачивать данные со скоростью 1000 запросов в секунду. PROFIT!
Архитектура[править]
Если ты решил приобщиться к матану, придется выучить местные заклинания. В отличие от переусложненного ООП в Selenium, тут всё сделано для людей.
Chromium[править]
Объект Chromium — это Царь и Бог твоего браузера. Он отвечает за запуск процесса chrome.exe, настройку портов и выдачу вкладок. По умолчанию Дрищ поднимает браузер на 9222 порту и, если браузер уже открыт, просто цепляется к нему. Это эпический вин для отладки: тебе не нужно каждый раз при краше скрипта заново логиниться на сайте.
FROM DRISSIONPAGE IMPORT CHROMIUM
BROWSER = CHROMIUM()
TAB = BROWSER.LATEST_TAB
TAB.GET(''HTTPS://LURKMORE.TO'')
Вкладки (MixTab и ChromiumTab)[править]
То, чем ты будешь оперировать 99 процентов времени. В Selenium для переключения между вкладками надо было писать кучу костылей с window_handles. В Дрище каждая вкладка — это независимый объект. Ты можешь создать 10 вкладок и управлять ими одновременно в 10 потоках, браузеру будет пофиг.
Настройки (ChromiumOptions)[править]
Место, где можно крутить флаги хромиума, настраивать прокси и подменять юзер-агенты, чтобы сайт не понял, что ты сидишь с убунты в подвале маминого дома.
FROM DRISSIONPAGE IMPORT CHROMIUMOPTIONS, CHROMIUM
CO = CHROMIUMOPTIONS()
CO.HEADLESS(TRUE)
CO.SET_ARGUMENT(''--NO-SANDBOX'')
CO.SET_PROXY(''HTTP://127.0.0.1:1080'')
BROWSER = CHROMIUM(CO)
SessionPage[править]
Для тех, кому браузер вообще нафиг не уперся. Просто обертка над requests, но с поддержкой Дрищевского синтаксиса локаторов. Работает в 1000 раз быстрее, чем браузер.
FROM DRISSIONPAGE IMPORT SESSIONPAGE
PAGE = SESSIONPAGE()
PAGE.GET(''HTTPS://API.GITHUB.COM/EVENTS'')
PRINT(PAGE.JSON)
Синтаксис локаторов[править]
Селениумовский синтаксис find_element(By.XPATH, //div[@class='shit']) заставлял плакать кровью каждого, кто парсил больше 2 элементов. В DrissionPage автор упоролся и придумал свой мини-язык, который позволяет находить элементы так же легко, как прыщи на лице задрота.
Базовые правила[править]
- @ — означает атрибут. @id=123 найдет элемент с id 123.
- t: — означает тег. t: div найдет первый попавшийся div.
- tx= или просто текст — означает поиск по тексту. tx=Купить найдет кнопку с текстом Купить.
- # — сокращение для id. #123 эквивалентно @id=123.
- . — сокращение для class. .btn-red эквивалентно @class=btn-red.
- x: — поиск по XPath.
- c: — поиск по CSS Selector.
Модификаторы совпадения[править]
Китаец предусмотрел, что классы на современных сайтах генерируются вебпаком и выглядят как div_abc123. Поэтому добавил модификаторы:
- = — точное совпадение.
- : — частичное совпадение (по умолчанию для текста).
- ^ — начинается с.
- $ — заканчивается на.
Пример: @class^btn найдет все элементы, класс которых начинается на btn.
Комбинирование[править]
Если 1 условия мало (а его всегда мало), можно лепить их друг к другу:
- @@ — логическое И. t: div@@class=main@@tx=Текст найдет div, у которого класс main И текст Текст.
- @| — логическое ИЛИ. @|id=btn1@|id=btn2 найдет элемент с id btn1 ИЛИ btn2.
- @! — логическое НЕ. t: a@!href найдет ссылку БЕЗ атрибута href.
ELE = TAB.ELE(''T:BUTTON@@CLASS:PRIMARY@@TX:ОТПРАВИТЬ'')
ELE.CLICK()
Относительное позиционирование[править]
Еще 1 причина выбросить Selenium. В Дрище можно найти 1 элемент, а потом прыгать от него по DOM-дереву, как макака по лианам.
- ele.parent(2) — взять деда (родителя 2-го уровня).
- ele.next(1) — взять следующего брата.
- ele.prev(3) — взять 3-го брата сверху.
- ele.after(t: div) — найти первый div где-то ниже по документу.
А если тебе визуально нужно найти элемент, который нарисован правее текущего, есть пространственные методы: ele.east(), ele.west(), ele.south(), ele.north(). Да, Карл, оно высчитывает координаты пикселей и находит то, что лежит справа!
Взаимодействие с элементами[править]
Найти элемент — это лишь 50 процентов дела. Его еще надо пнуть. И тут DrissionPage показывает себя во всей красе.
Клик[править]
Метод click() в Дрище настолько умный, что у него скоро появится самосознание. Если элемент перекрыт другим элементом (например, вылез ебучий поп-ап с куками), обычный Selenium вывалит ElementClickInterceptedException и пошлет тебя на 3 буквы. Дрищ же попытается доскроллить до элемента, подождать, пока он остановится (параметр wait_stop=True), а если кликнуть физически нельзя, ты можешь сказать ему: слышь, кликни через JS!
ELE = TAB.ELE(''#SUBMIT-BTN'')
ELE.CLICK(BY_JS=TRUE)
Алсо, есть click.to_download(), который перехватывает всплывающее окно скачивания файла и сохраняет его куда тебе надо, и click.for_new_tab(), который нажимает на ссылку и заботливо возвращает тебе объект новорожденной вкладки.
Ввод текста[править]
Метод input() заменяет селениумовский send_keys(). Может вводить текст, нажимать кнопки из модуля Keys, и самое главное — может загружать файлы! Забудь про поиск инпутов с type=file. В Дрище ты просто вызываешь:
TAB.ELE(''#UPLOAD-BUTTON'').CLICK.TO_UPLOAD(''C:\\MEMES\\PEPE.JPG'')
Это перехватывает системный диалог выбора файла (на уровне ОС, Карл!) и подсовывает туда путь. Эпик вин.
Цепочки действий[править]
Для тех, кто хочет нарисовать Джоконду курсором мыши, есть модуль Actions.
FROM DRISSIONPAGE.COMMON IMPORT ACTIONS
AC = ACTIONS(TAB)
AC.MOVE_TO(''#CANVAS'').HOLD().MOVE(100, 100).RELEASE()
Избавление от time.sleep()[править]
Каждый быдлокодер знает: если скрипт падает, добавь time.sleep(5). Если снова падает — добавь time.sleep(10). В результате скрипт выполняется 3 часа. В DrissionPage встроена мощнейшая система умных ожиданий, привязанная к атрибуту wait.
Почти все операции ищут элементы с неявным ожиданием (по умолчанию 10 секунд). Но если нужно жестко подождать какого-то события, есть методы:
- tab.wait.load_start() — подождать, пока страница вообще сообразит, что надо грузиться.
- tab.wait.doc_loaded() — подождать, пока загрузится DOM.
- tab.wait.eles_loaded(#my-div) — подождать появления элемента в DOM.
- ele.wait.displayed() — подождать, пока элемент не станет видимым (display: block).
- ele.wait.hidden() — подождать, пока ублюдок не исчезнет (например, лоадер).
- ele.wait.deleted() — подождать, пока элемент не выпилят из DOM.
- ele.wait.stop_moving() — подождать, пока CSS-анимация не закончит двигать кнопку.
- tab.wait.download_begin() — подождать старта загрузки файла.
- tab.wait.alert_closed() — подождать, пока юзер не закроет алерт.
TAB.GET(''HTTPS://EXAMPLE.COM'')
TAB.WAIT.ELE_DISPLAYED(''.LOADING-SPINNER'')
TAB.WAIT.ELE_HIDDEN(''.LOADING-SPINNER'', TIMEOUT=30)
TAB.ELE(''#DATA'').CLICK()
Перехват трафика[править]
Одна из самых киллер-фич сабжа. Зачем скрапить HTML, если сайт сам подгружает все нужные JSON-ы через XHR/Fetch? В Playwright для этого надо было писать асинхронную лапшу. В Дрище всё делается в 3 строчки.
У каждой вкладки есть объект listen, который сниффает трафик прямо из кишок браузера.
TAB.LISTEN.START(''API/V1/GET_USERS'')
TAB.GET(''HTTPS://SITE.COM/USERS'')
PACKET = TAB.LISTEN.WAIT()
PRINT(PACKET.RESPONSE.BODY)
Ты указываешь кусок URL, запускаешь прослушку, триггеришь загрузку на странице, и вызываешь wait(). Скрипт замирает, пока браузер не поймает нужный пакет, а потом выдает тебе готовый JSON, распарсенный в словарь питона. Никакого парсинга HTML, никаких CSS-селекторов. Ты получаешь чистую дату.
Можно даже перехватывать пачку пакетов через генератор steps():
FOR PACKET IN TAB.LISTEN.STEPS():
PRINT(PACKET.URL)
TAB.ELE(''#NEXT-PAGE'').CLICK()
IF CONDITION: BREAK
Файлопомойка: DownloadKit[править]
Встроенный менеджер загрузок. Умеет всё то, ради чего раньше ставили IDM или aria2c. Поддерживает многопоточность, докачку, переименование файлов на лету, обработку коллизий имен. Когда ты жмешь на кнопку скачивания в браузере, Дрищ может перехватить эту задачу и обработать ее своими силами.
MISSION = TAB.WAIT.DOWNLOAD_BEGIN() PRINT(MISSION.RATE) MISSION.WAIT()
Фреймы и Shadow-DOM[править]
Любой, кто пробовал парсить элементы внутри ебучих <iframe>, знает, какая это боль. Нужно сделать switch_to.frame(), потом найти элемент, потом не забыть сделать switch_to.default_content(). А если фрейм вложен во фрейм? А если их 10? Смерть.
В Дрище фрейм — это просто еще 1 страница. Более того, если фрейм находится на том же домене, что и главная страница, Дрищ способен искать элементы СКВОЗЬ фреймы. Ты просто пишешь tab.ele(#inside-frame-btn), и он сам найдет его внутри фрейма, без всяких переключений.
Если фрейм кроссдоменный, то получаешь его объект и работаешь с ним как со вкладкой:
IFRAME = TAB.GET_FRAME(''#PAYMENT-FRAME'')
IFRAME.ELE(''#CARD-NUMBER'').INPUT(''4242424242424242'')
С Shadow-DOM (теневым ДОМом, который так любят использовать в Web Components) история аналогичная. Если в Selenium достать shadow-root — это целый квест с инжектом JS, то тут достаточно обратиться к свойству shadow_root или его шорткату sr:
SR_ELE = TAB.ELE(''#APP'').SR
SR_ELE.ELE(''T:DIV'').CLICK()
Можно выстраивать многоэтажные цепочки: tab('#app').sr('#header').sr('.btn').
Глобальные настройки[править]
Автор понимал, что каждый раз хардкодить пути к хрому и настройки таймаутов в коде — удел быдла. Поэтому прикрутил систему конфигурации через INI-файлы.
Файл dp_configs.ini может лежать прямо в папке с твоим скриптом. В нем прописываются порты, прокси, юзер-агенты, таймауты и флаги хрома. Если файл есть, Дрищ сам его сожрет при инициализации. Чтобы сгенерировать дефолтный конфиг, достаточно 1 раз запустить метод configs_to_here().
Алсо, есть глобальный класс Settings, который позволяет настраивать поведение сабжа «на лету»:
FROM DRISSIONPAGE.COMMON IMPORT SETTINGS
SETTINGS.SET_RAISE_WHEN_ELE_NOT_FOUND(TRUE)
SETTINGS.SET_LANGUAGE(''EN'')
SETTINGS.SET_AUTO_HANDLE_ALERT(TRUE)
По дефолту, если метод ele() не находит элемент, он возвращает специальный объект NoneElement, который в логическом контексте равен False. Это сделано, чтобы код не падал с эксепшенами на каждом чихе, а позволял писать красивые проверки: IF TAB.ELE(#ERROR): PRINT(FAIL). Но если ты любитель БДСМ и хочешь, чтобы скрипт падал с ElementNotFoundError, просто включи set_raise_when_ele_not_found(True).
Турбо-ускорение скрапинга[править]
Хинт для продвинутых дата-майнеров. Допустим, у тебя на странице таблица из 1000 строк. Если ты будешь обходить ее в цикле обычным методом ele(), браузер будет для каждого элемента вычислять координаты, проверять видимость в окне, дергать CDP. На 1000 элементов уйдет секунд 5-10.
Но если тебе нужно просто вытащить текст, интерактивность не нужна! Для этого был придуман метод s_ele() (static element). Он берет кусок HTML, выдирает его из браузера, преобразует в легковесный объект SessionElement (парсинг на базе lxml) и позволяет искать внутри него со скоростью света.
FROM DRISSIONPAGE IMPORT CHROMIUM
FROM TIMEPINNER IMPORT PINNER
TAB = CHROMIUM().LATEST_TAB
TAB.GET(''HTTPS://WWW.163.COM'')
PINNER = PINNER()
PINNER.PIN()
LINKS = TAB(''T:BODY'').S_ELES(''T:A'')
FOR LNK IN LINKS:
PRINT(LNK.TEXT)
PINNER.PIN(''ВРЕМЯ'')
Обычный поиск занял бы 4 секунды. Поиск через s_eles() занимает 0.28 секунды. Ускорение более чем в 10 раз. Это абсолютный вин.
Битва с капчами[править]
А теперь о самом сладком, ради чего 90 процентов юзеров вообще гуглят этот инструмент. Антибот системы.
Cloudflare Turnstile (та самая галочка Verify you are human) стала проклятием автоматизаторов. Она палит WebDriver, палит странные фингерпринты, палит подозрительные движения мыши. Selenium отлетает на ней с вероятностью 100 процентов, Playwright тоже частенько буксует.
DrissionPage же, из-за того, что он работает не через стандартный протокол WebDriver, а напрямую через CDP (причем с патчами, убирающими палевные флаги автоматизации), часто проскакивает Turnstile даже без дополнительных танцев с бубном, просто имитируя клик по координатам галочки.
Но если Cloudflare врубил паранойю на максимум, на помощь приходят сторонние интеграции. В интернетах ходят мануалы, как скрестить DrissionPage с сервисами распознавания капч (типа CapSolver).
Суть: ты запускаешь Дрищ, натыкаешься на Cloudflare Challenge. Если галочки нет или клик не помогает, ты вытаскиваешь из DOM'а ключи сайта (data-sitekey), отправляешь их через HTTP-запрос (кстати, Дрищ может сделать это сам через свой встроенный S-режим) на API CapSolver. Через 5-10 секунд получаешь токен решения. Затем с помощью метода run_js() вставляешь этот токен в нужный скрытый input на странице и сабмитишь форму.
DEF SOLVE_CAPTCHA(SITE_KEY, URL):
PAYLOAD = {
''CLIENTKEY'': ''ТВОЙ_АПИ_КЛЮЧ'',
''TASK'': {
''TYPE'': ''TURNSTILETASKPROXylESS'',
''WEBSITEURL'': URL,
''WEBSITEKEY'': SITE_KEY
}
}
RES = REQUESTS.POST(''HTTPS://API.CAPSOLVER.COM/CREATETASK'', JSON=PAYLOAD)
TASK_ID = RES.JSON().GET(''TASKID'')
WHILE TRUE:
RES = REQUESTS.POST(''HTTPS://API.CAPSOLVER.COM/GETTASKRESULT'', JSON={''CLIENTKEY'': ''ТВОЙ_АПИ_КЛЮЧ'', ''TASKID'': TASK_ID})
IF RES.JSON().GET(''STATUS'') == ''READY'':
RETURN RES.JSON().GET(''SOLUTION'')
TIME.SLEEP(1)
TOKEN = SOLVE_CAPTCHA(''КЛЮЧ_С_САЙТА'', TAB.URL)
TAB.RUN_JS(F''DOCUMENT.GETELEMENTBYID("CF-CHL-WIDGET-X").VALUE = "{TOKEN}";'')
TAB.ELE(''#SUBMIT-BTN'').CLICK()
Вуаля! Cloudflare пройден, данные льются в твою базу данных.
Алсо, некоторые параноики идут дальше и интегрируют DrissionPage со специализированными антидетект-браузерами (например, TgeBrowser или AdsPower). Антидетект генерирует уникальный фингерпринт (подменяет WebGL, Canvas, шрифты, AudioContext, UserAgent), открывает порт отладки, а Дрищ просто цепляется к этому порту через ChromiumOptions().set_local_port(PORT). Получается монстр Франкенштейна, которого не спалит ни одна нейросеть.
ИИ-парсинг[править]
Как жаловался один анонимус на Reddit: 10 процентов моих скраперов ломаются еженедельно из-за того, что верстальщики сайта меняют классы.
Решение? LLM! Дрищ отлично подходит для ИИ-скрапинга. Вместо того чтобы хардкодить локаторы вроде t: div@@class=price, ты просто скармливаешь весь tab.html (или только tab.text, чтобы сэкономить токены) в API Gemini или ChatGPT, и просишь: Верни мне JSON со списком товаров и цен.
PAGE_CONTENT = TAB.TEXT
PROMPT = F''ВОТ ТЕКСТ СТРАНИЦЫ: {PAGE_CONTENT}. НАЙДИ ВСЕ ЦЕНЫ И НАЗВАНИЯ, ВЕРНИ В ФОРМАТЕ JSON.''
LLM_RESPONSE = GEMINI_API.GENERATE(PROMPT)
Поскольку Дрищ работает быстро и стабильно, он выступает идеальным глазом для ИИ-агентов. Неудивительно, что проекты вроде NicheMiner AI используют именно DrissionPage (через CDP) для скрытного обхода LinkedIn и выкачивания лидов, скармливая сырые данные в Gemini Flash.
Фичи для задротов[править]
- Скроллинг. В сабже реализован скроллинг на любой вкус. tab.scroll.to_bottom(), tab.scroll.to_see(ele), tab.scroll.down(500). Алсо, можно отключать плавный скроллинг, который внедряют дизайнеры на сайты, методом tab.set.scroll.smooth(False), чтобы клики происходили мгновенно и точно в цель.
- Мульти-вкладки. По умолчанию вкладки — синглтоны. Вызвав get_tab(1) 2 раза, ты получишь 1 и тот же объект. Если ты хочешь в 2 разных потоках управлять 1 вкладкой (1 поток кликает, 2-й слушает пакеты), надо выключить синглтоны: Settings.set_singleton_tab_obj(False).
- Блокировка URL. Метод tab.set.blocked_urls('*.css*') запретит браузеру качать CSS-файлы. Страница будет выглядеть как говно мамонта из 1995 года, зато загрузится за 0.1 секунды.
- Локальное чтение файлов. Метод get() принимает не только HTTP-урлы, но и локальные пути вроде file:///C:/memes/index.html.
- Сохранение страниц в PDF и MHTML. Вызов tab.save(as_pdf=True) тихо и без всяких диалоговых окон печатает страницу в PDF. Идеально для автоматической генерации отчетов.
- Запись видео. Да, эта хреновина умеет записывать видео с экрана браузера! Вызываешь tab.screencast.start() и tab.screencast.stop(), и получаешь готовый mp4 файлик. Требует установленного opencv-python.
- Консоль. Ты можешь читать логи из DevTools Console: tab.console.start(), затем tab.console.wait(). Если сайт пишет туда токены или ошибки, ты их поймаешь.