Trouver et télécharger le favicon d'une page en PHP

Mars 2014

Le favicon, c'est la petite image affichée par le navigateur web, généralement dans les onglets, pour identifier facilement et rapidement un site sans ouvrir l'onglet :

Favicon dans les onglets de Firefox 27.0.1

Techniquement, le favicon est simplement une image, qui porte généralement l'extension .ico, ou plus rarement l'extension .png (car le PNG n'est pas compatible avec certains navigateurs anciens ou de mauvaise qualité comme Internet Explorer).

Chaque site héberge son propre favicon, et le navigateur a 2 façons de trouver ce favicon :

  • Avec l'URL par défaut : http;//www.site.com/favicon.ico
  • Ou alors en utilisant la balise link shortcut icon qui indique l'URL du favicon : <link rel="shortcut icon" href="//cdn.sstatic.net/stackoverflow/img/favicon.ico?v=5bcec08ba0c5">

Du coup, déterminer l'URL du favicon d'un site web n'est pas simple, il y a beaucoup de paramètres à prendre en compte, notamment pour lire et interpréter la valeur de la base link shortcut icon qui peut contenir des URL absolues ou relatives, et dont potentiellement dépendantes du contexte comme le protocole (http/https) ou la base <base href="..." />. Sans compter les éventuelles redirections HTTP qui viennent encore compliquer le travail...

Voilà donc une classe PHP que j'ai écrite et qui permet de trouver et télécharger très facilement le favicon d'un site simplement à partir d'une URL. Voici comment on l'utilise :

La page du jour est aperçu avant impression dans excel, bonne lecture.

<?php
require 'FaviconDownloader.class.php';

$favicon = new FaviconDownloader('http://koreus.fr');

if($favicon->icoExists){
	echo "URL du favicon : ".$favicon->icoUrl."\n";
	file_put_contents('favicon.'.$favicon->icoType, $favicon->icoData);
} else {
	echo "Aucun favicon";
}

echo "Détails :\n";
$favicon->debug();
?>

Voici la classe FaviconDownloader (les commentaires sont en anglais car je ratisse large ^^) :

Classe FaviconDownloader Télécharger la classe PHP FaviconDownloader, avec un fichier d'exemple inclus.
Télécharger
<?php
/**
* FaviconDownloader
* Find favicon URL and download it easy
* Requirements : PHP 5.2+ with curl extension
* © 2014 www.finalclap.com
* @copyright  2014 Vincent Paré
* @author     Vincent Paré
* @link       http://www.finalclap.com/
* @version    1
*/

class FaviconDownloader
{
	public $url			= null; // (string) Page URL
	public $pageUrl		= null; // (string) Page URL, after prospective redirects
	public $siteUrl		= null; // (string) Site root URL (homepage), based on $pageUrl
	public $icoUrl		= null; // (string) full URI to favicon
	public $icoType		= null; // (string) favicon type (file extension, ex: ico|gif|png)
	public $findMethod	= null; // (string) favicon url determination method (default /favicon.ico or found in <head> <link/> tag)
	public $error		= null; // (string) details, in case of failure...
	public $icoExists 	= null; // (bool)   tell if the favicon exists (set after calling downloadFavicon)
	public $icoMd5		= null; // (string) md5 of $icoData
	public $icoData		= null; // (binary) favicon binary data
	public $debugInfo	= null; // (array)  additionnal debug info
	public static $httpProxy = null; // (string) HTTP proxy (ex: localhost:8888)

	/**
	* Create a new FaviconDownloader object, search & download favicon if $auto is true
	*/
	public function __construct($url = null, $auto = true)
	{
		if(!$url) return;
		$this->url = $url;
		if(!$auto) return;
		$this->getFaviconUrl();
		$this->downloadFavicon();
	}
	
	/**
	* Download page and search html to find favicon URL. Returns favicon URL.
	*/
	public function getFaviconUrl()
	{
		// If already executed, don't need to search again
		if(!empty($this->icoUrl)){
			return $this->icoUrl;
		}
		
		// Check URL to search
		if(empty($this->url)){
			trigger_error('$this->url is empty', E_USER_WARNING);
			return false;
		}
		
		// Removing fragment (hash) from URL
		$url = $this->url;
		$urlInfo = parse_url($this->url);
		if(isset($urlInfo['fragment'])){
			$url = str_replace('#'.$urlInfo['fragment'], '', $url);
		}
		
		// Downloading the page
		$html = self::downloadAs($url, $info);
		if($info['curl_errno'] != 0){
			$this->error = $info['curl_error'];
			return false;
		}
		
		// Saving final URL (after prospective redirects) and get root URL
		$this->pageUrl = $info['effective_url'];
		$pageUrlInfo = parse_url($this->pageUrl);
		if(!empty($pageUrlInfo['scheme']) && !empty($pageUrlInfo['host'])){
			$this->siteUrl = $pageUrlInfo['scheme'].'://'.$pageUrlInfo['host'].'/';
		}
		
		// Default favicon URL
		$this->icoUrl = $this->siteUrl.'favicon.ico';
		$this->findMethod = 'default';
		
		// HTML <head> tag extraction
		preg_match('#^(.*)<\s*body#isU', $html, $matches);
		$htmlHead = isset($matches[1]) ? $matches[1] : $html;
		
		// HTML <base> tag href extraction
		$base_href = null;
		if(preg_match('#<base[^>]+href=(["\'])([^>]+)\1#i', $htmlHead, $matches)){
			$base_href = rtrim($matches[2],'/').'/';
			$this->debugInfo['base_href'] = &$base_href;
		}
		
		// HTML <link> icon tag analysis
		if(preg_match('#<\s*link[^>]*(rel=(["\'])[^>\2]*icon[^>\2]*\2)[^>]*>#i', $htmlHead, $matches)){
			$link_tag = $matches[0];
			$this->debugInfo['link_tag'] = &$link_tag;
			
			// HTML <link> icon tag href analysis
			if(preg_match('#href\s*=\s*(["\'])(.*?)\1#i', $link_tag, $matches)){
				$ico_href = trim($matches[2]);
				$this->debugInfo['ico_href'] = &$ico_href;
				$this->findMethod = 'head';
				
				// Building full absolute URL
				$urlType = self::urlType($ico_href);
				$this->findMethod .= ' '.$urlType;
				switch($urlType){
					case 'absolue_full':
						$this->icoUrl = $ico_href;
						break;
					case 'absolute_scheme':
						$this->icoUrl = $pageUrlInfo['scheme'].':'.$ico_href;
						break;
					case 'absolute_path':
						if(isset($base_href)){
							$this->icoUrl = $base_href.$ico_href;
							$this->findMethod .= ' with base href';
						} else {
							$this->icoUrl = rtrim($this->siteUrl, '/').'/'.ltrim($ico_href, '/');
							$this->findMethod .= ' without base href';
						}
						break;
					case 'relative':
						$path = preg_replace('#/[^/]+?$#i', '/', $pageUrlInfo['path']);
						if(isset($base_href)){
							$this->icoUrl = $base_href.$ico_href;
							$this->findMethod .= ' with base href';
						} else {
							$this->icoUrl = $pageUrlInfo['scheme'].'://'.$pageUrlInfo['host'].$path.$ico_href;
							$this->findMethod .= ' without base href';
						}
						break;
				}
			}
		}
		
		$this->icoType = self::getExtension($this->icoUrl);
		return $this->icoUrl;
	}
	
	/**
	* Télécharge le favicon (et vérifie son existance au passage)
	*/
	public function downloadFavicon()
	{
		// Check params
		if(empty($this->icoUrl)){
			return false;
		}
		
		// Prevent useless re-download
		if(!empty($this->icoData)){
			return false;
		}
		
		// Download favicon
		$content = self::downloadAs($this->icoUrl, $info);
		$this->debugInfo['favicon_download_metadata'] = $info;
		
		// Failover : if getting a 404 with favicon URL found in HTML source, trying with the default favicon URL
		if($content === false && $info['http_code'] == 404 && $this->findMethod != 'default' && !isset($this->debugInfo['failover'])){
			$this->icoUrl = $this->siteUrl.'favicon.ico';
			$this->findMethod = 'default';
			$this->icoType = self::getExtension($this->icoUrl);
			$this->debugInfo['failover'] = true;
			return $this->downloadFavicon();
		}
		
		// Download error
		if($content === false){
			$this->error = 'HTTP '.$info['http_code'];
			return false;
		}
		
		// Check favicon content
		if(strlen($content) == 0){
			$this->error = "Empty content";
			return false;
		}
		if(in_array($info['content_type'], array('text/html', 'text/plain')) || preg_match('#(</html>|</b>)#i', $content)){
			$this->error = "Seems to be HTML page";
			return false;
		}
		
		// All right baby !
		$this->icoData   = $content;
		$this->icoMd5    = md5($content);
		$this->icoExists = true;
		return true;
	}
	
	/**
	* Download URL as Firefox with cURL
	* Details available in $info if provided
	*/
	public static function downloadAs($url, &$info = null)
	{
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Don't check SSL certificate to allow autosigned certificate
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // Follow redirects (302, 301)
		curl_setopt($ch, CURLOPT_MAXREDIRS, 20);         // Follow up to 20 redirects
		curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0');
		if(!empty(self::$httpProxy)){
			curl_setopt($ch, CURLOPT_PROXY, self::$httpProxy); // Set HTTP proxy
		}
		$content = curl_exec($ch);
		$info['curl_errno'] = curl_errno($ch);
		$info['curl_error'] = curl_error($ch);
		$info['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
		$info['effective_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
		$info['redirect_count'] = curl_getinfo($ch, CURLINFO_REDIRECT_COUNT);
		$info['content_type'] = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
		curl_close($ch);
		
		if($info['curl_errno'] !== 0 || in_array($info['http_code'], array(403, 404, 500, 503))){
			return false;
		}
		return $content;
	}
	
	/**
	* Return file extension from an URL or a file path
	*/
	public static function getExtension($filename)
	{
		if(preg_match('#^(https?|ftp)#i', $filename)){
			$url = parse_url($filename);
			$filename = $url['path'];
		}
		
		$info = pathinfo($filename);
		return $info['extension'];
	}
	
	/**
	* Return URL type, either :
	* - absolute_full   ex: http://www.domain.com/images/fav.ico
	* - absolute_scheme ex: //www.domain.com/images/fav.ico
	* - absolute_path   ex: /images/fav.ico
	* - relative        ex: ../images/fav.ico
	*/
	public static function urlType($url)
	{
		if(empty($url)) return false;
		$urlInfo = parse_url($url);
		if(!empty($urlInfo['scheme'])) return 'absolue_full';
		if(preg_match('#^//#i', $url)) return 'absolute_scheme';
		if(preg_match('#^/[^/]#i', $url)) return 'absolute_path';
		return 'relative';
	}
	
	/**
	* Show object printable properties, or return it if $return is true
	*/
	public function debug($return = false)
	{
		$dump = clone $this;
		unset($dump->icoData);
		if($return) return $dump;
		print_r($dump);
	}
}
?>

Allez donc jeter un oeil sur cette page : nuit magique.

12 commentaires :
Seuls les 10 derniers commentaires sont affichés.
Cliquez ici pour afficher tous les commentaires
commentaire n°7525 par reornepen@manmail.xyz
reornepen@manmail.xyz dimanche 5 septembre 2021, 08:26
<a href=https://buypropeciaon.com/>;buy canadian finasteride 1 mg</a>
commentaire n°7545 par flarakeva@abdulah.xyz
flarakeva@abdulah.xyz lundi 6 septembre 2021, 01:16
https://buypropeciaon.com/ - propecia side effects
commentaire n°7940 par assulpabe@rqmail.xyz
assulpabe@rqmail.xyz vendredi 17 septembre 2021, 21:21
<a href=https://buyplaquenilcv.com/>;Plaquenil</a>
commentaire n°8021 par Amedeenda@mktmail.xyz
Amedeenda@mktmail.xyz dimanche 19 septembre 2021, 05:39
<a href=https://buyzithromaxinf.com/>;buy zithromax no prescription</a>
commentaire n°8133 par chishar@sqmail.xyz
chishar@sqmail.xyz mardi 21 septembre 2021, 16:30
<a href=https://buypriligyhop.com/>;Priligy</a>
commentaire n°8419 par akjldlkews
akjldlkews mercredi 29 septembre 2021, 13:59
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog for sale</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">frenchie puppies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">frenchies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">frenchie for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">french bulldog for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog for sale cheap</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">lilac french bulldog for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/";rel="dofollow">mini french bulldog for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">baby bulldog price</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">buy french bulldog near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">blue french bulldog puppies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">mini frenchies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">french bulldog baby price</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog puppies for sale</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog puppies</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">female merle french bulldog for sale</a>
<a href="https://akcfrenchbulldogsforsale.com/";rel="dofollow">teacup frenchies for sale</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">french bulldog teacup</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup frenchies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">mini french bulldog puppies for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">french bulldog blue merle</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">teacup french bulldog near me</a>
<a href="https://akcfrenchbulldogsforsale.com/";rel="dofollow">merle french bulldog for sale near me</a>
<a href="https://akcfrenchbulldogsforsale.com/"; rel="dofollow">merle frenchies for sale near me</a>
commentaire n°8624 par teptupdah@sqmail.xyz
teptupdah@sqmail.xyz mercredi 6 octobre 2021, 11:15
https://prednisonebuyon.com/ - prednisone online purchase
commentaire n°9108 par NaisseWab@rqmail.xyz
NaisseWab@rqmail.xyz samedi 6 novembre 2021, 03:50
<a href=https://astromectoli.com/>;Stromectol</a>
commentaire n°9410 par Aidenry@supmail.xyz
Aidenry@supmail.xyz jeudi 11 novembre 2021, 19:16
<a href=https://abuylasixshop.com/>;over the counter lasix</a>
commentaire n°9518 par Suehoky@sqmail.xyz
Suehoky@sqmail.xyz dimanche 14 novembre 2021, 12:51
<a href=https://aprednisonen.com/>;Prednisone</a>
facultatif
Facebook Twitter RSS Email
Forum Excel
Venez découvrir le nouveau forum excel question/réponse à la stackoverflow.com !
Forum Excel
hit parade n'en a rien a foutre du W3C Positionnement et Statistiques Gratuites Vincent Paré