Зачем мне иконки сайтов?
На своём сайте в разделе Каталог я собираю разные полезные ссылки. Чтобы быстро по ним ориентироваться, с самого начала я добавил к каждой ссылке иконку соответствующего сайта.
Но с иконками возникли проблемы. Не на каждом сайте была иконка 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