分享一个比较实用的网页favicon图标获取方法

希米  (UID: 5950) [复制链接]
帖子链接已复制到剪贴板
帖子已经有人评论啦,不支持删除!

721 18

不用再费劲去查源代码啦

正常是从源代码查找:

  • rel="icon"

  • rel="shortcut icon"

  • rel="apple-touch-icon"


等字符,但是有些网站可能并非是写入meta标签中而是动态生成,搜索不到的;

当无favicon图标地址时候直接用此方法就方便啦;

博客友链,导航站,这些用上 省事很多;

调用:

https://favicon.splitbee.io/?url=网页地址

 

例如:

https://favicon.splitbee.io/?url=https://www.dalao.net/

https://favicon.splitbee.io/?url=https://www.ximi.me/

博客:www.ximi.me
已有评论 ( 18 )
  • 头像
    V tino (UID: 605)
    (ink.tino.im)
    1楼
  • 头像
    妙笔生花 (UID: 977)
    (小归客帐号、收藏ID9527)
    2楼

    这个不错噢。

  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    3楼
    tino https://ink.tino.im/api/content/links/fetch-metadata?url=dalao.net 支持

    不错,能开源嚒

  • 头像
    brynn (UID: 5053)
    4楼

    不错,能开源不

  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    5楼
    brynn 不错,能开源不

    用ai写了一个:

    例:

    https://i.ximi.me/logo.php?url=google.com

    代码:

    <?php
    /**
     * 仿 Splitbee Favicon 获取器
     * 用法: favicon.php?url=https://www.baidu.com
     */
    
    // 1. 获取并清理传入的 URL
    $target_url = isset($_GET['url']) ? trim($_GET['url']) : '';
    
    if (empty($target_url)) {
        header("HTTP/1.1 400 Bad Request");
        echo "Missing 'url' parameter.";
        exit;
    }
    
    // 确保 URL 带有协议头
    if (!preg_match("~^https?://~i", $target_url)) {
        $target_url = "http://" . $target_url;
    }
    
    // 解析出基础域名和 Scheme(用于后续拼接相对路径)
    $parsed_url = parse_url($target_url);
    if (!$parsed_url || !isset($parsed_url['host'])) {
        header("HTTP/1.1 400 Bad Request");
        echo "Invalid URL.";
        exit;
    }
    $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
    
    // 2. 初始化 cURL 用于请求
    function fetch_content($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);          // 设置超时
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 忽略SSL证书错误
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
        $data = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return [$data, $http_code];
    }
    
    $favicon_url = null;
    
    // 3. 步骤一:尝试抓取网页 HTML 并解析
    list($html, $code) = fetch_content($target_url);
    
    if ($code == 200 && !empty($html)) {
        // 使用 DOMDocument 解析 HTML,比正则更稳定
        $dom = new DOMDocument();
        // 禁用错误报错(防止 HTML 结构不规范弹出警告)
        libxml_use_internal_errors(true);
        $dom->loadHTML($html);
        libxml_clear_errors();
    
        $links = $dom->getElementsByTagName('link');
        foreach ($links as $link) {
            $rel = strtolower($link->getAttribute('rel'));
            // 匹配常见的 icon 关键词
            if (in_array($rel, ['icon', 'shortcut icon', 'apple-touch-icon'])) {
                $href = $link->getAttribute('href');
                if (!empty($href)) {
                    $favicon_url = $href;
                    break; // 找到第一个就跳出
                }
            }
        }
    }
    
    // 4. 步骤二:如果 HTML 中没找到,或者请求失败,尝试降级读取根目录下的 favicon.ico
    if (empty($favicon_url)) {
        $favicon_url = $base_url . '/favicon.ico';
    } else {
        // 5. 步骤三:补全相对路径
        if (substr($favicon_url, 0, 2) === '//') {
            $favicon_url = $parsed_url['scheme'] . ':' . $favicon_url;
        } elseif (substr($favicon_url, 0, 1) === '/') {
            $favicon_url = $base_url . $favicon_url;
        } elseif (!preg_match("~^https?://~i", $favicon_url)) {
            // 针对类似 "images/favicon.ico" 的相对路径
            $favicon_url = rtrim($target_url, '/') . '/' . ltrim($favicon_url, '/');
        }
    }
    
    // 6. 步骤四:请求真实的图标并输出
    list($icon_data, $icon_code) = fetch_content($favicon_url);
    
    if ($icon_code == 200 && !empty($icon_data)) {
        // 简单判断并设置图片响应头(也可以根据实际后缀或 mime 动态设置)
        if (strpos($favicon_url, '.png') !== false) {
            header('Content-Type: image/png');
        } elseif (strpos($favicon_url, '.svg') !== false) {
            header('Content-Type: image/svg+xml');
        } else {
            header('Content-Type: image/x-icon');
        }
        echo $icon_data;
    } else {
        // 如果连默认的也获取不到,可以返回一个你本地准备好的兜底透明图或默认图
        header('Content-Type: image/png');
        // 这里输出一个 1x1 的透明 PNG 或者是本地默认图标
        echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
    }
  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    6楼
    tino https://ink.tino.im/api/content/links/fetch-metadata?url=dalao.net 支持

    你这个与我用ai写的这个算法上好像有点点区别,可能是获取优先级不一样;

     

    获取的图标不一样;

     

    https://i.ximi.me/logo.php?url=www.ximi.me

     

    https://ink.tino.im/api/content/links/fetch-metadata?url=www.ximi.me

  • 头像
    佳茗 (UID: 819)
    (米表:34567888.xyz)
    7楼

    记得之前坛里也有佬整过一个  favicon.im

  • 头像
    V tino (UID: 605)
    (ink.tino.im)
    8楼
    希米 不错,能开源嚒

    可以让ai转写成php的

    @Slf4j public class LinkUtils { private static final int TIMEOUT = 5000; private static final SSLSocketFactory TRUST_ALL_SSL_SOCKET_FACTORY = createTrustAllSslSocketFactory(); private LinkUtils() { } public static LinkInfoDTO fetchLinkInfo(String url) throws IOException { if (!StringUtils.hasText(url)) { return new LinkInfoDTO(); } String bareUrl = url.replaceFirst("^(?i)https?://", ""); try { return doFetchLinkInfo("https://" + bareUrl); } catch (IOException e) { return doFetchLinkInfo("http://" + bareUrl); } } private static LinkInfoDTO doFetchLinkInfo(String url) throws IOException { LinkInfoDTO linkInfo = new LinkInfoDTO(); linkInfo.setUrl(url); Connection conn = Jsoup.connect(url) .timeout(TIMEOUT) .maxBodySize(0) .userAgent( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" + " Chrome/120.0.0.0 Safari/537.36") .sslSocketFactory(TRUST_ALL_SSL_SOCKET_FACTORY) .ignoreContentType(true) .ignoreHttpErrors(true) .followRedirects(true); Connection.Response response = conn.execute(); String contentType = response.contentType(); if (contentType != null && !contentType.contains("text/html") && !contentType.contains("xhtml") && !contentType.contains("xml")) { return linkInfo; } Document doc = response.parse(); String name = doc.title().trim(); if (!StringUtils.hasText(name)) { name = getMetaContent(doc, "meta[property=og:title]", "meta[name=twitter:title]"); } linkInfo.setName(name); String logo = getLinkHref(doc, "link[rel~=(?i)apple-touch-icon]", "link[rel~=(?i)icon]"); if (!StringUtils.hasText(logo)) { logo = getMetaUrl(doc, "meta[property=og:image]", "meta[name=twitter:image]"); } if (!StringUtils.hasText(logo)) { try { java.net.URI uri = new java.net.URI(url); logo = uri.getScheme() + "://" + uri.getHost() + "/favicon.ico"; } catch (Exception ignored) { // Ignore URI errors for fallback } } linkInfo.setLogo(logo); String description = getMetaContent(doc, "meta[name=description]", "meta[property=og:description]", "meta[name=twitter:description]"); linkInfo.setDescription(description); linkInfo.setCharset(response.charset()); return linkInfo; } private static String getMetaUrl(Document doc, String... selectors) { for (String selector : selectors) { Element element = doc.selectFirst(selector); if (element != null) { String url = element.attr("abs:content").trim(); if (StringUtils.hasText(url) && (url.startsWith("http") || url.startsWith("data:"))) { return url; } } } return ""; } private static String getMetaContent(Document doc, String... selectors) { for (String selector : selectors) { Element element = doc.selectFirst(selector); if (element != null) { String content = element.attr("content").trim(); if (StringUtils.hasText(content) && !content.startsWith("data:")) { return content; } } } return ""; } private static String getLinkHref(Document doc, String... selectors) { for (String selector : selectors) { Element element = doc.selectFirst(selector); if (element != null) { String href = element.attr("abs:href").trim(); if (StringUtils.hasText(href) && (href.startsWith("http") || href.startsWith("data:"))) { return href; } } } return ""; } private static SSLSocketFactory createTrustAllSslSocketFactory() { try { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new SecureRandom()); return sc.getSocketFactory(); } catch (Exception e) { log.error("Failed to create trust-all SSLSocketFactory", e); return (SSLSocketFactory) SSLSocketFactory.getDefault(); } } }

  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    9楼
    佳茗 记得之前坛里也有佬整过一个 favicon.im

    功能不错,但是广告太多太离谱了

  • 头像
    V tino (UID: 605)
    (ink.tino.im)
    10楼
    希米 你这个与我用ai写的这个算法上好像有点点区别,可能是获取优先级不一样; 获取的图标不一样; https://i.ximi.me/logo.php?url=www.ximi.me https://ink ...

    对的,我的优先级是  apple-touch-icon  >  icon  >  og:image  >  twitter:image  >  favicon.ico(兜底)

  • 头像
    账号软件之王 (UID: 63142)
    11楼
  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    12楼
    tino 可以让ai转写成php的 @Slf4j public class LinkUtils { private static final int TIMEOUT = 5000; private static ...

    根据你提供的代码转化好了

    logo:

    https://i.ximi.me/logo.php?url=www.ximi.me

    json:

    https://i.ximi.me/json.php?url=www.ximi.me

     

    代码如下:

    logo:

    <?php
    /**
     * 仿 Splitbee Favicon 获取器
     * 用法: favicon.php?url=https://www.baidu.com
     */
    
    // 1. 获取并清理传入的 URL
    $target_url = isset($_GET['url']) ? trim($_GET['url']) : '';
    
    if (empty($target_url)) {
        header("HTTP/1.1 400 Bad Request");
        echo "Missing 'url' parameter.";
        exit;
    }
    
    // 确保 URL 带有协议头
    if (!preg_match("~^https?://~i", $target_url)) {
        $target_url = "http://" . $target_url;
    }
    
    // 解析出基础域名和 Scheme(用于后续拼接相对路径)
    $parsed_url = parse_url($target_url);
    if (!$parsed_url || !isset($parsed_url['host'])) {
        header("HTTP/1.1 400 Bad Request");
        echo "Invalid URL.";
        exit;
    }
    $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
    
    // 2. 初始化 cURL 用于请求
    function fetch_content($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // 跟随重定向
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);          // 设置超时
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 忽略SSL证书错误
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
        $data = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return [$data, $http_code];
    }
    
    $favicon_url = null;
    
    // 3. 步骤一:尝试抓取网页 HTML 并解析
    list($html, $code) = fetch_content($target_url);
    
    if ($code == 200 && !empty($html)) {
        // 使用 DOMDocument 解析 HTML,比正则更稳定
        $dom = new DOMDocument();
        // 禁用错误报错(防止 HTML 结构不规范弹出警告)
        libxml_use_internal_errors(true);
        $dom->loadHTML($html);
        libxml_clear_errors();
    
        $links = $dom->getElementsByTagName('link');
        foreach ($links as $link) {
            $rel = strtolower($link->getAttribute('rel'));
            // 匹配常见的 icon 关键词
            if (in_array($rel, ['icon', 'shortcut icon', 'apple-touch-icon'])) {
                $href = $link->getAttribute('href');
                if (!empty($href)) {
                    $favicon_url = $href;
                    break; // 找到第一个就跳出
                }
            }
        }
    }
    
    // 4. 步骤二:如果 HTML 中没找到,或者请求失败,尝试降级读取根目录下的 favicon.ico
    if (empty($favicon_url)) {
        $favicon_url = $base_url . '/favicon.ico';
    } else {
        // 5. 步骤三:补全相对路径
        if (substr($favicon_url, 0, 2) === '//') {
            $favicon_url = $parsed_url['scheme'] . ':' . $favicon_url;
        } elseif (substr($favicon_url, 0, 1) === '/') {
            $favicon_url = $base_url . $favicon_url;
        } elseif (!preg_match("~^https?://~i", $favicon_url)) {
            // 针对类似 "images/favicon.ico" 的相对路径
            $favicon_url = rtrim($target_url, '/') . '/' . ltrim($favicon_url, '/');
        }
    }
    
    // 6. 步骤四:请求真实的图标并输出
    list($icon_data, $icon_code) = fetch_content($favicon_url);
    
    if ($icon_code == 200 && !empty($icon_data)) {
        // 简单判断并设置图片响应头(也可以根据实际后缀或 mime 动态设置)
        if (strpos($favicon_url, '.png') !== false) {
            header('Content-Type: image/png');
        } elseif (strpos($favicon_url, '.svg') !== false) {
            header('Content-Type: image/svg+xml');
        } else {
            header('Content-Type: image/x-icon');
        }
        echo $icon_data;
    } else {
        // 如果连默认的也获取不到,可以返回一个你本地准备好的兜底透明图或默认图
        header('Content-Type: image/png');
        // 这里输出一个 1x1 的透明 PNG 或者是本地默认图标
        echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
    }

    json:

    <?php
    // 1. 设置响应头为 JSON 格式
    header('Content-Type: application/json; charset=utf-8');
    
    // 2. 获取并清理 URL
    $target_url = isset($_GET['url']) ? trim($_GET['url']) : '';
    if (empty($target_url)) {
        echo json_encode([
            "status" => 400,
            "message" => "Bad Request",
            "devMessage" => "Missing 'url' parameter.",
            "data" => null
        ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        exit;
    }
    
    // 补全协议头
    if (!preg_match("~^https?://~i", $target_url)) {
        $target_url = "https://" . $target_url;
    }
    
    // 解析域名基础路径
    $parsed_url = parse_url($target_url);
    if (!$parsed_url || !isset($parsed_url['host'])) {
        echo json_encode([
            "status" => 400,
            "message" => "Bad Request",
            "devMessage" => "Invalid URL.",
            "data" => null
        ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        exit;
    }
    $base_url = $parsed_url['scheme'] . '://' . $parsed_url['host'];
    
    // 3. 构造纯原生的请求环境(模拟浏览器)
    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\r\n",
            'timeout' => 5,
            'follow_location' => 1,
            'ignore_errors' => true
        ],
        'ssl' => [
            'verify_peer' => false,
            'verify_peer_name' => false,
        ]
    ]);
    
    // 4. 抓取网页 HTML
    $html = @file_get_contents($target_url, false, $context);
    
    // 初始化返回的 Data 结构 (对应你的 LinkInfoDTO)
    $data = [
        "url" => $target_url,
        "name" => "",
        "logo" => "",
        "description" => "",
        "charset" => "UTF-8" // 默认给 UTF-8
    ];
    
    if ($html) {
        // 5. 尝试从 Response Header 中或 HTML 中提取 Charset
        // 这里简单通过正则从 HTML 匹配 charset
        if (preg_match('/<meta[^>]+charset=["\']?([\w-]+)/i', $html, $matches)) {
            $data['charset'] = strtoupper($matches[1]);
        }
    
        // 6. 提取网站标题 Name (优先找 <title>,找不到找 og:title)
        if (preg_match('/<title>(.*?)<\/title>/is', $html, $matches)) {
            $data['name'] = trim($matches[1]);
        }
        if (empty($data['name'])) {
            if (preg_match('/<meta[^>]+(?:property|name)=["\'](?:og|twitter):title["\'][^>]+content=["\']([^"\']+)["\']/i', $html, $matches)) {
                $data['name'] = trim($matches[1]);
            }
        }
    
        // 7. 用正则匹配 <link> 标签中的 icon / logo
        $favicon_url = '';
        if (preg_match('/<link[^>]+rel=["\'](?:shortcut )?icon["\'][^>]+href=["\']([^"\']+)["\']/i', $html, $matches)) {
            $favicon_url = $matches[1];
        } else {
            // 尝试匹配苹果设备高清图标
            if (preg_match('/<link[^>]+rel=["\']apple-touch-icon[^"\']*["\'][^>]+href=["\']([^"\']+)["\']/i', $html, $matches)) {
                $favicon_url = $matches[1];
            }
        }
    
        // 如果 HTML 没找到 logo,尝试找 og:image
        if (empty($favicon_url)) {
            if (preg_match('/<meta[^>]+(?:property|name)=["\'](?:og|twitter):image["\'][^>]+content=["\']([^"\']+)["\']/i', $html, $matches)) {
                $favicon_url = $matches[1];
            }
        }
    
        // 8. 路径补全逻辑
        if (empty($favicon_url)) {
            $favicon_url = $base_url . '/favicon.ico';
        } else {
            if (substr($favicon_url, 0, 2) === '//') {
                $favicon_url = $parsed_url['scheme'] . ':' . $favicon_url;
            } elseif (substr($favicon_url, 0, 1) === '/') {
                $favicon_url = $base_url . $favicon_url;
            } elseif (!preg_match("~^https?://~i", $favicon_url)) {
                $favicon_url = rtrim($target_url, '/') . '/' . ltrim($favicon_url, '/');
            }
        }
        $data['logo'] = $favicon_url;
    
        // 9. 提取网站描述 Description
        if (preg_match('/<meta[^>]+(?:name|property)=["\'](?:description|og:description|twitter:description)["\'][^>]+content=["\']([^"\']+)["\']/i', $html, $matches)) {
            $data['description'] = trim($matches[1]);
        }
    }
    
    // 10. 组装最终的目标 JSON 格式
    $result = [
        "status" => 200,
        "message" => "OK",
        "devMessage" => null,
        "data" => $data
    ];
    
    // 输出 JSON
    echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
  • 头像
    cjwlove (UID: 4475)
    13楼

    希米出品,必属精品。

    期待开源

  • 头像
    希米 (UID: 5950)
    (博客:www.ximi.me)
    14楼
    cjwlove 希米出品,必属精品。 期待开源

    代码就在楼上

  • 头像
    V tino (UID: 605)
    (ink.tino.im)
    15楼
    希米 根据你提供的代码转化好了 logo: https://i.ximi.me/logo.php?url=www.ximi.me json: https://i.ximi.me/json.php?url=w ...

    支持表情

  • 头像
    V D V8 (UID: 765)
    (Figo米表←点击进入 & gxb.cn待售)
    16楼

    学习

  • 头像
    chieng (UID: 21735)
    17楼

    不错!

  • 头像
    V 王先生 (UID: 2766)
    (王先生官网:www.wangxiansheng.com)
    18楼
域名市场
   域名载入中...
创建新帖
自助推广购买广告位
确认删除
确定要删除这篇帖子吗?删除后将无法恢复。
删除成功
帖子已成功删除,页面将自动刷新。
删除失败
删除帖子时发生错误,请稍后再试。