Многие пользуются Slack, Twitter и видели такие штуки:

Как это работает и как такое сделать самим?

Для тех, кому нужно срочно и сразу: я написал готовый сервер для возврата oembed-like информации об URL. Работающий вариант можно посмотреть здесь.

А теперь по пунктам, откуда можно выдрать интересную инфу:

1. Oembed


Про <title> и <description> известно всем, на этом останавливаться не будем.

Есть такой формат как Oembed. Многие крупные порталы имеют в своем распоряжении oembed endpoint. Например:


Из oembed информации можно получить гораздо более полную выжимку страницы чем пытаясь распарсить html, поэтому приоритетной задачей при парсинге URL является поиск oembed ссылок.

Это описано в п.4 на сайте oembed.com:

В head страницы нужно добавить по крайней мере одну ссылку такого вида:

<link rel="alternate" type="application/json+oembed"
  href="http://flickr.com/services/oembed?url=http%3A%2F%2Fflickr.com%2Fphotos%2Fbees%2F2362225867%2F&format=json"
  title="Bacon Lollys oEmbed Profile" />
<link rel="alternate" type="text/xml+oembed"
  href="http://flickr.com/services/oembed?url=http%3A%2F%2Fflickr.com%2Fphotos%2Fbees%2F2362225867%2F&format=xml"
  title="Bacon Lollys oEmbed Profile" />

Как можно понять, здесь задается тип endpoint'а: xml или json. Соответственно, при парсинге html, если мы находим ссылку на oembed, то можно выдохнуть и взять нужную информацию из oembed ednpoint. Парсинг oembed для Golang реализован в моей библиотеке.

2. Open Graph


Это дополнительная metadata на странице, которую используют Google+, Facebook и другие для встраивания содержимого страниц в свои ленты. Подробнее можно прочитать здесь. Эта разметка используется на огромном количестве сайтов, даже на Хабре. Например, посмотрите исходный текст этого поста и поищите по 'og:'.

Парсинг OpenGraph для Golang реализован в моей библиотеке (наиболее полный функционал по сравнению с аналогами).

3. Собираем информацию по крупицам из того что есть


Если ни oembed ни opengraph на странице нету, то довольствуемся имеющимися данными:

  • <title> — для заголовка
  • <meta name="description"> — для описания
  • <link rel="image_src"> — для картинки
  • * если картинки нету, то выдираем первую картинку из основного текста
  • * если описания нету, то выдираем часть текста из основного контента как описание

Для выдирания контента я использую github.com/dyatlov/go-readability — это форк оригинального go-readability c добавлением whitelabeled attributes (это необходимо для корректного вытаскивания картинок).

Это реализовано в github.com/dyatlov/go-htmlinfo.

4. Генерация oembed для ресурсов не являющихся html


Ссылки могут быть не только на страницы, но и на картинки или видео, на архивы и т.д. Для таких ссылок никакой информации получить естественно не получится. Поэтому придется генерировать ее самим.

В Golang есть такая штука как http#DetectContentType. Основываясь на этой информации, можно получить тип контента, находящегося по указанному адресу, основываясь на первых нескольких сотнях байтов. Затем, базируясь на типе контента, можно предпринимать следующие шаги. В случае изображений я использую декодирование хедеров изображения и таким образом получаю их размеры, которые затем возвращаю в ответе. Все это реализовано в соответствующей библиотеке.

Защищаемся


Задачи (кроме очевидных):

1. Разворачивать redirect'ы и при этом не уходить в бесконечный цикл. Например, bit.ly/1cWYIdC должно стать Хабром. Решение
2. Защита от атаки на локальные ресурсы (см. атака на pocket). Решение
3. Грузить только ограниченное количество информации (если пришла ссылка на ISO образ Linux, то незачем скачивать его весь). Решение

Заключение


Прошу прощения за большое количество отсылок к своим репозиториям и сумбурное описание. Старался сделать код понятным и при этом разбить его на логические модули, которые могут быть потом повторно использованы независимо. Надеюсь что кому-то это пригодится.

Исходный код готового сервера здесь. А он сам здесь.

Для чего можно использовать: автоматическая подгрузка url preview для url'ов из комментариев, попап с preview в постах с url'ами и другие связанные с показом содержимого вещи.

Комментарии (4)


  1. vsadm
    19.10.2015 19:46

    А еще бывают одноразовые ссылки, GET на которые можно сделать только один раз.


    1. xytop
      19.10.2015 19:59

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


  1. deliya
    20.10.2015 13:50

    У Яндекса есть API валидатора микроразметки, с помощью которого можно извлекать всю информацию, размеченную популярными стандартами семантической разметки. Поддерживается в том числе и Open Graph.


    1. xytop
      20.10.2015 14:50

      Спасибо за комментарий. Мой проект это все же больше low-level аналог embed.ly ( embed.ly/embed ) — микроразметка как таковая тут не особо нужна, если все что нужно это получить oembed-like json на выходе.
      Попробую добавить парсинг микроразметки, посмотрим что получится.