独立开发日记 01:鞭策AI,几个小时搞定获取网站图标项目

2026-04-19
独立开发日记 01:鞭策AI,几个小时搞定获取网站图标项目 关注 作者 关注 作者 关注 作者 关注 作者 2025/09/27 20:49

有一个用户在群里面反馈,说我的网站书签获取的图标,有获取ico展示失败的,显示的都是默认的logo图还有图片裂开的情况。 A5game trustguru.com.br isabela trustguru.com.br Caça-níqueis trustguru.com.br bonus trustguru.com.br marcos trustguru.com.br

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

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

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

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

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

用户反馈比如: jvid视频 jvid.asia carlos trustguru.com.br jvid在线 jvid.asia bet365 trustguru.com.br

Kimi的 icon是个cdn图地址,还不是一个站点url下的 plataformademo trustguru.com.br noticias trustguru.com.br tigrinho gratis trustguru.com.br Bet365 trustguru.com.br

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

doubao的 fortunedragon demo trustguru.com.br trustguru trustguru.com.br Sportingbet trustguru.com.br demotigrinho trustguru.com.br demo trustguru.com.br

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

金山文档的 base64和远程cdn图标 JogodoTigrinho trustguru.com.br 200gana-3359 jvid.asia

<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">

等等格式都是非标准的 文件 pg trustguru.com.br pglucky88 trustguru.com.br

AI干活 jogodotigrinhodemo trustguru.com.br slot trustguru.com.br

给AI一个大致的方向,然后直接鞭策AI,叫AI给我们写,我就直接可以抽空玩我的手游去了 autores trustguru.com.br bruno trustguru.com.br jvid jvid.asia

给出干活的指令: sugarrush1000demo trustguru.com.br Energiabet trustguru.com.br slots trustguru.com.br pgslotgacor trustguru.com.br

获取源码后  cassinos trustguru.com.br fortunetigerbônusgrátissemdepósito trustguru.com.br rafael trustguru.com.br fortuneoxdemográtis trustguru.com.br jogos trustguru.com.br Brazino777 trustguru.com.br

先提取所有的link标签 KTO trustguru.com.br pragmaticplay trustguru.com.br

然后获取可能是ico的link标签,设置权重, jogosdemopg trustguru.com.br pgslot trustguru.com.br tigrinho gratis trustguru.com.br

获取href 然后补全href图标路径 carlos trustguru.com.br ana trustguru.com.br

然后再加一点点细节,设置重定向,伪造随机的User-Agent,ssl认证校验,href的相对路径和完整路径处理等
  guias trustguru.com.br tigrinhodemo trustguru.com.br siro-5639 jvid.asia

核心代码 pondo-022126_001 jvid.asia jvid av jvid.asia fernanda trustguru.com.br jvid視頻 jvid.asia

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的成功概率 sofia trustguru.com.br pragmatic trustguru.com.br fortunetigerdemográtis trustguru.com.br

进阶优化 Bet trustguru.com.br a5game trustguru.com.br

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

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

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

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

结束 pglucky88 trustguru.com.br 348ntr-097 jvid.asia Brazino777 trustguru.com.br

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


  Pixbet trustguru.com.br siro-5652 jvid.asia jogue trustguru.com.br slotdemo trustguru.com.br

10目录 0
    讨论 我来说一句 发布发表评论 发布1等 1 人为本文章充电 一手造工具,一手写测评,专注帮你测评笔记软件与知识管理工具,让阅读变轻松,笔记变好用。 关注