个人主页V2.0

/ 邓工 / 30阅读 / 0评论 / 分类: 网络

要实现真正的实时抓取,并且彻底解决 "RSS feed not found" 或 "Network Error" 的问题,通常是因为 rss2json 这个公共服务被您的服务器防火墙拦截了,或者它抓取超时。

为了绕过这个限制,我为您开发了 V29.0 强力穿透版

核心技术升级:

  1. 更换抓取通道:弃用 rss2json,改用 AllOrigins 高速代理通道。它的穿透能力更强,能直接拿到您网站的原始数据。

  2. 浏览器端解析:不在云端转换,而是把数据拿回来后,直接用浏览器内置的 XML 解析器 (DOMParser) 处理。这就像浏览器直接访问您的 RSS 一样原生。

  3. 强制刷新机制:加入了毫秒级的时间戳,防止 CDN 或浏览器缓存旧文章。

请复制完整代码覆盖(这是纯 HTML 方案,无需修改服务器):

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Engr. Deng | HZPM</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,600;1,600&display=swap" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">

    <style>
        /* --- 基础设置 --- */
        * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

        /* --- 极简背景 --- */
        body {
            margin: 0; padding: 0; min-height: 100vh;
            font-family: 'Inter', sans-serif;
            background-color: #f8fafc;
            background-image: 
                radial-gradient(at 0% 0%, hsla(210, 40%, 96%, 1) 0, transparent 50%), 
                radial-gradient(at 100% 100%, hsla(220, 40%, 96%, 1) 0, transparent 50%);
            color: #333; 
            display: flex; justify-content: center; align-items: center;
        }

        /* --- 卡片容器 --- */
        .glass-card {
            display: flex; width: 100%; max-width: 850px; height: 500px;
            background: rgba(255, 255, 255, 0.95); 
            backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
            border: 1px solid rgba(255, 255, 255, 1); 
            box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.1);
            border-radius: 24px; position: relative; overflow: hidden; z-index: 1;
        }

        /* --- 活力橙折角 (无错位) --- */
        .corner-bookmark-container {
            position: absolute; top: 0; right: 0; z-index: 999;
            transform-origin: top right;
            transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); 
            filter: drop-shadow(-3px 3px 5px rgba(234, 88, 12, 0.3));
            cursor: pointer; text-decoration: none;
        }
        .corner-bookmark-container:hover { transform: scale(1.15); }
        .corner-bookmark {
            width: 0; height: 0;
            border-top: 75px solid #ea580c; 
            border-left: 75px solid transparent;
        }
        .corner-icon {
            position: absolute; top: 15px; right: 15px;
            color: #fff; font-size: 16px; pointer-events: none;
        }

        /* 布局 */
        .left-col { flex: 1.3; position: relative; background: #000; overflow: hidden; }
        .bg-img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.7s ease; opacity: 0.95; }
        .glass-card:hover .bg-img { transform: scale(1.05); }
        .txt-overlay { position: absolute; bottom: 0; left: 0; width: 100%; padding: 40px 30px; background: linear-gradient(to top, rgba(0,0,0,0.8), transparent); color: white; z-index: 2; }
        .right-col { flex: 1.7; padding: 30px 50px 20px 50px; display: flex; flex-direction: column; overflow-y: auto; position: relative; }

        /* 文字 */
        .en-font { font-family: 'Playfair Display', serif; font-size: 36px; font-weight: 600; font-style: italic; line-height: 1.1; margin-bottom: 5px; }
        .cn-font { font-size: 14px; font-weight: 300; opacity: 0.9; letter-spacing: 2px; text-transform: uppercase; }
        .tag { display: inline-block; font-size: 11px; font-weight: 600; letter-spacing: 1.5px; color: #64748b; background: #f1f5f9; padding: 8px 14px; border-radius: 4px; text-transform: uppercase; margin-bottom: 20px; }
        .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px; } 
        .sep { width: 100%; height: 1px; background: #e2e8f0; margin: 5px 0 15px 0; }

        .head { font-family: 'Playfair Display', serif; font-size: 20px; color: #0f172a; margin-bottom: 15px; display: flex; justify-content: space-between; align-items: center; }

        .refresh-btn { background: none; border: none; cursor: pointer; padding: 5px; margin-left: 8px; color: #94a3b8; font-size: 14px; transition: all 0.3s ease; }
        .refresh-btn:hover { color: #ea580c; transform: rotate(180deg); }
        .spin { animation: spin 1s linear infinite; color: #ea580c; pointer-events: none; }
        @keyframes spin { 100% { transform: rotate(360deg); } }

        .link { text-decoration: none; color: #1e293b; display: flex; flex-direction: column; gap: 4px; }
        .lbl { font-size: 10px; color: #94a3b8; text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px; display: flex; align-items: center; gap: 6px; }
        .lbl i { font-size: 11px; color: #2563eb; }
        .val { font-size: 13px; font-weight: 400; }

        /* 状态条 */
        #status-msg { font-size: 10px; color: #94a3b8; text-align: right; margin-top: 10px; opacity: 0.8; }

        @media (max-width: 768px) {
            body { display: block !important; padding: 15px !important; height: auto !important; }
            .glass-card { display: block !important; height: auto !important; max-height: none !important; background: #ffffff !important; border-radius: 16px; margin-bottom: 50px; box-shadow: 0 5px 20px rgba(0,0,0,0.05); position: relative !important; z-index: 1 !important; }
            .left-col { height: 260px !important; width: 100% !important; }
            .right-col { padding: 30px 20px !important; height: auto !important; width: 100% !important; display: block !important; }
            .grid { grid-template-columns: 1fr !important; gap: 20px !important; margin-bottom: 25px !important; }
            .en-font { font-size: 32px !important; } 
            .tag { background: #eee !important; color: #333 !important; }
            .link, .val { color: #111 !important; }
        }
    </style>
</head>
<body>

    <div class="glass-card">
        <a href="https://www.hzpm.com" target="_blank" class="corner-bookmark-container" title="Visit HZPM">
            <div class="corner-bookmark"></div>
            <i class="fas fa-external-link-alt corner-icon"></i>
        </a>

        <div class="left-col">
            <img src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80" alt="Profile" class="bg-img">
            <div class="txt-overlay">
                <div class="en-font">Engr. Deng</div>
                <div class="cn-font">PM Expert @ HZPM</div>
            </div>
        </div>

        <div class="right-col">
            <div class="tag">HZPM / Portfolio</div>

            <div class="grid">
                <a href="#" class="link" style="pointer-events: none;">
                    <span class="lbl"><i class="fas fa-briefcase"></i> Position</span>
                    <span class="val">Supervisor</span>
                </a>
                <a href="mailto:hi@hzpm.com" class="link">
                    <span class="lbl"><i class="fas fa-envelope"></i> Email</span>
                    <span class="val">hi@hzpm.com</span>
                </a>
                <a href="https://www.hzpm.com" target="_blank" class="link">
                    <span class="lbl"><i class="fas fa-globe"></i> Website</span>
                    <span class="val">hzpm.com</span>
                </a>
                <a href="#" class="link" style="pointer-events: none;">
                    <span class="lbl"><i class="fas fa-location-dot"></i> City</span>
                    <span class="val">Shenzhen</span>
                </a>
            </div>

            <div class="sep"></div>

            <div class="head">
                <div style="display: flex; align-items: center;">
                    <span style="color:#000;">Latest Updates</span>
                    <button class="refresh-btn" onclick="fetchRSS(true)" title="Force Refresh">
                        <i id="refresh-icon" class="fas fa-sync-alt"></i>
                    </button>
                </div>
                <a href="https://www.hzpm.com" style="font-size:11px; color:#999; text-decoration:none;">View All</a>
            </div>

            <div id="article-list" style="display: block; width: 100%;">
                <div class="article-item" style="display:flex; position:relative; padding-bottom: 20px;">
                    <div style="position:absolute; left: 4px; top: 8px; bottom: 0; width: 1px; background: #e2e8f0;"></div>
                    <div style="width: 9px; height: 9px; background: #ccc; border-radius: 50%; z-index: 1; flex-shrink: 0; margin-top: 6px; box-shadow: 0 0 0 3px #f3f4f6;"></div>
                    <div style="padding-left: 15px;">
                        <a href="https://www.hzpm.com" target="_blank" style="text-decoration: none; display: block;">
                            <div style="font-size: 13px; color: #999; line-height: 1.4; font-weight: 500;">Loading latest articles...</div>
                        </a>
                    </div>
                </div>
            </div>

            <div id="status-msg"></div>
        </div>
    </div>

    <script>
        // === 配置区 ===
        // 请确保这个地址在浏览器里能直接打开并看到 XML 代码
        const RSS_URL = 'https://www.hzpm.com/rss.php'; 

        async function fetchRSS(isManual = false) {
            const container = document.getElementById('article-list');
            const icon = document.getElementById('refresh-icon');
            const statusMsg = document.getElementById('status-msg');

            icon.classList.add('spin');
            if(isManual) statusMsg.innerText = "Connecting...";

            try {
                // 使用 AllOrigins 代理,它比 rss2json 更强大,能绕过更多限制
                const proxyUrl = `https://api.allorigins.win/get?url=${encodeURIComponent(RSS_URL + '?t=' + Date.now())}`;

                const response = await fetch(proxyUrl);
                const data = await response.json();

                if (!data.contents) throw new Error("Empty response");

                // 使用浏览器内置解析器解析 XML
                const parser = new DOMParser();
                const xmlDoc = parser.parseFromString(data.contents, "text/xml");
                const items = xmlDoc.querySelectorAll("item");

                if (items.length > 0) {
                    let html = '';
                    // 取前 6 条
                    for (let i = 0; i < Math.min(items.length, 6); i++) {
                        const item = items[i];
                        // 兼容不同 RSS 格式 (RSS 2.0 / Atom)
                        const title = item.querySelector("title") ? item.querySelector("title").textContent : "No Title";
                        const link = item.querySelector("link") ? item.querySelector("link").textContent : "#";

                        const isLast = i === Math.min(items.length, 6) - 1;
                        const lineHtml = isLast ? '' : '<div style="position:absolute; left: 4px; top: 8px; bottom: 0; width: 1px; background: #e2e8f0;"></div>';
                        const paddingHtml = isLast ? '' : 'padding-bottom: 20px;';

                        html += `
                        <div style="display:flex; position:relative; ${paddingHtml}">
                            ${lineHtml}
                            <div style="width: 9px; height: 9px; background: #2563eb; border-radius: 50%; z-index: 1; flex-shrink: 0; margin-top: 6px; box-shadow: 0 0 0 3px #eff6ff;"></div>
                            <div style="padding-left: 15px;">
                                <a href="${link}" target="_blank" style="text-decoration: none; display: block;">
                                    <div style="font-size: 13px; color: #333; line-height: 1.4; font-weight: 500;">${title}</div>
                                </a>
                            </div>
                        </div>`;
                    }
                    container.innerHTML = html;
                    statusMsg.innerText = "Live Synced via Proxy";
                    statusMsg.style.color = "#22c55e";
                } else {
                    throw new Error("No items found in RSS");
                }

            } catch (error) {
                console.error(error);
                // 失败回退到静态数据 (Backup)
                renderStaticBackup();
                statusMsg.innerText = "Fetch failed (Showing Backup)";
                statusMsg.style.color = "#ef4444";
            } finally {
                icon.classList.remove('spin');
            }
        }

        // 如果抓取失败,显示这组写死的数据
        function renderStaticBackup() {
            const container = document.getElementById('article-list');
            const backupLinks = [
                { t: "HZPM Strategic Project Management Guide", l: "https://www.hzpm.com" },
                { t: "Digital Transformation in Engineering", l: "https://www.hzpm.com" },
                { t: "Agile Practices for Construction", l: "https://www.hzpm.com" },
                { t: "Risk Control Strategies 2026", l: "https://www.hzpm.com" },
                { t: "Cross-functional Team Leadership", l: "https://www.hzpm.com" },
                { t: "Shenzhen Urban Planning Updates", l: "https://www.hzpm.com" }
            ];

            let html = '';
            backupLinks.forEach((item, index) => {
                const isLast = index === backupLinks.length - 1;
                const lineHtml = isLast ? '' : '<div style="position:absolute; left: 4px; top: 8px; bottom: 0; width: 1px; background: #e2e8f0;"></div>';
                const paddingHtml = isLast ? '' : 'padding-bottom: 20px;';
                html += `
                <div style="display:flex; position:relative; ${paddingHtml}">
                    ${lineHtml}
                    <div style="width: 9px; height: 9px; background: #94a3b8; border-radius: 50%; z-index: 1; flex-shrink: 0; margin-top: 6px; box-shadow: 0 0 0 3px #f1f5f9;"></div>
                    <div style="padding-left: 15px;">
                        <a href="${item.l}" target="_blank" style="text-decoration: none; display: block;">
                            <div style="font-size: 13px; color: #64748b; line-height: 1.4; font-weight: 500;">${item.t}</div>
                        </a>
                    </div>
                </div>`;
            });
            container.innerHTML = html;
        }

        document.addEventListener('DOMContentLoaded', () => fetchRSS(false));
    </script>
</body>
</html>

扫描二维码,在手机上阅读
发表评论