惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
H
Hackread – Cybersecurity News, Data Breaches, AI and More
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
Security Latest
Security Latest
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
博客园 - Franky
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
T
Threatpost
Last Week in AI
Last Week in AI
P
Privacy International News Feed
S
SegmentFault 最新的问题
aimingoo的专栏
aimingoo的专栏
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
MyScale Blog
MyScale Blog
P
Palo Alto Networks Blog
Cisco Talos Blog
Cisco Talos Blog
T
Tailwind CSS Blog
Blog — PlanetScale
Blog — PlanetScale
G
GRAHAM CLULEY
GbyAI
GbyAI
G
Google Developers Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
A
About on SuperTechFans
H
Help Net Security
T
Threat Research - Cisco Blogs
C
Check Point Blog
S
Schneier on Security
Google DeepMind News
Google DeepMind News
T
The Exploit Database - CXSecurity.com
博客园 - 叶小钗
Scott Helme
Scott Helme
博客园 - 司徒正美
美团技术团队
W
WeLiveSecurity
O
OpenAI News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
AWS News Blog
AWS News Blog
I
InfoQ

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
独立开发日记 01:鞭策AI,几个小时搞定获取网站图标项目 - 少数派
2025-09-27 · via 少数派

有一个用户在群里面反馈,说我的网站书签获取的图标,有获取ico展示失败的,显示的都是默认的logo图还有图片裂开的情况。

这个获取站点图标项目是我2年前网上找的一个开源的PHP项目,然后作者也非常久没维护了,我拉下来改改,直接就是部署一波,有一些小问题,不是很影响,后面想着再优化优化,大家懂的一般说后面再做的事情......。期间也是优化了几次,因为不 会PHP没敢大改动,都是一些站点小兼容。现在有AI了,有啥会不会的,直接鞭策AI不就好了吗 🤔 !!!也是好起来了,对PHP一点不会,使用AI花了几个小时深度优化了一波。项目原核心逻辑根据网站的url 获取网页的源码,然后使用正则获取源码里面的可能是icon的link标签,最后获取link标签的href路径 ,存为文件,缓存30天。原逻辑是从html中获取 link标签

