Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

Мониторинг сайтов

Приложение принимает список ссылок, параллельно открывает каждую в stealth-Chrome (undetected-chromedriver поверх Selenium), дожидается полной загрузки страницы и снимает полностраничный скриншот. Ход работы виден в Streamlit в реальном времени: статус, прогресс, превью по мере готовности, пауза, стоп и история прогонов. Снимки можно посмотреть прямо в приложении и выгрузить архивом — и из текущего прогона, и из истории.

Поддержка систем. Разработано и проверено на macOS 26 (Apple Silicon). Код кросс-платформенный, пути, определение версии Chrome и запуск разведены по системам — на Windows и Linux приложение должно работать, но там оно не тестировалось. Если на Windows что-то не заведётся, первым делом uv run python -m monitor.doctor: он показывает, на каком именно шаге ломается.


Установка с нуля

Нужны три вещи: Google Chrome, uv и git. Python ставить отдельно не нужно — uv сам скачает 3.12, указанный в .python-version.

macOS

# 1. Homebrew — если его ещё нет
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 2. uv и git
brew install uv git

# 3. Google Chrome — если не установлен
brew install --cask google-chrome

# 4. Проект
git clone https://github.com/ilianovi3/monitoring.git
cd monitoring

# 5. Зависимости и Python 3.12
make install

# 6. Проверка окружения — обязательный шаг
make doctor

# 7. Запуск
make run

Без Homebrew uv ставится так: curl -LsSf https://astral.sh/uv/install.sh | sh (после этого перезапустите терминал).

Windows

В PowerShell:

# 1. uv и git
winget install --id=astral-sh.uv -e
winget install --id=Git.Git -e

# 2. Google Chrome — если не установлен
winget install --id=Google.Chrome -e

# 3. Перезапустите PowerShell, чтобы обновился PATH, затем:
git clone https://github.com/ilianovi3/monitoring.git
cd monitoring

# 4. Зависимости и Python 3.12
uv python install 3.12
uv sync

# 5. Проверка окружения — обязательный шаг
uv run python -m monitor.doctor

# 6. Запуск
.\start.ps1

Если winget недоступен, uv ставится так: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Если PowerShell отказывается выполнять .\start.ps1, разрешите скрипты для текущего пользователя: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.

Останов — .\stop.ps1.

Linux

curl -LsSf https://astral.sh/uv/install.sh | sh
sudo apt install -y git google-chrome-stable   # или ваш пакетный менеджер

git clone https://github.com/ilianovi3/monitoring.git
cd monitoring
make install && make doctor && make run

Почему make doctor — обязательный шаг

undetected-chromedriver не обновлялся с середины 2025 года, а Chrome выходит каждые четыре недели. Доктор прогоняет всю цепочку — версия Chrome, платформа драйвера, скачивание, патч, подпись, запуск браузера, тестовый скриншот, проверка на детект — и, если что-то не так, называет конкретный шаг. Без него поломка всплывёт посреди прогона на сотне ссылок.


Команды

На macOS и Linux — через make (make help покажет всё):

Команда Что делает
make install Python 3.12 и зависимости
make run запустить в фоне (PID в .run/app.pid, логи в logs/app.log)
make dev запустить на переднем плане, логи в терминал
make stop остановить приложение и добить оставшиеся Chrome
make restart перезапустить
make status запущено ли, сколько Chrome работает
make logs следить за логом
make doctor смоук-тест окружения
make test юнит-тесты
make info версии Chrome/chromedriver, объём данных
make reinstall-driver снести кэш драйвера и скачать заново
make clean-history / clean-screenshots / clean очистки

Порт задаётся так: make run PORT=8600.

На Windows make обычно нет, поэтому те же операции — напрямую:

Действие Команда
Установка uv python install 3.12; uv sync
Запуск в фоне .\start.ps1 (или .\start.ps1 -Port 8600)
Запуск в терминале uv run streamlit run app.py
Остановка .\stop.ps1
Логи Get-Content logs\app.log -Wait
Смоук-тест uv run python -m monitor.doctor
Тесты uv run pytest -q
Состояние окружения uv run python -m monitor.cli info
Переустановить драйвер uv run python -m monitor.cli reinstall-driver
Очистить историю uv run python -m monitor.cli clear-history
Удалить скриншоты uv run python -m monitor.cli clear-screenshots

Как пользоваться

  1. Вставьте ссылки в текстовое поле — по одной в строке (\n или \r\n). Кнопка «Вставить из буфера обмена» добавляет содержимое буфера. Схема дописывается автоматически, дубликаты и мусор отбрасываются с пояснением.
  2. Задайте число воркеров (1–8). Каждый воркер — отдельное окно Chrome со своим профилем, примерно 300–500 МБ памяти.
  3. «Старт». Лента обновляется раз в секунду сама.
  4. «Пауза» останавливает выдачу новых ссылок; страница, которая уже грузится, дорабатывается до конца. «Стоп» гасит прогон, недобранные ссылки помечаются пропущенными.

