В этот раз - снова про программирование и автоматизацию. Если вы ещё не знали про существование Selenium WebDriver и ему подобных инструментов, то этот небольшой обзор поможет сэкономить некоторое время на рутине.
## Задача: получить новые видео с подписок в виде RSS
Я очень люблю некоторых людей, которые ведут YouTube-каналы, но очень не люблю тратить кучу времени на просмотр рекомендаций, на рассылки с "интересными" видео и на отсмотр подписок вручную. У меня имеется некоторый список любимых каналов в подписках, каждый из которых со своей периодичностью выпускает новые видео.
Обновления для новых видео с ютуб-канала можно получать не только с помощью рассылки и пуш-уведомлений, но и через весьма надёжные [RSS-каналы]( https://amateurblogger.ru/rss-eto-dolzhen-znat-kazhdyj/ ). Проблема лишь в том, что для генерации RSS-ленты нужно знать идентификаторы каналов и правильно составлять ссылки, которые нужно проталкивать в клиенты-читалки.
А что если ты отпишешься или наоборот подпишешься на канал? Тогда придётся брать и копировать ссылку вручную в RSS-клиент или агрегатор, что крайне неудобно и грозит путаницей. В идеале можно обновлять вручную файлик [subscription_manager]( https://www.youtube.com/subscription_manager?action_takeout=1 ), который содержит список всех твоих подписок, но это всё равно надо делать вручную.
## YouTube API и сложности с ним
Казалось бы, можно воспользоваться YouTube API и получать всю информацию оттуда. Хорошо сказано, да трудно сделано. Для скачиванияв личной информации пользователя требуется получать специальный Oauth-токен, который просто так не достанешь (нужно создавать Web Endpoint с редиректом в нужное место, поднимать отдельный сервис на подтверждённом домене и.т.п.). К тому же, токены имеют свойство протухать, и требуется как-то задумываться об обновлениях. Зачем вся эта возня для какого-то простейшего скрипта, который должен будет тихо-мирно запускаться в Cron, отработать секунду и заглохнуть?
В общем, повозился я в панели управления Google, плюнул и решил перехитрить систему. В браузере ведь у меня уже есть авторизация на гуглосервисах, да и личную информацию оттуда как раз прочитать проще простого. Почему бы не извлечь список каналов как раз через браузер?
## Selenium: управляем браузером через скрипты
Цитата [из Википедии]( https://ru.wikipedia.org/wiki/Selenium ):
> Selenium WebDriver — это инструмент для автоматизации действий веб-браузера. В большинстве случаев используется для тестирования Web-приложений, но этим не ограничивается. В частности, он может быть использован для решения рутинных задач администрирования сайта или регулярного получения данных из различных источников (сайтов).
У Selenium имеется куча биндингов для разных языков программирования, в том числе для Python. Перед началом использования требуется установить некоторые пакеты из репозиториев, в Debian и Ubuntu их доставить проще простого: `apt-cache search selenium`, и всё сразу вылезет. Доступны движки на основе Firefox и Chromium (разумеется, сам браузер нужно перед этим тоже установить).
## Ну что, поехали пушкой по воробьям
Решил использовать свой любимый браузер - Firefox. Благо, здесь можно воспользоваться уже существующим браузерным профилем, хотя по умолчанию Selenium обычно создаёт свежий. Приготовления:
1. Устанавливаем все пакеты вида python3-selenium, firefoxdriver, geckodriver и.т.п.
2. Из текущего профиля фаерфокса логинимся в Гугле, пробуем скачать файл вручную
3. Выбираем при скачивании (или напрямую в настройках), что файл автоматически будет сохраняться в папку загрузок без подтверждения
4. Копируем путь в системе к профилю браузера
#!/usr/bin/env python3 import os, sys import time from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.firefox.options import Options yturl = "https://www.youtube.com/subscription_manager?action_takeout=1" file_path = "/home/user/Downloads/subscription_manager" if os.path.exists(file_path): os.remove(file_path) options = Options() options.headless = True fp = webdriver.FirefoxProfile("/home/user/.mozilla/firefox/blablabla.default") driver = webdriver.Firefox(fp, options=options) driver.set_page_load_timeout(15) try: driver.get(yturl) except: pass time.sleep(5) driver.quit()
Важное замечание насчёт Headless-режима. Headless представляет собой запуск вне графического окружения, без всякой отрисовки. Это требуется как раз для работы браузера вообще без участия человека. При отладке Selenium-скриптов лучше пользоваться браузером в обычном режиме, но когда вся автоматизация будет доведена до совершенства, то можно включить Headless и закидывать скрипты в Cron. **При отсутствии запущенного X-сервера скрипты в Cron будут работать только в Headless.**
Кроме простого посещения сайтов Selenium умеет и многое другое, например:
- нажимать на ссылки/кнопки, прокручивать страницы, вводить текст и "сёрфить веб" как человек
- запускать произвольный Javascript на сайте
- доставать полезную информацию из любого куска страницы
- делать скриншоты сайтов
Итак, список каналов вытащили. Теперь дело остаётся за малым - распарсить его, вытащить айдишники и закинуть в программу, которая слепит из них красивую RSS-ленту.
## Обрабатываем подписки
#!/usr/bin/env python3 from xml.dom import minidom xmldoc = minidom.parse("subscription_manager") itemlist = xmldoc.getElementsByTagName("outline") del(itemlist[0]) # there is no xmlUrl in 1-st element for i in itemlist: print(i.attributes['xmlUrl'].value.split("=")[1])
Для того чтобы полученный список ID преобразовать в RSS-ленту, рекомендую программу ytsubs: https://github.com/ali1234/ytsubs. Она консольная и отлично работает.
## В качестве бонуса
Одной из самых популярных статей здесь в блоге является статья про [построение графиков в matplotlib]( https://blog.alicorn.tk/posts/best-plot-mpl.html ). Из новых добавлений к лайфхакам в той статье:
- Упомянул параметр width_ratios для более удобного управления размерами графиков
- Написал вариант итерации по subplots с помощью объекта axes.flat: теперь ещё меньше строк кода и никакой возни с двухмерными массивами!
- Масштабы сетки можно устанавливать отдельно для разных осей, для красивых графиков это очень полезно
Этот пост в блоге: https://blog.alicorn.tk/posts/selenium.html