Please enable Javascript to view the contents

导航网站favicon图标显示方案

 ·  ☕ 6 分钟

ChatGPT免费站点 和 AIGC 工具导航

前一段写了一个AI工具导航,但是发现有些网站的favicon图标显示不出来(某外服务可显示,某内服务无法显示),于是就想办法解决这个问题。

favicon图标

导航网站制作上没什么技术难度,无非就是爬取一些网站的信息,然后列表展示出来。当然展示的时候,favicon图标是必不可少的,要不然就不好看了。

favicon图标是网站的标志,一般在浏览器的地址栏、标签页、书签栏等地方显示。一般是一个16×16, 32×32的小图标,通常存放在网站的根目录,图片格式是ico、png、svg等,名字常为faviconlogo等。

以下为favicon图标在HTML页面中声明:

1
<link rel="icon" href="https://www.baidu.com/favicon.ico" type="image/x-icon" />

**注意,**我们应直接使用rel="icon",现在很多网站依然使用rel="shortcut icon",这是一种过时的用法,可以被淘汰掉了。

favicon除了使用线上地址,还支持base64格式内联,例如:

1
<link rel="icon" href="data:image/x-icon;base64,AAABAA...8AAA==" type="image/x-icon" />

favicon图标显示方案

1. 使用 Favicon url

favicon图标不同于一般的图片素材,一般都不会有防盗链的问题,所以我们可以直接使用线上地址。即:

1
<img src="https://www.baidu.com/favicon.ico" />

所以,你在获取网站信息的时候,记得获取favicon图标的url,直接使用即可。

2. 存储 favicon 图标

如果你不想使用线上地址,那么你可以将网站favicon图标存储到你的服务器上,自己做映射。这样做的好处是,你可以将favicon图标存储到你的CDN上,统一管理。缺点是,你需要自己维护这些图标,当然,你可以使用一些工具来自动化处理。

当然,你也可以存储为base64格式,这样就不需要额外的存储空间了,就是网站数据文件会变大。

3. 使用第三方服务

如果你不想自己存储favicon图标,那么你可以使用第三方服务,例如:Google Shared Stuff (S2)、faviconkit.com、icons.duckduckgo.com等。

Google Shared Stuff (S2)

Google Shared Stuff (S2) 提供了一个统一形式 URL api服务,该 URL 可以自动提取相应网站的图标图像并返回。

其使用方法为 https://www.google.com/s2/favicons?domain=abc.com&sz=24,您需要将 abc.com 替换为实际网站的域名。

如:https://www.google.com/s2/favicons?domain=blog.mzh.ren&sz=24,就会返回我博客的favicon图标,直接通过<img>标签引用即可。

faviconkit.com

faviconkit.com api:invisible-scarlet-centipede.faviconkit.com/{website}/{size},替换 website 为实际网站的域名,size 为图标大小。

如:https://invisible-scarlet-centipede.faviconkit.com/blog.mzh.ren/24,就会返回我博客的favicon图标。

faviconkit.com

通过以上免费服务,我们可以获取到网站的favicon图标,但是这些api都是外网的,如果你的网站是内网,那么就无法使用了。我到现在还没找到一个内网的api服务,如果你知道,欢迎告诉我。

favicon图标批量获取

我导航网站的数量大概有900个,如果手动获取,那就太麻烦了,所以我写了一个脚本来自动获取,这样就省了很多时间。

puppeteer方式

Puppeteer | Puppeteer

Puppeteer is a Node.js library which provides a high-level API to control Chrome/Chromium over the DevTools Protocol. Puppeteer runs in headless mode by default, but can be configured to run in full (“headful”) Chrome/Chromium.

puppeteer是一个用于浏览器自动化的 Node.js 库,它提供了一组用来操纵Chrome的API。

2017年,谷歌公开发布了 Puppeteer,由 Chrome DevTools 团队开发,使其比其他类似项目具有重大优势,因为它得到了世界上使用最广泛的浏览器的同一家公司的机构支持。

具体操作步骤如下:

  1. 首先通过head请求检查网站是否有favicon图标,如果有,那么直接使用,否则进入下一步。

2.通过puppeteer打开网站,然后获取网站的favicon图标地址。

如果这两步都失败了,就使用自己的默认图标。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const http = require('http');
const https = require('https');
const fs = require('fs');
const puppeteer = require('puppeteer');
const { EventEmitter } = require('events')
EventEmitter.defaultMaxListeners = 20 

