[Из песочницы] Выдергивание информации из URL, в стиле Slack и Twitter

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

2c98186f753241b4bd99352f4edad2c9.png d04cb0db96624f298c6fc1a8c301c3ad.png


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

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

1. Oembed


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

Есть такой формат как 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, то незачем скачивать его весь). Решение

Заключение


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

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

© Habrahabr.ru