СофтХабрахабр

«Прочту потом»: трудная судьба оффлайновой коллекции интернет-страничек

Для меня долгие годы такой программой был Macropool WebResearch, позволявший сохранять, читать и организовывать интернет-страницы в некое подобие оффлайновой библиотеки. Есть виды софта, без которого одни люди жить не могут, а другие даже не представляют, что такое существует и кому-то вообще нужно. Мне же хотелось бы иметь возможность хотя бы отмечать документы как "прочитанные" или "избранные", быстро переходить от одного текста к другому и не зависеть от доступности интернета или конкретного сайта. Уверен, многие из читателей прекрасно обходятся коллекцией ссылок или комбинацией браузера и папки с набором сохранённых документов. Бывает, что читать есть время ровно тогда, когда интернета нет (в дороге, например), да и ссылки, к сожалению, нередко оказываются недолговечными.

Эта программа была нашпигована самыми разнообразными функциями: каталогизация по разделам и по тегам, редактирование заметок, всевозможные виды экспорта/импорта и так далее. Видимо, примерно на таких людей и рассчитывали авторы WebResearch. Ещё несколько лет мне удавалось кататься на этой лошадке, но сначала отвалились браузерные плагины (доступные только для тогдашних версий IE и FireFox), а затем и современные сайты перестали нормально отображаться в просмотрщике на основе старого IE-движка. Однако примерно к 2013 году проект перестал обновляться, а затем прекратил существование и сайт разработчика.


Главное окно WebResearch, PC Week/RE №17 (575)

Дорога разочарований

Мне казалось, особых сложностей здесь не будет, поскольку желания мои крайне скромны. Как только стало ясно, что замены не избежать, я в фоновом режиме взялся за поиск приличного аналога. Я был готов обойтись лишь малым подмножеством средств WebResearch, включающим в себя:

  • сохранение HTML страницы из браузера с помощью расширения;
  • хотя бы минимальные средства каталогизации (переименование, организация каталогов, метки);
  • (желательно) поддержка PDF документов;
  • любой сносный способ синхронизации коллекции с другими устройствами.

На сегодняшний день хоть как-то удовлетворяют моим пожеланиям разве что проекты TagSpaces и myBase. К моему удивлению, ничего похожего мне найти так и не удалось, хотя я честно излазил интернет вдоль и поперёк и внимательно изучил с десяток подходящих по аннотации программ (за исключением Evernote, где похожая по описанию функциональность доступна только по подписке). Их изучение, вообще говоря, представляет определённый культурологический интерес.

При этом злосчастное оглавление коллекции c модными закруглёнными значками занимает половину экрана, вмещая при этом штук двадцать элементов максимум, а базовые штуки вроде поддержки горячих клавиш или рендеринга просматриваемого документа пишутся по остаточному принципу. TagSpaces — это такой вот "стильный-модный-молодёжный" органайзер на Electron с красивым сайтом, адаптивной вёрсткой и, конечно, тёмной темой, куда ж без неё. В итоге документы отображаются криво, а работа с коллекцией превращается в скучный и трудоёмкий комплекс упражнений с мышью.

Однако в качестве окна просмотра здесь выступает всё тот же браузер на основе старого IE (что уже делает чтение затруднительным), а все документы хранятся в монолитной базе данных. Его антипод myBase родом из конца девяностых: здесь помимо чисто функционального интерфейса мы имеем крайне богатый набор настроек и функций. Если её положить в папку Dropbox, например (других способов синхронизации с другими устройствами всё равно нет), то при малейшем изменении коллекции приходится дожидаться закачивания на сервер сотен мегабайт информации.

Поворотный момент

Как бы да, но не совсем. Вероятно, дальнейшее содержание заметки читателю представляется очевидным: сейчас нам предложат собственный велосипед, который, конечно, окажется на голову выше любого существующего аналога. Однако этот мелкий проект для личных нужд сам по себе не заслуживал бы отдельной статьи; я пишу в большей степени потому, что мне показалось интересным поделиться опытом, полученным в процессе работы, и целым рядом неприятных сюрпризов, на которые я никак не рассчитывал. Я действительно не выдержал мытарств с myBase и TagSpaces и набросал собственный менеджер документов, ссылку на который приведу ближе к концу.

Цели и задачи

Поэтому с самого начала я решил, что готов лепить свой инструмент из любых компонентов, которые попадутся под руку, если это позволит ускорить дело. Начну с того, что у меня сейчас довольно напряжённая жизнь, и времени на полноценные хобби-проекты попросту нет. Кроме того, я пока что берусь реализовать лишь абсолютный минимум функциональности, без которого обойтись никак нельзя.

Формат данных и сохранение страниц