if (@preg_match('/((<link[^>]+rel=.(icon|shortcut icon|alternate icon|apple-touch-icon)[^>]+>))/i', $html['data'], $match_tag)) {

ps:有的朋友可能是直接暴力获取 www.baidu.com/favicon.ico,但是有的网站根本没有这个

使用正则获取link标签并不是很规范 rel可能在href后面 有点在前面 还有引号的问题,导致无法匹配

目前遇到的一些ico的格式有 各种各样的 通过url/favicon.ico 只能获取小部分的icon问题

用户反馈比如:

Kimi的 icon是个cdn图地址,还不是一个站点url下的

<link rel="shortcut icon" href=//statics.moonshot.cn/kimi-web-seo/favicon.ico>

doubao的

<link rel="icon" href="//lf-flow-web-cdn.doubao.com/obj/flow-doubao/doubao/web/logo-icon.png">

金山文档的 base64和远程cdn图标

<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACH1BMVEUAAAA9kP8mpv9Fv/8QT94TXeQAfv8trP0xr/0VUdoAcv8LgPs1sf4AgP4QUuURTuEVl/ksq/wAg/84tf4NUt80sf0bnPsPUuAAYfsAgP4UTtYAY/8GXPQRkv1Jxv8AZP86tv9Bvv9Gv/9Atv8KiPUOVesAZ+wbS8wUl/kjo/sAYv8PVekjovxBu/8AY/8trPxCu/8AW/AAcvAxsP0npvsYTdElo/oQT9o+uP8AYv8KVOIHdugAYfwAfPwbfe49uf4XmfkcdOopo/oAYv86tf4AYf8TT9cZaeYVTdUJVOUcnvsAYv8Agv9Iwv8AYv9Jwf8bSs0gofoAY/8Ag/9Gv/8en/oAYf9Fv/8AWuQZS80AdO5BvP8AYv9DvP8Umfo9uv8LVOUAgf8amfk/uf8AY/8Ag/8Ahf8TUdgVT9UNWOknov8trPwxr/wmpvspqfs1sv0io/s5tf0AgP8bnfoMV+4AYPoAYv8AYf4AYfwAe/sen/oAd/gAXvYAff0LW/MLWfEOUN0PT9oSTtZAu/49uP0qqfweoPoXmvkAdPUAXPIAcfEKUuIWTNIXS88KXPYAWusOVeoPVOkPUuYQUeQQT+IMUeAUTdMZSs09uP4Aa+kAWOgAaeQIVOQIU+QAV+MRTd8STd0RTtgSl/kAW+8NWO8Abu0AbuwBZNsAg/8AYPwAc/MIVecAZuEAVeAAWt8AZN0JgvAIfOsJV+sTVuIAU9qil9AQAAAAa3RSTlMAAwb++A3+bkkkGxL8+Pf36tnTubCYjol4dmVjRjEvLysZFA7+/Pv6+fj39/b19fPw8O/u7Ovq6Ofn5+Xj4uDa2trZ1dPS0s/Ny7+9vLi4trapoqGgoJ2XkI+KgoJycGRhX1BNS0pHQj06IcCB3jkAAAIBSURBVDjLdZCHctpAFEWfCMU1ce8tTu+99957770nJFGwkE1sgyMTAginYbkAIQSD494+0G+ltTAacWZnNLt7dO/Og3Tqzl2og8wsvroAudEI+iy5s+6LzMZ7Bp1r5sFOQegRenAJwoEnoCXr6Mc0ihvSrhuK32vJK21Mva00760OWyz0KZYNbzJQmKXUF1o7rZ24rGngweETIGOwbH2nw/ZDUaM6grJVHzSs3R+NoqDy+lKLuwWX2618r03/QRalhNzbewo+qZyuZX4TVMFQubm3f3xy5VeZg49xsL8IC+mUH+1zeLyJ3pHkRCwWK7hvIGc/CYrwvIhtbXZ4hgYwJLm0LFf56QdhDRAq8wdZMypD3kT/SBJo6nfCMmWTc2WQlUO8GDIn2GSAUnuWGERJpIS4LR4HlWdFNITua9pl5s3h5vJ81tzscAAhuyTc3kZQ51CxqasvMmDGENyZyo1BX2iqLRAI0L7qvU2fv6Ex5mVbgana4XIGfb7QsI0KNcc4O4/Gv67I6Jjn5cmOvy4nGuFwaFipqFjN2YlBQkZn/N0dxAhiSfjUKzqHyxwq/0lIX0T0d6OAGb7dD0Gl/gzH8UqN6JcNp7HcBPN5esTO8U2oSCIpcZVkgwamepedxxpJROP4C9DBdHc9vlWSxG1VDOiTc53jpRW3TJCZ+vMXNeWzDz4DoNZyqecAAAAASUVORK5CYII=">

<link rel="apple-touch-icon" href="https://qn.cache.wpscdn.cn/kdocs/mobile/touch/apple-120.png">

等等格式都是非标准的 文件

AI干活

给AI一个大致的方向,然后直接鞭策AI,叫AI给我们写,我就直接可以抽空玩我的手游去了

给出干活的指令:

获取源码后 

先提取所有的link标签

然后获取可能是ico的link标签,设置权重,

获取href 然后补全href图标路径

然后再加一点点细节,设置重定向,伪造随机的User-Agent,ssl认证校验,href的相对路径和完整路径处理等
 

核心代码

if ($html && $html['status'] == 'OK') {
         
            // 对取到的HTML内容进行删除换行符的处理,避免link信息折行导致的正则匹配失败
           
            $html = str_replace(array("\n", "\r"), '', $html);
            //匹配完整的LINK标签,再从LINK标签中获取HREF的值
            // 优化的两步匹配策略:先匹配所有link标签,再筛选favicon相关的标签

            // 第一步:匹配所有link标签
            $all_links = [];
            if (preg_match_all('/<link[^>]*>/i', $html['data'], $link_matches)) {
                $all_links = $link_matches[0];
            }

            $this->_log_message("【LINK匹配】找到 " . count($all_links) . " 个link标签");

            // 定义favicon相关的rel类型及其优先级(数字越小优先级越高)
            $favicon_types = [
                'icon' => 1,
                'shortcut icon' => 2,
                'apple-touch-icon' => 3,
                'apple-touch-icon-precomposed' => 4,
                'mask-icon' => 5,
                'alternate icon' => 6
            ];

            $found_icons = [];

            // 第二步:从所有link标签中筛选favicon相关的标签
            foreach ($all_links as $link_tag) {
                $this->_log_message("【LINK分析】处理标签: " . htmlspecialchars($link_tag));

                // 提取rel属性值
                $rel_value = '';
                if (preg_match('/rel\s*=\s*["\']([^"\']*)["\']|rel\s*=\s*([^\s>]+)/i', $link_tag, $rel_matches)) {
                    $rel_value = trim($rel_matches[1] ?? $rel_matches[2] ?? '');
                }

                // 检查是否为favicon相关的rel类型
                $matched_type = null;
                $priority = 999;

                foreach ($favicon_types as $type => $type_priority) {
                     // 使用更精确的匹配,避免部分匹配导致的错误识别
                     if ($type === 'icon' && preg_match('/^icon$/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     } elseif ($type === 'shortcut icon' && preg_match('/^shortcut\s+icon$/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     } elseif ($type === 'apple-touch-icon' && preg_match('/^apple-touch-icon/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     } elseif ($type === 'apple-touch-icon-precomposed' && preg_match('/^apple-touch-icon-precomposed/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     } elseif ($type === 'mask-icon' && preg_match('/^mask-icon$/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     } elseif ($type === 'alternate icon' && preg_match('/^alternate\s+icon$/i', trim($rel_value))) {
                         if ($type_priority < $priority) {
                             $matched_type = $type;
                             $priority = $type_priority;
                         }
                     }
                 }

                if ($matched_type) {
                    $this->_log_message("【LINK分析】匹配到favicon类型: {$matched_type}, rel值: {$rel_value}");

                    // 提取href属性值
                    $href_value = '';
                    if (preg_match('/href\s*=\s*["\']([^"\']*)["\']|href\s*=\s*([^\s>]+)/i', $link_tag, $href_matches)) {
                        $href_value = trim($href_matches[1] ?? $href_matches[2] ?? '');
                    }

                    if (!empty($href_value)) {
                        $this->_log_message("【LINK分析】提取到href: {$href_value}");

                        // 处理URL路径
                        if (preg_match('/^data:image\/[^;]+;base64,/', $href_value)) {
                            $full_icon_url = $href_value;
                        } elseif (preg_match('/^https?:\/\//', $href_value)) {
                            $full_icon_url = $href_value;
                        } else {
                            $full_icon_url = $this->filterRelativeUrl($href_value, $this->params['origin_url']);
                        }

                        // 避免重复添加相同的URL
                        $icon_key = md5($full_icon_url);
                        if (!isset($found_icons[$icon_key])) {
                            $found_icons[$icon_key] = [
                                'url' => $full_icon_url,
                                'type' => $matched_type,
                                'priority' => $priority,
                                'original_rel' => $rel_value
                            ];
                        }
                    } else {
                        $this->_log_message("【LINK分析】未找到href属性");
                    }
                } else {
                    $this->_log_message("【LINK分析】非favicon标签,rel值: {$rel_value}");
                }
            }
            // 按优先级排序图标
            uasort($found_icons, function($a, $b) {
                return $a['priority'] - $b['priority'];
            });
            // 打印所有找到的ICO路径
            $this->_log_message("【ICO路径】找到 " . count($found_icons) . " 个favicon链接:");
            foreach ($found_icons as $key => $icon_info) {
                $this->_log_message("【ICO路径】类型: {$icon_info['type']}, 优先级: {$icon_info['priority']}, 原始rel: {$icon_info['original_rel']}, URL: {$icon_info['url']}");
            }
            // 尝试获取找到的favicon,按优先级顺序
            foreach ($found_icons as $icon_info) {
                $icon_url = $icon_info['url'];
                $this->_log_message("Trying favicon URL ({$icon_info['type']}): {$icon_url}");

                // 检查是否为 base64 data URI
                if (preg_match('/^data:image\/[^;]+;base64,(.+)$/i', $icon_url, $matches)) {
                    $this->_log_message("Found base64 data URI favicon");
                    $base64_data = $matches[1];

                    // 解码 base64 数据
                    $decoded_data = base64_decode($base64_data);

                    if ($decoded_data !== false && strlen($decoded_data) > 0) {
                        $this->_log_message("Successfully decoded base64 favicon data, size: " . strlen($decoded_data) . " bytes");
                        $this->data = $decoded_data;
                        return $this->data; // 成功获取到 base64 favicon,直接返回
                    } else {
                        $this->_log_message("Failed to decode base64 favicon data");
                    }
                } else {
                    // 普通的 HTTP/HTTPS URL,使用原有的 getFile 方法
                    $icon = $this->getFile($icon_url, true);
                    if ($icon && $icon['status'] == 'OK') {
                        $this->_log_message("Success get icon from {$this->params['origin_url']}, icon url is {$icon_url}");
                        $this->data = $icon['data'];
                        break; // 找到第一个可用的就停止
                    } else {
                        $this->_log_message("Failed to get icon from: {$icon_url}");
                    }
                }
            }
        }

直接叫AI改写核心的逻辑,到这一步已经大幅度的提升了获取icon的成功概率

进阶优化

到这一步已经解决了兼容绝大部分的站点,但是还有有一些特殊的展示无法兼容,这个时候就需要继续鞭策AI新增一个备用方案,支持手动映射站点和文件图标,解决剩下的无法获取图标的站点。

PHP我也没咋用过,直接叫AI自己出方案自己实现,新增可手动映射列表管理后台,并且可导出导入备份功能,简易后台维护系统 。

最终AI的技术栈选的SqLite+本地文件的形式做数据存储,非常的方便,也不需要依赖其他中间件了。

通过API自动识别+后台手动配置的方式,至此绝大部分站点都可以轻松的获取到图标。如果后续还遇到不兼容的站点,直接去管理后台配置一下就好了。

结束

AI越来越强悍了,全程花了几个小时,我一行代码没写,AI全程帮我完成了,我完全不会的PHP项目改造。自己写我可能就直接考虑换成自己熟悉的语言重写了,比如NodeJs代替。