Найти в Дзене

Автоматический парсинг иконок сайтов на ruby

Зачем мне иконки сайтов? На своём сайте в разделе Каталог я собираю разные полезные ссылки. Чтобы быстро по ним ориентироваться, с самого начала я добавил к каждой ссылке иконку соответствующего сайта.
Но с иконками возникли проблемы. Не на каждом сайте была иконка favicon.ico, некоторые были доступны только по прямой ссылке href из <link rel="icon" href="..." />. Часто эта ссылка в имени содержала хэш в результате работы сборщика. Для таких сайтов я искал исходные картинки и сохранял себе. Не очень красиво, да и следить приходиться за актуальностью иконок, вдруг поменяют (как Яндекс недавно). Автоматическое обновление иконок Конечно, хочется автоматизировать ручной процесс поиска иконки для каждого сайта. Нам нужно рассмотреть в том числе такие случаи, когда ссылка на иконку присутствует только внутри <head> сайта. Средствами внутри браузера это сделать не получиться, так как доступ к DOM стороннего сайта запрещён. Кроссдоменные запросы через XHR или fetch будут блокироваться полити
Оглавление
Для каждого сайта иконка подставляется автоматом
Для каждого сайта иконка подставляется автоматом

Зачем мне иконки сайтов?

На своём сайте в разделе Каталог я собираю разные полезные ссылки. Чтобы быстро по ним ориентироваться, с самого начала я добавил к каждой ссылке иконку соответствующего сайта.

Но с иконками возникли проблемы. Не на каждом сайте была иконка
favicon.ico, некоторые были доступны только по прямой ссылке href из <link rel="icon" href="..." />. Часто эта ссылка в имени содержала хэш в результате работы сборщика. Для таких сайтов я искал исходные картинки и сохранял себе. Не очень красиво, да и следить приходиться за актуальностью иконок, вдруг поменяют (как Яндекс недавно).

Автоматическое обновление иконок

Конечно, хочется автоматизировать ручной процесс поиска иконки для каждого сайта. Нам нужно рассмотреть в том числе такие случаи, когда ссылка на иконку присутствует только внутри <head> сайта. Средствами внутри браузера это сделать не получиться, так как доступ к DOM стороннего сайта запрещён. Кроссдоменные запросы через XHR или fetch будут блокироваться политиками безопасности, а если воспользоваться iframe, доступ к его содержимому также будет закрыт для стороннего домена.

Поэтому все необходимые ссылки получим заранее, до этапа сборки. Я буду использовать
ruby и библиотеку nokogiri для парсинга html.

Далее опишу основной алгоритм.

Для каждого адреса url получим дерево html.

response = Net::HTTP.get_response(URI(url))
doc = Nokogiri::HTML(response.body)

Смотрим все ссылки, в которых описаны иконки

doc.css('head link[rel~=icon]').each do |link|
...
end

Если попалась svg иконка, то нам повезло, берём её и уходим

if link['type'].to_s.start_with?('image/svg')
href = link['href']
break
end

Из оставшихся выбираем иконку максимального размера

sizes = link['sizes'] || '1x1'
size = sizes.split('x')[0].to_i
if size > max_size
href = link['href']
max_size = size
end

Если ни одна ссылка не была найдена, возьмём дефолтную

if href == ''
href = '/favicon.ico'
end

Использование

Запишем наши результаты в js файл и сразу экспортируем как переменную. Это будет простой объект, где ключ это адрес сайта, а значение — адрес иконки.

export const icons = {/* ... */} // Record<string, string>

Теперь можно импортировать этот объект в нужные компоненты стандартным способом.

Исходный код https://github.com/nikalexxx/nikalexxx.github.io/tree/master/services/icons

Оригинал статьи