С учётом ранее сформулированных требований мне казалось, что выбор невелик: либо формат сохранения "веб-страница полностью", то есть основной HTML файл и папка со связанными ресурсами, либо формат MHTML. В каком виде хранить веб-страницы на диске? Когда я пытался работать с TagSpaces, мне пришлось пересохранить все свои документы так, чтобы имя папки с ресурсами начиналось с точки: тогда система распознавала их как "скрытые" и не отображала. Первый вариант мне сразу показался менее предпочтительным: невелика радость иметь на диске помойку из кучи файлов, из которых понадобится извлекать значимые документы, фильтровать лишнее при поиске и следить за целостностью при копировании.

Эта проблема скрыта из виду в myBase, поскольку всё хранится в базе данных, но в моём случае принцип простоты взял верх: очень хотелось хранить всё в виде обычных файлов на диске, чтобы не пришлось заниматься реализацией рутинных операций вроде копирования, переименования, удаления и синхронизации.

Простой способ сохранять MHTML был выброшен из Chrome этим летом, и я вот даже не знаю, в чём теперь предполагается хранить страницы? Формат MHTML переживает не лучшие свои времена. Кроме того, сохранение в формате MHTML не поддерживается в Chromium Embedded Framework, что тоже не прибавляет оптимизма. Понятно, что возможность пока что никуда не делась, есть сторонние расширения, но в целом это какой-то нехороший признак.

В итоге обе проблемы удалось разрешить малой кровью: я наткнулся на замечательный проект SingleFile, умеющий сохранять содержимое веб-страницы в отдельном независимом HTML-файле. Параллельно я стал искать простой способ сохранения страниц из браузера в указанную папку. Конечно, при этом размер файла растёт, да и содержимое выглядит несколько замусоренным, но в целом подход мне показался надёжным и простым, и я остановился на нём. Делается это путём преобразования всех связанных ресурсов в формат base64 и внедрения непосредственно в HTML.

Сейчас я просто пользуюсь расширением: это достаточно удобно, если не считать того, что надо вручную выбирать целевую папку для сохранения. SingleFile поставляется как в виде браузерного расширения, так и в виде приложения командной строки. Для вызова стороннего приложения из Chrome можно использовать расширение External Application Button — это ещё одно моё полезное открытие. В будущем, вероятно, постараюсь доработать приложение, чтобы упростить этот процесс. Кстати, приложение уже принесло пользу: с его помощью я сконвертировал коллекцию папок и файлов из TagSpaces в набор самостоятельных HTML-документов.

Хлопоты с GUI и браузером

Мне показалось, что для всевозможных простых операций с файлами и строками хорошо подходит Python, а поскольку в одном из моих рабочих проектов используется wxWidgets, выбор wxPython в качестве основного фреймворка выглядел логичным.

Далее, насмотревшись на косяки с отображением страниц в других программах, я сделал для себя вывод, что единственный надёжный способ справиться с ними — это внедрить в программу визуализатор на основе современного браузера, то есть Chrome или Firefox.

Оказалось, что "просто шлёпнуть браузер на форму" нельзя: как-то вот человечество не сумело надёжно и универсально справиться с этой задачей. Должен признаться, что в последний раз мне приходилось заниматься чем-либо подобным лет 15 назад, и я не ожидал никаких подвохов. Какой-нибудь listbox или кнопку на форму можно поместить в любом GUI-фреймворке, да ещё и сгенерировать кроссплатформенный код, и мне казалось, что в 2019 году отображение HTML тоже должно было быть повсеместно решённой проблемой.

Как видите, не я один занимался последние 15 лет другими делами — здесь тоже с места ничего не сдвинулось. Оказалось, что в wxWidgets, например, стандартный компонент "браузер" является кроссплатформенной обёрткой над системно-зависимым "браузером", который в случае Windows, например, означает Internet Explorer 7, причём ситуация в Windows Forms ничуть не лучше, и версии свежее IE9 доступны только при помощи нетривиальных манипуляций с реестром.

Поколебавшись, я решил попробовать сначала второй путь и достаточно быстро наткнулся на проект CEF Python: Python bindings for Chromium Embedded Framework, предназначенный ровно для задачи встраивания Chromium в приложения на Python. Дальше передо мной стоял выбор: менять фреймворк или искать альтернативный компонент для браузера.

При этом CEF Python фактически поддерживается энергией одного парня, сил и здоровья ему. Оцените ситуацию: Python является одним из самых популярных языков программирования в мире, Chrome — по сути монополист на рынке браузеров. Неужели это больше никому не нужно?..

Даже не буду углубляться в тему, вряд ли она того заслуживает. Впрочем, мне CEF Python в итоге не помог: хотя даже базовый пример интеграции с wxWidgets из репозитория проекта откровенно глючит, я попробовал повозиться с ним подольше, но так и не смог решить все возникающие проблемы.

Поскольку работаю я почти всё время с Windows, перспектива отказаться от кроссплатформенности меня, в общем, не особенно смущала. Я изучил подробнее компоненты на основе Chromium Embedded Framework и в итоге решил попробовать версию для C#.

После некоторой неизбежной возни на старте дело пошло гораздо быстрее: связка CefSharp и Windows Forms оказалась выигрышной, и мне удалось без особых проблем решить большинство поставленных технических задач.

О неиспробованном

Стандартный браузерный компонент фреймворка Qt под названием QWebEngineView основан на Chromium, так что, вероятно, будет работать не хуже CefSharp. В приложение на C# можно попробовать внедрить и FireFox, используя компонент Geckofx, но я про него ничего сказать не могу.

Возможно, что так оно и есть, но и wxWidgets можно считать если не первой, то второй опцией при выборе GUI фреймворка для приложений на Python или C++. У любителей Qt может возникнуть соблазн откомментировать: мол, брал бы Qt, не имел бы проблем. И по моему скромному мнению уж такая вещь как браузер должна встраиваться в любой более-менее развитый GUI фреймворк без плясок с бубном.

WebLibrary

На сегодняшний день выглядит оно (барабанная дробь) вот так: Вернёмся, однако, к моему приложению с рабочим названием WebLibrary.

Помимо чистого и лаконичного интерфейса здесь реализованы лишь самые наибазовые функции:

  • Отображение любого указанного каталога в системе как библиотеки документов.
  • Просмотр документов в окне браузера. Навигация по списку обычным образом (клавиши курсора, PgUp, PgDn, Home, End), пролистывание в браузере клавишами Space и Shift+Space.
  • Переименование документов.
  • Пометка документов как прочитанных или избранных с помощью горячих клавиш.
  • Сортировка документов по любому полю.
  • Обновление окна приложения при любых изменениях в библиотечной папке.
  • Сохранение настроек окна при выходе.

Это всё может показаться тривиальной функциональностью, но вот, скажем, сохранение размеров колонок в TagSpaces всё ещё не поддерживается — видимо, у авторов другие приоритеты.

Синхронизация как таковая не реализована, но я попросту держу библиотеку в Dropbox — в конце концов, это всего лишь папка с файлами. Статус (прочитано/избранное) попросту хранится в имени файла (прочитанный файл doc.html переименовывается в doc.html).

Если кто захочет помочь — буду только рад. В планах ещё доработать простые вещи вроде перемещения и удаления файлов, а также реализовать пометки произвольными тегами.

Выводы

Как я сказал с самого начала, удивительно, насколько инструментарий одного человека может отличаться от инструментария другого. Самые разные. При этом, судя по всему, единомышленников у меня немного, иначе бы проблем с поиском аналогов не возникло бы. Для меня пользоваться средством вроде WebResearch естественно, и я ощущал почти физический дискомфорт от его отсутствия. С другой стороны, аналогичные случаи происходят и с куда более меинстримным софтом: например, Microsoft не собирается обновлять десктопную версию OneNote, так что я вынужден пользоваться версией 2016 года, и рано или поздно придётся с неё тоже куда-то переезжать.

Мне по долгу службы редко приходится писать десктопные приложения от начала до конца, и я предполагал, что для моей задачи (одно окно, три компонента, тривиальные взаимодействия) подойдёт буквально любой инструмент для любого языка программирования. Ещё удивительно то, насколько непросто ориентироваться в нынешнем ландшафте библиотек и фреймворков. Вот прямо берём что угодно и в течение нескольких дней делаем.

Скажем, есть у меня два splitter-а, с помощью которых можно растянуть окно браузера. Оказалось, что реальность куда менее благожелательна, и нарваться на проблему можно просто на ровном месте. Вот кто бы предполагал? Так вот, восстановить их позиции после загрузки в wxWidgets крайне непросто, поскольку система выставляет их в позиции по умолчанию практически после всех доступных мне событий, и приходится заниматься всяческим хакерством, чтобы добиться нужного.

Практически всё, что требовалось, оказалось доступно из коробки: и сохранение/восстановление настроек приложения, и удобный интерфейс компонентов (скажем, не ожидал, что у компонента TreeView можно запросить полный путь от корня до любого дочернего элемента в виде строки), и нетривиальные средства вроде отслеживателя изменений содержимого папки. С другой стороны, видно, что в Windows Forms всё заточено под "бизнес-интерфейсы".

В любом случае время потрачено не зря, а результат можно признать удовлетворительным, так чего же ещё желать от жизни, верно?

Теги
Показать больше

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть