{"id":16959462,"url":"https://github.com/willow-god/check-flink","last_synced_at":"2026-04-06T05:31:38.992Z","repository":{"id":245059882,"uuid":"817133880","full_name":"willow-god/check-flink","owner":"willow-god","description":"⚙️检查友链链接是否可连，可以大幅度减少检查工作量。","archived":false,"fork":false,"pushed_at":"2026-04-04T03:39:35.000Z","size":1176,"stargazers_count":29,"open_issues_count":0,"forks_count":32,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-04T05:27:50.537Z","etag":null,"topics":["html","json","python"],"latest_commit_sha":null,"homepage":"https://blog.liushen.fun/posts/c2262998/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willow-god.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-06-19T05:11:33.000Z","updated_at":"2026-03-22T18:54:08.000Z","dependencies_parsed_at":"2025-12-19T13:06:48.104Z","dependency_job_id":null,"html_url":"https://github.com/willow-god/check-flink","commit_stats":null,"previous_names":["willow-god/check-flink"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/willow-god/check-flink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willow-god%2Fcheck-flink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willow-god%2Fcheck-flink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willow-god%2Fcheck-flink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willow-god%2Fcheck-flink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willow-god","download_url":"https://codeload.github.com/willow-god/check-flink/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willow-god%2Fcheck-flink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31461527,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["html","json","python"],"created_at":"2024-10-13T22:45:03.304Z","updated_at":"2026-04-06T05:31:38.986Z","avatar_url":"https://github.com/willow-god.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"![](./static/pic-doc/show.png)\n\n---\n\n\u003e 近期进行了重构，将两种检测放在了一起，所有需要配置的数据放在了环境变量中，如果需要csv格式，请自行新建csv文件，这样不会影响上游直接同步内容，如果更新请根据下面的说明进行修改！\n\n# 友情链接自动检查项目\n\n[示例页面](https://check-flink.qyliu.top) | [详细教程](https://blog.liushen.fun/posts/c2262998/)\n\n这个项目旨在自动检查从互联网上托管的 JSON 文件中的链接的可访问性。它利用 GitHub Actions 来定期调度检查，并将结果输出为 JSON 文件，可以部署到如 Vercel 等平台以便于访问。该项目基于并改进了 [butterfly-check-links](https://github.com/shangskr/butterfly-check-links.git) 项目。\n\n## 功能\n\n- 使用 GitHub Actions 进行自动链接检查\n- 支持使用 `HEAD` 和 `GET` 请求进行链接验证\n- 并发处理以提高链接检查效率\n- 结果输出为 JSON 格式，便于与 Web 应用集成\n- 使用 cron 作业进行定时执行\n- 使用 Cloudflare Worker 和 xxapi 提高检测准确率\n- 可以统计连续失效次数，方便站长进行判断\n\n\u003e 注意，该项目结果仅供参考，可能会因为各种原因无法检测到对应站点数据，请不要将该数据作为友链可达性的唯一评判标准\n\n## 主要文件结构\n\n```plaintext\n.\n├── .github\n│   └── workflows\n│       └── check_links.yml\n├── main.py\n├── .env  # 环境变量文件，可以设置在仓库机密中。\n├── result.json\n├── link.csv  # 可选，如有需要，自行创建\n└── README.md\n```\n\n## 文件说明\n\n- `.github/workflows/check_links.yml`: GitHub Actions 的工作流配置文件，用于定时执行链接检查脚本。\n- `main.py`: 主链接检查脚本，负责检查来源数据中的链接可访问性并生成结果。\n- `result.json`: 链接检查结果的输出文件。\n- `link.csv`: 存放待检查链接的文本文件(可选方式之一)。\n- `README.md`: 项目说明文件。\n\n## 使用说明\n\n### 1. 复刻仓库\n\n首先，复刻这个仓库到你的 GitHub 账户，命名随意。\n\n### 2. 环境配置\n\n#### （1）配置仓库权限\n\n在 GitHub 仓库的设置中，确保 Actions 有写权限，步骤如下：\n\n1. 打开你的 GitHub 仓库，点击右上角的“Settings”。\n2. 在左侧栏中找到并点击“Actions”。\n3. 选择“General”。\n4. 在“Workflow permissions”部分，选择“Read and write permissions”。\n5. 点击“Save”按钮保存设置。\n\n### 3. 选择抓取方式\n\n该项目支持两种方式抓取友链数据，你可以根据自己的需求选择其中一种，可以通过在设置-仓库机密中添加变量来实现修改。\n\n![添加变量](./static/pic-doc/secret.png)\n\n#### （1）JSON（比较复杂，但是后续无需再在仓库中修改，等于维护一个在线友链列表，如果可以实现，强烈推荐）\n\n- 在变量中设置`SOURCE_URL`，链接填写`json`文件链接地址，比如：\n\n  ```env\n  SOURCE_URL=https://blog.liushen.fun/flink_count.json\n  ```\n\n- 具体所需JSON文件格式示例：\n\n  ```json\n  {\n    \"link_list\": [\n      {\n        \"name\": \"String\",\n        \"link\": \"String\",\n        \"avatar\": \"String\",\n        \"descr\": \"String\",\n        \"siteshot\": \"String\",\n        \"linkpage\": \"String\"\n      },{\n        \"name\": \"String\",\n        \"link\": \"String\",\n        \"avatar\": \"String\",\n        \"descr\": \"String\",\n        \"siteshot\": \"String\",\n        \"linkpage\": \"String\"\n      },\n    ],\n    \"length\": 100\n  }\n  ```\n\n- 如果其中头像和描述，网站截图信息缺失，无需担心，该检测并不使用这些信息，仅检查链接是否可访问。\n\n- name 为友链名称，用于显示在结果中。\n- link 为友链地址，用于检查是否可访问。\n- linkpage 为友链页面，用于检查是否存在反链，如果需要检测反链请在环境变量中添加`AUTHOR_URL`，填写作者网站域名，比如：\n\n  ```env\n  AUTHOR_URL=blog.liushen.fun\n  ```\n\n  直接写域名部分即可，无需协议头，比如：`blog.liushen.fun`\n\n- JSON 具体生成教程请看[详细教程](https://blog.liushen.fun/posts/c2262998/)。\n\n#### （2）csv（简单，全适用）\n\n- 在变量中设置`SOURCE_URL`，链接填写`csv`文件链接地址，可以是相对地址或者网络地址，比如：\n\n  ```env\n  SOURCE_URL=./link.csv # 相对于项目根目录\n  ```\n\n- 创建并修改 `link.csv` 中的内容，格式如下，请修改为你自己的数据：\n\n  ```plaintext\n  清羽飞扬,https://blog.liushen.fun/,https://blog.liushen.fun/link/\n  ChrisKim,https://www.zouht.com/,https://www.zouht.com/link-exchange\n  Akilar,https://akilar.top/,https://akilar.top/link/\n  张洪Heo,https://blog.zhheo.com/,https://blog.zhheo.com/link/\n  ```\n\n  本项目提供了示例数据，如果不清楚可以进行修改以匹配。\n\n**注**：两种抓取方式最终获得结果相同，所以不影响后面的操作。\n\n尝试运行action后，如果没有问题，结果应该出现在 `page` 分支。\n\n### 4. 提升准确率\n\n由于该脚本通过action进行，可能会出现由于各种原因导致准确率极低的现象发生，此时我们需要通过各种方式提升检测的准确率，本脚本内置了通过[奶思猫API](https://api.nsmao.net/)和[CloudFlare Worker](https://www.cloudflare.com/zh-cn/)转发的方式，可以分别应对屏蔽国外，以及使用国外CDN且防火墙屏蔽Github的两种情况，经过实测，最终准确率基本达到100%。\n\n启用两种方式非常简单，仅需要设置对应的环境变量即可。\n\n1. **小小API**:\n\n   \u003e 2025-04-23更新：使用xxapi，可以无需token进行请求，本项目已进行限速，对api请求每秒不超过10次，通常为5次（并行上限一秒十次，api请求每个请求之间相隔0.2s）\n\n   小小api已经默认内置，无需任何配置，如有违反产品条例，请联系我删除。\n\n   API仅在直接访问，代理访问均无法请求的情况下，进行检测，并且经过了严格的兵法限制，不会造成大规模大量快速请求。\n\n   ~~打开[奶思猫API](https://api.nsmao.net/)并在右上角注册完成，进入控制台。进入控制台后，点击左边的密钥管理，生成密钥。~~\n\n   \u003c!-- ![奶思猫api](./static/pic-doc/lijiangapi.png) --\u003e\n\n   ~~然后再在仓库设置-\u003esecret-\u003eaction中，添加`LIJIANGAPI_TOKEN`（原名是梨酱API，改名了懒得换了QAQ，注意变量别错了）的密钥即可自动启用。~~\n\n2. **CloudFlare Worker**：\n\n   其实上面的xxapi就可以达到很高的准确率了，如果仍然有部分站本身能访问却无法检测，你可以尝试使用下面的方式进行检测：\n\n   首先部署转发代理，具体教程可以点击[查看文章](https://blog.liushen.fun/posts/dd89adc9/)，不需要绑定域名，因为github action本身就是国外环境。\n\n   然后添加密钥`PROXY_URL`，设置密钥即自动启用。\n\n### 5. 部署前端页面\n\n在 Vercel 或 Zeabur 选择对应仓库，按照以下步骤进行操作：\n\n1. **登录 Vercel 或 Zeabur**：\n   - 如果还没有账户，请先注册一个 Vercel 或 Zeabur 账户。\n   - 登录后进入仪表板。\n\n2. **导入 GitHub 仓库**：\n   - 点击“New Project”或“Import Project”按钮。\n   - 选择“Import Git Repository”。\n   - 连接到您的 GitHub 账户，并选择该链接检查项目的仓库。\n\n3. **配置项目**：\n   - 确保选择正确的分支（page）。\n   - 对于 Vercel，在“Build and Output Settings”中，确保 `output.json` 文件在构建输出目录中。\n\n4. **部署项目**：\n   - 点击“Deploy”按钮开始部署。\n   - 部署完成后，Vercel 或 Zeabur 会生成一个 URL，您可以使用这个 URL 访问部署的网页。\n\n使用zeabur或者vercel部署的目的是加快结果文件国内访问并且展示最终结果， Vercel 或 Zeabur 平台可以跟随仓库更新实时同步内容，配合上前端的缓存和异步加载，哪怕是国内访问也可以得到非常好的体验，可以在我的友链页面测试。\n\n### 6. 进阶操作\n\n该项目最终文件结果为 JSON 格式，通过该文件可以获取最新的链接检查结果。您可以使用任何支持 HTTP(S) 请求的编程语言或工具来获取此 JSON 数据，API 地址如下（用本站部署的作为示例）：\n\n```txt\nhttps://check-flink.qyliu.top/result.json\n```\n\n#### `result.json` 格式：\n\n```json\n{\n    \"timestamp\": \"2024-09-19 09:18:49\",\n    \"accessible_count\": 64,\n    \"inaccessible_count\": 12,\n    \"total_count\": 76,\n    \"link_status\": [\n        {\n            \"name\": \"清羽飞扬\",\n            \"link\": \"https://blog.qyliu.top/\",\n            \"latency\": -1,\n        },\n        {\n            \"name\": \"ChrisKim\",\n            \"link\": \"https://www.zouht.com/\",\n            \"latency\": 0.76,\n        },\n        {\n            \"name\": \"Akilar\",\n            \"link\": \"https://akilar.top/\",\n            \"latency\": 3.31,\n        }\n    ]\n}\n```\n\n#### 展示到单页HTML：\n\n通过 `Javascript` 获取无法访问链接数据的简单页面示例（代码仅供参考，可以参考展示页面的js代码实现或自行实现）：\n\n```javascript\n    fetch('./result.json')\n        .then(response =\u003e response.json())\n        .then(data =\u003e {\n            // 提取整体统计信息\n            const summary = `总数: ${data.total_count} | 可访问: ${data.accessible_count} | 不可访问: ${data.inaccessible_count}`;\n            document.getElementById('summary').textContent = summary + ` | 检测时间: ${data.timestamp}`;\n\n            // 动态生成表格数据\n            const tbody = document.getElementById('link-table-body');\n            data.link_status.forEach(item =\u003e {\n                const row = document.createElement('tr');\n\n                // 名称列\n                const nameCell = document.createElement('td');\n                nameCell.textContent = item.name;\n                row.appendChild(nameCell);\n\n                // 链接列\n                const linkCell = document.createElement('td');\n                const linkElement = document.createElement('a');\n                linkElement.href = item.link;\n                linkElement.target = \"_blank\";\n                linkElement.textContent = item.link;\n                linkCell.appendChild(linkElement);\n                row.appendChild(linkCell);\n\n                // 时延列\n                const latencyCell = document.createElement('td');\n                latencyCell.textContent = item.latency \u003e= 0 ? item.latency.toFixed(2) : '不可达';\n                row.appendChild(latencyCell);\n\n                // SSL 状态列\n                const sslCell = document.createElement('td');\n                sslCell.textContent = item.ssl_status ? '✔️' : '❌';\n                row.appendChild(sslCell);\n\n                tbody.appendChild(row);\n            });\n        })\n        .catch(error =\u003e {\n            console.error('Error loading JSON data:', error);\n            const tbody = document.getElementById('link-table-body');\n            const row = document.createElement('tr');\n            const cell = document.createElement('td');\n            cell.colSpan = 4;\n            cell.textContent = '无法加载数据';\n            cell.style.textAlign = 'center';\n            row.appendChild(cell);\n            tbody.appendChild(row);\n        });\n```\n\n#### 展示到友链卡片\n\n除以上展示为单独页面之外，还可以通过JavaScript对比友链结果，生成友链卡片小标签，大致效果可以看[清羽飞扬の友链页面](https://blog.liushen.fun/link/)，示例代码如下：\n\n```html\n\u003cstyle\u003e\n    .status-tag {\n        position: absolute;\n        top: 0px;\n        left: 0px;\n        padding: 3px 8px;\n        border-radius: 12px 0px 12px 0px;\n        font-size: 12px;\n        color: white;\n        font-weight: bold;\n        transition: font-size 0.3s ease-out, width 0.3s ease-out, opacity 0.3s ease-out;\n    }\n    .flink-list-item:hover .status-tag {\n        font-size: 0px;\n        opacity: 0;\n    }\n    /* 固态颜色 */\n    .status-tag-green {\n        background-color: #005E00; /* 绿色 */\n    }\n    .status-tag-light-yellow {\n        background-color: #FED101; /* 浅黄色 */\n    }\n    .status-tag-dark-yellow {\n        background-color: #F0B606; /* 深黄色 */\n    }\n    .status-tag-red {\n        background-color: #B90000; /* 红色 */\n    }\n\u003c/style\u003e\n\u003cscript\u003e\nfunction addStatusTagsWithCache(jsonUrl) {\n    const cacheKey = \"statusTagsData\";\n    const cacheExpirationTime = 30 * 60 * 1000; // 半小时\n    function applyStatusTags(data) {\n        const linkStatus = data.link_status;\n        document.querySelectorAll('.flink-list-item').forEach(card =\u003e {\n            if (!card.href) return;\n            const link = card.href.replace(/\\/$/, '');\n            const statusTag = document.createElement('div');\n            statusTag.classList.add('status-tag');\n            let matched = false;\n            // 查找链接状态\n            const status = linkStatus.find(item =\u003e item.link.replace(/\\/$/, '') === link);\n            if (status) {\n                let latencyText = '未知';\n                let className = 'status-tag-red'; // 默认红色\n                if (status.latency === -1) {\n                    latencyText = '未知';\n                } else {\n                    latencyText = status.latency.toFixed(2) + ' s';\n                    if (status.latency \u003c= 2) {\n                        className = 'status-tag-green';\n                    } else if (status.latency \u003c= 5) {\n                        className = 'status-tag-light-yellow';\n                    } else if (status.latency \u003c= 10) {\n                        className = 'status-tag-dark-yellow';\n                    }\n                }\n                statusTag.textContent = latencyText;\n                statusTag.classList.add(className);\n                matched = true;\n            }\n            if (matched) {\n                card.style.position = 'relative';\n                card.appendChild(statusTag);\n            }\n        });\n    }\n    function fetchDataAndUpdateUI() {\n        fetch(jsonUrl)\n            .then(response =\u003e response.json())\n            .then(data =\u003e {\n                applyStatusTags(data);\n                const cacheData = {\n                    data: data,\n                    timestamp: Date.now()\n                };\n                localStorage.setItem(cacheKey, JSON.stringify(cacheData));\n            })\n            .catch(error =\u003e console.error('Error fetching test-flink result.json:', error));\n    }\n    const cachedData = localStorage.getItem(cacheKey);\n    if (cachedData) {\n        const { data, timestamp } = JSON.parse(cachedData);\n        if (Date.now() - timestamp \u003c cacheExpirationTime) {\n            applyStatusTags(data);\n            return;\n        }\n    }\n    fetchDataAndUpdateUI();\n}\nsetTimeout(() =\u003e {\n    addStatusTagsWithCache('https://check-flink.qyliu.top/result.json');\n}, 0);\n\u003c/script\u003e\n```\n\n### 7. 联系作者\n\n如果有疑问可通过[个人主页](https://www.liushen.fun)或者提 issue 进行联系，非常欢迎。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillow-god%2Fcheck-flink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillow-god%2Fcheck-flink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillow-god%2Fcheck-flink/lists"}