Окна Chrome открываются за пределами видимой области и работать не мешают — скриншот снимается через CDP, реальный экран для этого не нужен. Режим Headless в сайдбаре быстрее, но защиты вроде Cloudflare срабатывают на него заметно чаще.

Адресная строка на снимке

Интерфейс браузера в скриншот не попадает: Page.captureScreenshot — это снимок рендерера, а не окна, и никакими параметрами омнибокс туда не добавить. Снять окно целиком умеет только ОС (screencapture -l на macOS, PrintWindow на Windows), но для этого окно должно быть видимым на экране — у нас они специально уводятся в -4000,-4000, а в headless окон нет вовсе; вдобавок ОС снимет ровно вьюпорт, и полностраничный снимок потеряется.

Поэтому полоса с адресом дорисовывается над снимком (src/monitor/imaging.py): омнибокс с замочком и итоговым адресом после редиректов, кнопки окна, светлая тема Chrome. Результат детерминированный, одинаковый на всех платформах и в headless, и корректно живёт с полностраничным снимком любой высоты. Высота кадра растёт примерно на 48 px (полоса масштабируется от ширины снимка), сам контент страницы не перекрывается. Отключается тумблером «Адресная строка на снимке» в сайдбаре.


Просмотр и выгрузка снимков

Превью

Отдавать в браузер исходные PNG нельзя: страница высотой 16384 px весит несколько мегабайт, и десяток таких карточек кладёт и вкладку, и память. Поэтому в ленте показываются уменьшенные JPEG-копии, а полный снимок открывается по кнопке «Открыть полностью» — по одному за раз, в модальном окне, вместе с кнопкой скачивания PNG.

Превью собираются воркером сразу после снимка (интерфейс никогда не декодирует многомегабайтные файлы) и кэшируются на диск под ключом параметров, поэтому смена ширины или режима не портит уже собранное. На практике 10 МБ скриншотов превращаются примерно в 130 КБ превью.

В сайдбаре, раздел «Просмотр»:

Настройка Что делает
Показывать превью выключает картинки полностью — остаётся только список результатов, в браузер не уходит ни одного изображения
Карточек на странице сколько снимков рисовать за раз; остальные — через переключатель страниц
Что показывать в превью «Верх страницы» — крупно и читаемо; «Вся страница» — целиком, но длинный лендинг станет узкой полоской
Ширина превью 200–800 px

Высота превью ограничена полутора ширинами, иначе одна длинная страница растянула бы карточку на несколько экранов и перекосила ряд.

Выгрузка

Из текущего прогона — блок «Выгрузка снимков» над лентой: галочки на карточках, кнопки «Выбрать все» / «Снять выделение», затем «Подготовить архив» и «Скачать архив».

Из истории — выделите строки в таблице (чекбоксы в первой колонке), под ней появятся размер выборки, кнопки выгрузки и превью выделенных снимков.

Обрезанная копия. Галочка «Добавлять копию 1080 px» (раздел «Выгрузка» в сайдбаре, включена по умолчанию) кладёт в архив рядом с каждым оригиналом вторую версию — верхние 1080 px кадра, с суффиксом _1080px в имени:

run_20260819_115711/example.com__327c3fda.png          1920×4312 — оригинал
run_20260819_115711/example.com__327c3fda_1080px.png   1920×1080 — обрезка

Копия делается на лету, на диске не хранится и на историю не влияет, так что галочку можно переключать между выгрузками — в том числе для старых прогонов. Если снимок и так не выше 1080 px, в архив уходят исходные байты без перекодирования. Та же пара кнопок есть в окне «Открыть полностью».

Архив собирается только по нажатию «Подготовить архив», а не на каждую перерисовку, и складывается без сжатия: PNG уже сжат, повторное сжатие даёт единицы процентов ценой заметного времени на десятках мегабайт. Если выбрано больше 300 МБ, приложение предупредит — такой архив целиком держится в памяти.


Где что лежит

data/screenshots/run_20260801_173022/   подпапка на каждый прогон
    example.com__327c3fda.png
    en.wikipedia.org_wiki_Python__b43612ed.png
data/thumbnails/crop-w420-a1.5-q80/     кэш превью, ключ — параметры показа
    run_20260801_173022/example.com__327c3fda.jpg
data/history/history.jsonl              история, по записи на строку
data/.drivers/uc_driver_w0              личная копия драйвера для воркера
logs/app.log                            лог приложения

Превью пересобираются из исходников в любой момент, так что data/thumbnails/ можно удалять свободно.

Имена файлов