const chatgptJson = require('./chatgpt.json');

const processFaviconUrl = async (item) => {
    const url = item.url;
    const url_object = new URL(url);
    const website_url = url_object.protocol + "//" + url_object.host
    const default_favicon_url = website_url + '/favicon.ico';

    try {
        console.log("check website url")
        const website_url_available = await checkURLAvailable(website_url);
        if (!website_url_available) {
            console.log('website url not available');
            return item;
        }
    } catch (err) {
        console.log('Error:', err);
    }

    try {
        console.log("check default favicon url")
        const default_favicon_url_available = await checkURLAvailable(default_favicon_url);
        console.log(`The URL is ${default_favicon_url_available ? 'valid' : 'invalid'}`);
        if (default_favicon_url_available) {
            console.log('has favicon');
            item.favicon = default_favicon_url;
            return item;
        }
    } catch (err) {
        console.error('Error:', err);
    }


    try {
        console.log('use puppeteer');
        const browser = await puppeteer.launch({
            headless: "new",
        });
        const page = await browser.newPage();
        await page.setDefaultNavigationTimeout(0);
        await page.goto(item.url);
        // get favicon
        const favicon = await page.evaluate(() => {
            const favicon = document.querySelector('link[rel="icon"]') || document.querySelector('link[rel="shortcut icon"]');
            return favicon ? favicon.href : null;
        });

        console.log(favicon);

        if (favicon) {
            item.favicon = favicon;
            console.log(item);
        }
        await browser.close();
        return item;
    } catch (err) {
        console.log(err);
    }

}

function checkURLAvailable(url) {
    return new Promise((resolve, reject) => {
        const protocol = url.startsWith('https') ? https : http;
        protocol
            .request(url, { method: 'HEAD' }, (res) => {
                if (res.statusCode >= 200 && res.statusCode < 400) {
                    resolve(true);
                } else {
                    resolve(false);
                }
            })
            .on('error', (err) => {
                reject(err);
            })
            .end();
    });
}


Promise.all(chatgptJson.map(processFaviconUrl)).then((results) => {
    fs.writeFileSync('./chatgpt-new.json', JSON.stringify(results));
    console.log('All done!');
}).catch((err) => {
    console.error(err);
});

以上代码每次获取20-50个网站的favicon图标,多了就会卡死。我对异步和并发的一无所知,暂时没找到解决办法,哪位大佬发现了问题,欢迎指点一下,不胜感激。

playwright方式

microsoft/playwright: Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.

2020 年 1 月 31 日,微软发布Playwright。Playwright 在很多方面与 Puppeteer 非常相似。API 方法在大多数情况下是相同的,并且默认情况下 Playwright 还捆绑了兼容的浏览器。

Playwright 最大的区别在于跨浏览器支持。它可以驱动 Chromium、WebKit(Safari 的浏览器引擎)和 Firefox。

以下是使用playwright获取favicon图标的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const fs = require('fs');
const { chromium } = require('playwright');

(async () => {
    const data = fs.readFileSync('chatgpt.json');
    const websites = JSON.parse(data);

    const browser = await chromium.launch();
    const context = await browser.newContext();

    for (let website of websites) {
        const page = await context.newPage();
        try {
            await page.goto(website.url);
            const faviconUrl = await page.$eval(
                'link[rel="icon"]',
                (el) => el.href
            ) || page.$eval('link[rel="shortcut icon"]', (el) => el.href);
            console.log(`Favicon URL for ${website.url}: ${faviconUrl}`);
            website.favicon = faviconUrl;
        } catch (err) {
            console.log(err);
            continue;
        }

        await page.close();
    }

    fs.writeFileSync('chatgpt_new.json', JSON.stringify(websites));

    await context.close();
    await browser.close();
})();

代码运行后,会在当前目录下生成chatgpt_new.json文件,里面包含了所有网站的favicon图标地址。

以上代码一次性获取了所有网站的favicon图标,感觉playwrightpuppeteer 强壮一些,访问不了也不会卡死。(纯属个人感觉)

以上两段代码都是在ChatGPT的帮助下完成的,至于原理,我也不太清楚,请路过的大佬指点一下。

代码库

mzhren/favicon: get sites favicon by playwright and puppeteer

含有两种方式获取favicon图标的代码,以及 chatgpt站点数据 chatgpt.json 和 aigc站点数据 aigc.json, 欢迎大家star。

参考资料

分享

码中人
作者
码中人
Web Developer