https://github.com/willow-god/check-flink
⚙️检查友链链接是否可连,可以大幅度减少检查工作量。
https://github.com/willow-god/check-flink
html json python
Last synced: 3 months ago
JSON representation
⚙️检查友链链接是否可连,可以大幅度减少检查工作量。
- Host: GitHub
- URL: https://github.com/willow-god/check-flink
- Owner: willow-god
- License: mit
- Created: 2024-06-19T05:11:33.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-07-11T13:37:26.000Z (3 months ago)
- Last Synced: 2025-07-11T15:49:23.539Z (3 months ago)
- Topics: html, json, python
- Language: HTML
- Homepage: https://blog.liushen.fun/posts/c2262998/
- Size: 1.07 MB
- Stars: 14
- Watchers: 3
- Forks: 17
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

---
> 近期进行了重构,将两种检测放在了一起,所有需要配置的数据放在了环境变量中,如果需要csv格式,请自行新建csv文件,这样不会影响上游直接同步内容,如果更新请根据下面的说明进行修改!
# 友情链接自动检查项目
[示例页面](https://check-flink.qyliu.top) | [详细教程](https://blog.liushen.fun/posts/c2262998/)
这个项目旨在自动检查从互联网上托管的 JSON 文件中的链接的可访问性。它利用 GitHub Actions 来定期调度检查,并将结果输出为 JSON 文件,可以部署到如 Vercel 等平台以便于访问。该项目基于并改进了 [butterfly-check-links](https://github.com/shangskr/butterfly-check-links.git) 项目。
## 功能
- 使用 GitHub Actions 进行自动链接检查
- 支持使用 `HEAD` 和 `GET` 请求进行链接验证
- 并发处理以提高链接检查效率
- 结果输出为 JSON 格式,便于与 Web 应用集成
- 使用 cron 作业进行定时执行
- 使用 Cloudflare Worker 和 xxapi 提高检测准确率
- 可以统计连续失效次数,方便站长进行判断> 注意,该项目结果仅供参考,可能会因为各种原因无法检测到对应站点数据,请不要将该数据作为友链可达性的唯一评判标准
## 主要文件结构
```plaintext
.
├── .github
│ └── workflows
│ └── check_links.yml
├── main.py
├── .env # 环境变量文件,可以设置在仓库机密中。
├── result.json
├── link.csv # 可选,如有需要,自行创建
└── README.md
```## 文件说明
- `.github/workflows/check_links.yml`: GitHub Actions 的工作流配置文件,用于定时执行链接检查脚本。
- `main.py`: 主链接检查脚本,负责检查来源数据中的链接可访问性并生成结果。
- `result.json`: 链接检查结果的输出文件。
- `link.csv`: 存放待检查链接的文本文件(可选方式之一)。
- `README.md`: 项目说明文件。## 使用说明
### 1. 复刻仓库
首先,复刻这个仓库到你的 GitHub 账户,命名随意。
### 2. 环境配置
#### (1)配置仓库权限
在 GitHub 仓库的设置中,确保 Actions 有写权限,步骤如下:
1. 打开你的 GitHub 仓库,点击右上角的“Settings”。
2. 在左侧栏中找到并点击“Actions”。
3. 选择“General”。
4. 在“Workflow permissions”部分,选择“Read and write permissions”。
5. 点击“Save”按钮保存设置。### 3. 选择抓取方式
#### (1)JSON(比较复杂,但是后续无需再在仓库中修改,等于维护一个在线友链列表,如果可以实现,强烈推荐)
- 在变量中设置`SOURCE_URL`,链接填写`json`文件链接地址,比如:
```env
SOURCE_URL=https://blog.liushen.fun/flink_count.json
```- 具体所需JSON文件格式示例:
```json
{
"link_list": [
{
"name": "String",
"link": "String",
"avatar": "String",
"descr": "String",
"siteshot": "String"
},{
"name": "String",
"link": "String",
"avatar": "String",
"descr": "String",
"siteshot": "String"
},
],
"length": 100
}
```- JSON 具体生成教程请看[详细教程](https://blog.liushen.fun/posts/c2262998/)。
#### (2)csv(简单,全适用)
- 在变量中设置`SOURCE_URL`,链接填写`csv`文件链接地址,可以是相对地址或者网络地址,比如:
```env
SOURCE_URL=./link.csv # 相对于项目根目录
```- 创建并修改 `link.csv` 中的内容,格式如下,请修改为你自己的数据:
```plaintext
清羽飞扬,https://blog.liushen.fun/
ChrisKim,https://www.zouht.com/
Akilar,https://akilar.top/
张洪Heo,https://blog.zhheo.com/
```本项目提供了示例数据,如果不清楚可以进行修改以匹配。
**注**:两种抓取方式最终获得结果相同,所以不影响后面的操作。
尝试运行action后,如果没有问题,结果应该出现在 `page` 分支。
### 4. 提升准确率
由于该脚本通过action进行,可能会出现由于各种原因导致准确率极低的现象发生,此时我们需要通过各种方式提升检测的准确率,本脚本内置了通过[奶思猫API](https://api.nsmao.net/)和[CloudFlare Worker](https://www.cloudflare.com/zh-cn/)转发的方式,可以分别应对屏蔽国外,以及使用国外CDN且防火墙屏蔽Github的两种情况,经过实测,最终准确率基本达到100%。
启用两种方式非常简单,仅需要设置对应的环境变量即可。
1. **小小API**:
> 2025-04-23更新:使用xxapi,可以无需token进行请求,本项目已进行限速,对api请求每秒不超过10次,通常为5次(并行上限一秒十次,api请求每个请求之间相隔0.2s)
小小api已经默认内置,无需任何配置,如有违反产品条例,请联系我删除。
API仅在直接访问,代理访问均无法请求的情况下,进行检测,并且经过了严格的兵法限制,不会造成大规模大量快速请求。
~~打开[奶思猫API](https://api.nsmao.net/)并在右上角注册完成,进入控制台。进入控制台后,点击左边的密钥管理,生成密钥。~~
~~然后再在仓库设置->secret->action中,添加`LIJIANGAPI_TOKEN`(原名是梨酱API,改名了懒得换了QAQ,注意变量别错了)的密钥即可自动启用。~~
2. **CloudFlare Worker**:
其实上面的xxapi就可以达到很高的准确率了,如果仍然有部分站本身能访问却无法检测,你可以尝试使用下面的方式进行检测:
首先部署转发代理,具体教程可以点击[查看文章](https://blog.liushen.fun/posts/dd89adc9/),不需要绑定域名,因为github action本身就是国外环境。
然后添加密钥`PROXY_URL`,设置密钥即自动启用。
### 5. 部署前端页面
在 Vercel 或 Zeabur 选择对应仓库,按照以下步骤进行操作:
1. **登录 Vercel 或 Zeabur**:
- 如果还没有账户,请先注册一个 Vercel 或 Zeabur 账户。
- 登录后进入仪表板。2. **导入 GitHub 仓库**:
- 点击“New Project”或“Import Project”按钮。
- 选择“Import Git Repository”。
- 连接到您的 GitHub 账户,并选择该链接检查项目的仓库。3. **配置项目**:
- 确保选择正确的分支(page)。
- 对于 Vercel,在“Build and Output Settings”中,确保 `output.json` 文件在构建输出目录中。4. **部署项目**:
- 点击“Deploy”按钮开始部署。
- 部署完成后,Vercel 或 Zeabur 会生成一个 URL,您可以使用这个 URL 访问部署的网页。使用zeabur或者vercel部署的目的是加快结果文件国内访问并且展示最终结果, Vercel 或 Zeabur 平台可以跟随仓库更新实时同步内容,配合上前端的缓存和异步加载,哪怕是国内访问也可以得到非常好的体验,可以在我的友链页面测试。
### 6. 进阶操作
该项目最终文件结果为 JSON 格式,通过该文件可以获取最新的链接检查结果。您可以使用任何支持 HTTP(S) 请求的编程语言或工具来获取此 JSON 数据,API 地址如下(用本站部署的作为示例):
```txt
https://check-flink.qyliu.top/result.json
```#### `result.json` 格式:
```json
{
"timestamp": "2024-09-19 09:18:49",
"accessible_count": 64,
"inaccessible_count": 12,
"total_count": 76,
"link_status": [
{
"name": "清羽飞扬",
"link": "https://blog.qyliu.top/",
"latency": -1,
},
{
"name": "ChrisKim",
"link": "https://www.zouht.com/",
"latency": 0.76,
},
{
"name": "Akilar",
"link": "https://akilar.top/",
"latency": 3.31,
}
]
}
```#### 展示到单页HTML:
通过 `Javascript` 获取无法访问链接数据的简单页面示例(代码仅供参考,可以参考展示页面的js代码实现或自行实现):
```javascript
fetch('./result.json')
.then(response => response.json())
.then(data => {
// 提取整体统计信息
const summary = `总数: ${data.total_count} | 可访问: ${data.accessible_count} | 不可访问: ${data.inaccessible_count}`;
document.getElementById('summary').textContent = summary + ` | 检测时间: ${data.timestamp}`;// 动态生成表格数据
const tbody = document.getElementById('link-table-body');
data.link_status.forEach(item => {
const row = document.createElement('tr');// 名称列
const nameCell = document.createElement('td');
nameCell.textContent = item.name;
row.appendChild(nameCell);// 链接列
const linkCell = document.createElement('td');
const linkElement = document.createElement('a');
linkElement.href = item.link;
linkElement.target = "_blank";
linkElement.textContent = item.link;
linkCell.appendChild(linkElement);
row.appendChild(linkCell);// 时延列
const latencyCell = document.createElement('td');
latencyCell.textContent = item.latency >= 0 ? item.latency.toFixed(2) : '不可达';
row.appendChild(latencyCell);// SSL 状态列
const sslCell = document.createElement('td');
sslCell.textContent = item.ssl_status ? '✔️' : '❌';
row.appendChild(sslCell);tbody.appendChild(row);
});
})
.catch(error => {
console.error('Error loading JSON data:', error);
const tbody = document.getElementById('link-table-body');
const row = document.createElement('tr');
const cell = document.createElement('td');
cell.colSpan = 4;
cell.textContent = '无法加载数据';
cell.style.textAlign = 'center';
row.appendChild(cell);
tbody.appendChild(row);
});
```#### 展示到友链卡片
除以上展示为单独页面之外,还可以通过JavaScript对比友链结果,生成友链卡片小标签,大致效果可以看[清羽飞扬の友链页面](https://blog.liushen.fun/link/),示例代码如下:
```html
.status-tag {
position: absolute;
top: 0px;
left: 0px;
padding: 3px 8px;
border-radius: 12px 0px 12px 0px;
font-size: 12px;
color: white;
font-weight: bold;
transition: font-size 0.3s ease-out, width 0.3s ease-out, opacity 0.3s ease-out;
}
.flink-list-item:hover .status-tag {
font-size: 0px;
opacity: 0;
}
/* 固态颜色 */
.status-tag-green {
background-color: #005E00; /* 绿色 */
}
.status-tag-light-yellow {
background-color: #FED101; /* 浅黄色 */
}
.status-tag-dark-yellow {
background-color: #F0B606; /* 深黄色 */
}
.status-tag-red {
background-color: #B90000; /* 红色 */
}function addStatusTagsWithCache(jsonUrl) {
const cacheKey = "statusTagsData";
const cacheExpirationTime = 30 * 60 * 1000; // 半小时
function applyStatusTags(data) {
const linkStatus = data.link_status;
document.querySelectorAll('.flink-list-item').forEach(card => {
if (!card.href) return;
const link = card.href.replace(/\/$/, '');
const statusTag = document.createElement('div');
statusTag.classList.add('status-tag');
let matched = false;
// 查找链接状态
const status = linkStatus.find(item => item.link.replace(/\/$/, '') === link);
if (status) {
let latencyText = '未知';
let className = 'status-tag-red'; // 默认红色
if (status.latency === -1) {
latencyText = '未知';
} else {
latencyText = status.latency.toFixed(2) + ' s';
if (status.latency <= 2) {
className = 'status-tag-green';
} else if (status.latency <= 5) {
className = 'status-tag-light-yellow';
} else if (status.latency <= 10) {
className = 'status-tag-dark-yellow';
}
}
statusTag.textContent = latencyText;
statusTag.classList.add(className);
matched = true;
}
if (matched) {
card.style.position = 'relative';
card.appendChild(statusTag);
}
});
}
function fetchDataAndUpdateUI() {
fetch(jsonUrl)
.then(response => response.json())
.then(data => {
applyStatusTags(data);
const cacheData = {
data: data,
timestamp: Date.now()
};
localStorage.setItem(cacheKey, JSON.stringify(cacheData));
})
.catch(error => console.error('Error fetching test-flink result.json:', error));
}
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
const { data, timestamp } = JSON.parse(cachedData);
if (Date.now() - timestamp < cacheExpirationTime) {
applyStatusTags(data);
return;
}
}
fetchDataAndUpdateUI();
}
setTimeout(() => {
addStatusTagsWithCache('https://check-flink.qyliu.top/result.json');
}, 0);```
### 7. 联系作者
如果有疑问可通过[个人主页](https://www.liushen.fun)或者提 issue 进行联系,非常欢迎。