Скриншоты могут сниматься на одной системе, а разбираться на другой, поэтому имя удовлетворяет ограничениям macOS и Windows одновременно:

  • убраны символы < > : " / \ | ? * и управляющие;
  • имена DOS-устройств (CON, PRN, COM1…) экранируются — Windows запрещает их и с расширением, то есть CON.png тоже;
  • нет точки и пробела в конце (Windows) и в начале (на macOS это скрытый файл);
  • IDN-домены переводятся в punycode, строка нормализуется в NFC (macOS хранит имена в NFD — без нормализации имя «поедет» после переноса);
  • длина обрезается до 120 байт UTF-8, не разрывая символ (лимит APFS — 255 байт);
  • в конце добавляется __ и 8 символов sha1 полного URL: после санитизации и обрезки разные адреса легко сходятся в одну читаемую часть.

Как это устроено

  • src/monitor/engine.py — пул потоков. Потоки, а не процессы: работа I/O-bound, GIL не мешает, общее состояние проще. Воркеры никогда не обращаются к st.* (у фоновых потоков нет ScriptRunContext); UI читает состояние через engine.snapshot(). Движок живёт в @st.cache_resource, поэтому переживает рероны Streamlit.
  • src/monitor/capture.py — «полная загрузка» это лестница: readyState, затем сетевая тишина, затем document.fonts.ready, прокрутка для ленивых картинок, ожидание img.complete и выдержка. Счётчик незавершённых запросов ставится через CDP до выполнения скриптов страницы: readyState ничего не знает про XHR и fetch, которыми SPA догружают контент.
  • Скриншот — Page.captureScreenshot с captureBeyondViewport. Сторона ограничена 16384 px (потолок Chrome); если страница выше, снимок помечается обрезанным.
  • src/monitor/imaging.py — постобработка на Pillow: адресная строка поверх снимка и обрезанная копия для архива. Размеры в ленте и истории остаются размерами страницы: дорисованная полоса — оформление снимка, а не свойство сайта, и потолок Chrome считается тоже по ним.

Патчи undetected-chromedriver

Пакет заморожен: релиз на PyPI — 3.5.5 (февраль 2024), последний коммит — июль 2025. Ставится из git по пину коммита (в master новее зависимости), а в src/monitor/uc_patch.py и driver.py чинятся четыре поломки, без которых он не поднимается. Если решите обновить uc — проверьте, не стали ли патчи лишними или, наоборот, недостаточными.

  1. Нет Apple Silicon. patcher.py::_set_platform_name для darwin жёстко ставит mac-x64 и вообще не смотрит на platform.machine() — на M-чипе тянется Intel-сборка драйвера. Chrome for Testing публикует mac-arm64.
  2. distutils-API на packaging.Version. patcher.py импортирует packaging.version.Version as LooseVersion, но обращается к нему как к distutils-версии: release.version[0] и version_full.vstring. Таких атрибутов нет — скачивание драйвера падает с AttributeError. Это и есть известное «uc не работает с Chrome 138+». Ломается на всех системах.
  3. Невалидная подпись после патча (только macOS). patch_exe() переписывает байты прямо в бинарнике, ad-hoc подпись перестаёт сходиться с содержимым, и на Apple Silicon ядро убивает драйвер сигналом 9 ещё до первой строки вывода (Selenium показывает это как Status code was: -9). Лечится codesign --force --sign - после патча.
  4. Гонка при параллельном старте. Patcher.auto() без своего пути удаляет и перекачивает драйвер, а защита от параллелизма нерабочая: patcher.py пишет with Lock():, конструируя новый multiprocessing.Lock на каждый вызов. Поэтому драйвер готовится один раз последовательно, а воркерам раздаются личные копии через driver_executable_path.

Ещё три места, где легко наступить:

  • В ChromeOptions нельзя передавать --headless: uc на этот аргумент выполняет options.headless = True, а такой сеттер удалён в Selenium 4.13+. Headless включается только через uc.Chrome(headless=...).
  • macOS ужимает окно под размер дисплея (запрошенные 1920×1080 превращаются, например, в 1728×904). Если система обрезала окно, размер вьюпорта навязывается через Emulation.setDeviceMetricsOverride.
  • На Windows chrome.exe --version ничего не печатает в stdout, поэтому версия читается из реестра (HKCU\Software\Google\Chrome\BLBeacon), затем из метаданных файла, затем из имени версионной подпапки.

Если что-то не работает

Симптом Что делать
doctor падает на загрузке драйвера make reinstall-driver (Windows: uv run python -m monitor.cli reinstall-driver)
Status code was: -9 (macOS) подпись драйвера слетела — переустановите драйвер
Chrome обновился, драйвер старый переустановите драйвер
Google Chrome не найден доктор напечатает список проверенных путей; поправьте _chrome_candidates() в src/monitor/config.py
Остались висеть окна Chrome make stop / .\stop.ps1 — добивают только процессы этого приложения
Сайт отдаёт капчу выключите Headless в сайдбаре, уменьшите число воркеров
Скриншот обрезан страница выше 16384 px — это потолок Chrome, не приложения
Лента тормозит уменьшите «Карточек на странице» или выключите «Показывать превью» в сайдбаре
Превью не появилось снимок мог не сохраниться — проверьте make logs; кэш можно снести, data/thumbnails/ пересоберётся
PowerShell не запускает .ps1 Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages