Как мы делаем скриншоты страниц

    Привет!

    Это наша первая статья на хабре, и в ней мы хотели бы рассказать вам о том, как мы делаем для своего сервиса красивые скриншоты страниц и как к этому пришли, на какие грабли наступали.

    Сервис этот нам крайне важен для показа ленты обновлений. И чем быстрее и ближе к реальности (читай — поддержка флеша) он работает, тем приятнее пользователям.

       

     
    После изучения интернетов были найдены следующие варианты:  
    • 1. Сайты
    • BrowserShots — видимо, заслуженный ветеран. Бесплатен, поддерживает кучу браузеров, за денежку может делать скриншоты с более высоким приоритетом, чем без неё. Радость заключается в том, что он opensource, исходники — на Python. А печаль в том, что скриншоты в большей части браузеров просто не работают, флеш не поддерживается, некоторые из вроде бы сделанных скриншотов битые. Ориентирован на верстальщиков.
    • WebShots Pro — тоже с первой страницы гугла. У них есть апи, что уже интересно, стоимость использования которого зависит от объемов, размеров картинок и наличия метки сервиса. Но — вы не поверите, пацаны — они делают скриншоты при помощи IE! В общем, с ними всё ясно. Ну, на самом деле они и помимо этого выглядят сомнительно.
    • thumbalizr beta — многообещающие ребята, доступно апи, платный сервис заключается в отключении вотермарка, но не поддерживают флеш.
    • url2png — все предыдущие сайты, кроме в какой-то степени thumbalizr, выглядят, честно говоря, раздолбайски-пофигистично. И, мне кажется, работают так же. url2png оставляет очень приятное впечатление — правда, опять же не снимает флеш и стоит дороже остальных.
    • websnapr — ну, серединка на половинку, ничего особенного.
    • 2. Программы  
    • kthml2png — заслуженный старичок. Почти не поддерживается. В общем-то, мы его и не пробовали.
    • CutyCapt — более актуальная программка. Но — «CutyCapt has a number of known quirks». Иногда ему просто не удается снять скрин. Подружить со флешом — трудно.
    • PhantomJS — насколько я знаю, самый активный из подобных проектов. Куча возможностей, не только скриншоты. Можно исполнять Javascript в контексте страницы, например. Но позиция автора насчет плагинов (флеша в том числе) такова — должно работать, но я ничего гарантировать не хочу и не буду. Ниже будут описаны интересные проблемы с этим :)

        В общем, посмотрев на это и не увидев идеала, нашей первой попыткой реализовать максимально аутентичные скриншоты стал запуск Firefox под Xvfb, и скриншот всего экрана по таймату в 30 секунд.
    Xvfb — незаменимая утилита для таких целей. Это виртуальный фреймбуфер, позволяющий не ставить на сервер полноценные иксы, и запускать «дисплеи» с любым разрешением. Ну и приложения в них :)  
    Способ был, конечно, неидеален. Во-первых, файрфокс оказался строптив и все время норовил подсунуть окошко обновления себя или плагинов. Во-вторых, иногда просто гробил скриншоты (получалась одноцветная картинка) или вис. В-третьих, делать скриншот строго через 30 секунд явно неоптимально — если сайт загрузился раньше, нечего ждать :) И память он, конечно, жрал.
     
    Следующим шагом мы попробовали написать собственную утилиту на Qt — чтобы иметь возможность править её под себя, ведь всё-таки сервис изготовления скриншотов и правда очень важен для нас. Qt не зря пользуются авторы большинства подобных программ — там современный движок и удобный API. Проблем стало меньше, но они остались — иногда утилита висла, иногда делала битый скриншот… И почему-то не работала на боевых серверах со флешом.
     
    Сам разработчик этой утилиты в какой-то момент предложил нам перейти на PhantomJS — чем мы и воспользовались. Ситуация снова стала несколько лучше, но не идеальной. Он тоже иногда виснет, причем есть как ссылки, всегда вызывающие ошибку, так и случаи спорадических отказов. Флеш тоже не работал, показывал черные прямоугольники, что в принципе уже нас более-менее устраивало.
    Зато PhantomJS за счет возможности выполнять js на странице позволил нам получать из DOM описание страницы (из мета-тегов или секретным алгоритмом прямо из текста), заголовок, и потенциально вообще делать всякие интересные вещи, например — определять страницы, запрещающие показывать себя в ифрейме, что тоже довольно критичная для нас фича. HTML может быть таким месивом, что работать с ним на сервере может быть мучением; браузерные же движки десятилетиями приучены терпеть любые издевательства.
    Так бы мы с ним долго и прожили, но когда к нам стали добавлять порядочное количество ссылок, Фантом обрадовал нас чем-то новеньким — работающие параллельно несколько копий программы, обрабатывающие ссылки со флешом, стали ВНЕЗАПНО его снимать, но, скажем, в один скриншот попадал ролик из другой ссылки, а во втором скриншоте на месте ролика была комбинация сразу двух роликов. Короче, кровь-кишки-расчлененка.
     
    Да ну его нафиг, подумал я, и вспомнил, что Google Chrome вообще имеет встроенный флеш-плеер, да и уже понятно было, что все самописные программы страдали от недостатка надежности. Хром в этом упрекнуть сложно :) Кроме того, под него крайне просто писать расширения на чистом js, которые могли делать все то же самое, что и PhantomJS, и даже больше.
    Также у него нашлись все волшебные ключики командной строки, позволяющие запускать его без интерфейса и без каких-либо назойливых предложений. Ещё он умел писать в STDERR, и работать в режиме «инкогнито», что обеспечивает максимально повторяющиеся результаты снятия скриншотов. Нам стала доступна вся мощь CSS-селекторов и у нас заработал флеш.
    В общем-то, Google Chrome исполнил все наши эротические мечты ;)
    Собственно скриншоты снимаются снимком всего экрана, в котором работает браузер, ну, плюс некоторые особые случаи для особых, волшебных сайтов.
     
    В каком-то смысле история сделала виток спирали — мы начали с браузера, и закончили браузером. Просто работать над этой задачей с Хромом оказалось намного проще. Да, можно и под Firefox написать расширение, которое, наверно, сможет делать все то же самое, что мы делаем Хромом — но это будет труднее. После написания тулбара для Firefox я гарантирую это :)
    И с памятью он работает бережнее. Кстати, в Хроме есть даже возможность снимать скриншоты без внешних утилит — их можно получать в виде data-uri, но, ирония, в таком режиме не снимается флеш :)
     
     
    Итак, вот небольшой чеклист, как получить собственный сервис скриншотов, избежав долгих проб и ошибок:
     
    • Используйте очередь заданий. Я советую Resque, но это может быть и *MQ, и Kestrel, даже и реляционная БД. Главное, чтобы нормально работала с несколькими параллельными обработчиками.
    • Запустите нужное количество сессий Xvfb, на разных «дисплеях»
    • Напишите обработчик очереди, который будет запускать Google Chrome с нужными параметрами, вроде DISPLAY=:1 /opt/google-chrome/google/chrome .... Список параметров можно почерпнуть здесь: http://src.chromium.org/svn/trunk/src/chrome/common/chrome_switches.cc
    • Напишите расширение для Google Chrome, которое будет следить за завершением загрузки страницы и посылать сигнал во внешний мир. Документация: http://code.google.com/chrome/extensions/getstarted.html
    • Натравите на очередь заданий нужное количество обработчиков, каждый из которых будет использовать свой DISPLAY. Не забудьте проверить, что DISPLAY доступен. Это несложно сделать при помощи консольных утилит из семейства X11
    • Снимайте скриншот снимком экрана по сигналу из расширения, или используйте метод captureVisibleTab, но тогда флеш (и другие плагины) сниматься не будет

     
    Не забудьте про шаги «…» и «Profit!» ;)

    В итоге, я считаю, у нас получилось решение, превосходящее все коммерческие аналоги, что я видел.
    Surfingbird
    0.00
    Company
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 34

      +3
      А ваша реализация умеет снимать скриншоты во всю высоту страницы, а не только высотой в экран?
        0
        Не умеет, нам это не нужно.

        Если бы делали, то, видимо, скроллили js-ом страницу и каждый раз посылали бы сигнал на скриншот. Потом сшивали бы.
          0
          Фишка своего решения в том, что его можно подкрутить как угодно :)
            0
            Это понятно. Но, например, решение на CutyCapt тоже можно подкрутить — есть исходники на Сях. Снимает страницу целиком и на порядок быстрее чем любая другая реализация, которая мне знакома. Из явных минусов только отсутствие поддержки флеша. Ваша реализация подходит в вашем конкретном случае, но если «подкрутить» перемотку страницы и склейку — это займет еще больше времени и ресурсов, а значит в тех задачах где это критично подобное решение не подойдет. В общем, есть еще куда стремиться в создание идеального и универсального решения ;)
              0
              У Фантома тоже исходники доступны, но вот про его надежность я отписался. CutyCapt не пробовали, но концептуально они похожи, насколько я понимаю, так что я подозреваю такие же проблемы :)

              Ну а в вообще, да, конечно, случаи и границы применимости они разные бывают :)
                +2
                Как это нет поддержки флеша?) У меня скринит. Главное не забыть поставить флешплагин в систему и кутикапт указывать ключик при запуске — плагинс-энаблед или как то так, точнее не могу упомнить.
                  0
                  Это все черная магия, пример с тем же фантомом и нашим самописным решением на Qt тому подтверждение.

                  Ну, может быть, именно в CutyCapt все прекрасно и никогда не глючит, я не могу утверждать :)
                  0
                  CutyCapt прекрасно работает с flash, просто поставьте адобовский плагин по инструкции. Там есть один момент — надо как-то эмпирически подбирать время для загрузки сайта, иначе вместо отдельных флеш-блоков могут быть пустые квадратики. Хотя. это может быть от того, что я деалал всю схему на относительно медленной машине и на хорошей железке все будет летать.
                0
                А, кстати, ещё можно создать виртуальный экран невероятной высоты. Но будут проблемы, если body высотой 100%, и что-нибудь привязано ко дну страницы, или просто есть фон по всей высоте (просто белизну довольно легко распознать и отрезать).
                0
                есть отличный бесплатный сервис s-shot.ru
                  0
                  Заказал скриншот нашей морды — ждал 40 секунд, получил белое поле… С другой стороны, страничка ютьюба наугад была готова через пять секунд, флеш виден.

                  На самом деле, когда речь идет о важной фиче, тем более что-то такое про реалтайм, надо иметь над ней контроль. Не обязательно «надо сделать самим», но исходники точно хочется.
                  0
                  Рекомендую ScreenPresso
                  Можно получить вот такие скриншоты
                    0
                    Но это же десктопное приложение, а нам нужно было серверное автоматизированное.

                    Можно, конечно, запустить в Xvfb Wine, и там запускать приложение и манипулировать мышью/клавиатурой, но… :)

                    Кстати, советую clip2net, скриншот экрана в два клика (надо чуть настроить, кажется, изначально больше). Аналогов по этому критерию не видел. Порисовать, написать текст — можно.
                      0
                      У меня clip2net и ScreenPresso в паре работают.
                      Когда нужен быстрый скриншот — конечно clip2net :)
                      Аналог clip2net — floomby, но он какой-то странный.
                        0
                        клиптунет очень хороший (все остальное так себе), но в последнее время он не стабилен, из-за нагрузок. нас такой вариант не устраивает
                      0
                      у вашего проблемы с antialiasing
                      +2
                      addons.mozilla.org/ru/firefox/addon/fireshot/

                      а так floomby, только пара лишних кликов до получения прямой ссылки.
                        0
                        Про ФФ в статье написано. И вроде это расширение тоже требует интерактивности, мы на него смотрели года полтора назад.
                        0
                        А Selenium пробовали?
                          0
                          Селениум у нас в другом месте используется, а тут вроде как и незачем, открыть нужный урл проще через командную строку, и у расширения минимальный оверхед. Селениум же жрал бы ещё себе памяти и усложнял конструкцию.

                          На самом деле, было бы прекрасно, если бы таки прямо captureVisibleTab делал скрин всего, что видно на экране… Очень удобно — он делает скрин в формате data-uri, можно аяксом куда угодно отправить.
                          0
                          у меня у одного такое чувство, что эта статья уже была на хабре?
                            0
                            adobe.browserlab?
                              0
                              судя по описанию прекрасная штука, но совсем про другое.
                              0
                              Возможно более простым и надежным было бы написать свое xul приложение и запускать его через firefox для того что бы сохранились все его настройки xulrunner. Приложение вышло бы на 50 строк js кода, за то не пришлось бы делать снимок из фреймбуффера.
                              В xulrunner доступен метод drawWindow
                              Фактически это должно полностью решить проблемы битых скриншотов, и одновременно дать полный доступ к DOM и JS показываемого сайта. Можно подписыватся на любые события, модифицировать сайт — например вырезать\затенять рекламу и тд.
                                0
                                Спасибо, я посмотрю :)

                                Но, боюсь, мы уже сели на крючок — работает хорошо, грабли отмечены флажками, нужды менять нет. Но все равно интересно!
                                0
                                Когда мне понадобилось сделать скриншот всей страницы, я побродил, поискал, не нашел, да и написал маленькую программу Page to Image для Mac OS. Умеет делать скриншот в полный рост на WebKit при 1024х768. Никаких доп. функций, написал и забыл. Прошу хабросообщество не минусовать; не ради пиара, вдруг кому полезно будет. Программа бесплатная.
                                  0
                                  QT Embedded Webkit отлично справляется с задачей, и довольно много библиотек, для примера github.com/csquared/IMGKit
                                    0
                                    Выкладываю у вас ссылки на один и тот же сайт — скриншоты то делаются, то не делаются. На некоторых первых ссылках до сих пор лоадбар крутится.
                                      0
                                      можно примеры?
                                      0
                                      Да, очень нужно. Мы сами, конечно, постоянно пользуемся собой же, и если в лентах видим пустые места, реагируем. А тут вон оно чо деется-то за нашей спиной…
                                        0
                                        Мы вроде как все починили. Кажется, слишком много обработчиков на одной машине запускали, хромы иногда втупляли)
                                          0
                                          Вижу. Теперь штуки 4 всего не работает и новые скрины стали быстрее делаться.

                                      Only users with full accounts can post comments. Log in, please.