1. 项目背景与需求分析
作为技术博客作者,我经常需要整理自己在CSDN平台发布的文章列表。当需要向他人展示作品集、备份存档或迁移内容时,手动复制粘贴每篇文章标题既低效又容易出错。这个需求在以下场景中尤为突出:
- 求职时需要提供技术博客清单
- 准备个人作品集展示
- 博客内容迁移到其他平台
- 定期备份文章目录
CSDN后台虽然提供了文章管理界面,但缺乏批量导出功能。通过开发者工具分析,我们发现文章列表数据其实通过API接口返回,这为自动化获取提供了可能。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 整体实现思路
方案采用浏览器控制台脚本的方式实现,主要基于以下考虑:
- 无需额外安装软件,直接在CSDN页面运行
- 利用现有登录态,避免处理登录认证
- 实时获取最新文章数据
核心流程分为三个步骤:
- 自动翻页获取全部文章数据
- 提取文章标题和链接
- 生成Markdown格式列表
2.2 关键技术点
2.2.1 页面数据获取
通过分析CSDN个人中心网络请求,发现文章列表数据接口为:
code复制https://blog.csdn.net/community/home-api/v1/get-business-list
该接口返回JSON格式数据,包含文章标题、链接、发布时间等信息。
2.2.2 自动翻页处理
CSDN采用分页加载机制,需要模拟"加载更多"操作。通过监测发现:
- 每页返回15条数据
- 通过page参数控制分页
- 总页数可从首次返回数据中获取
2.2.3 Markdown格式生成
将获取的数据转换为标准Markdown列表格式:
markdown复制- [文章标题](文章链接)
- [另一篇文章标题](另一篇文章链接)
3. 完整实现代码
3.1 控制台脚本代码
javascript复制// ==UserScript==
// @name CSDN文章列表导出工具
// @namespace https://blog.csdn.net/
// @version 1.0
// @description 导出CSDN个人文章列表为Markdown格式
// @match https://blog.csdn.net/*
// @grant none
// ==/UserScript==
(async function() {
// 获取用户ID
const userId = window.location.pathname.split('/')[1];
if (!userId) return alert('请在个人博客主页运行此脚本');
let allArticles = [];
let page = 1;
let totalPage = 1;
// 获取文章数据函数
async function getArticles(page) {
const url = `https://blog.csdn.net/community/home-api/v1/get-business-list?page=${page}&size=15&businessType=blog&orderby=&noMore=false&username=${userId}`;
try {
const response = await fetch(url);
const data = await response.json();
return data.data;
} catch (error) {
console.error('获取文章失败:', error);
return null;
}
}
// 主处理逻辑
console.log('开始获取文章列表...');
const firstPage = await getArticles(1);
if (!firstPage) return alert('获取文章失败,请检查网络或登录状态');
totalPage = Math.ceil(firstPage.total / 15);
allArticles = allArticles.concat(firstPage.list);
// 获取剩余页数据
for (let i = 2; i <= totalPage; i++) {
const pageData = await getArticles(i);
if (pageData) {
allArticles = allArticles.concat(pageData.list);
console.log(`已获取第${i}页,共${totalPage}页`);
}
}
// 生成Markdown
let markdownContent = `# CSDN文章列表\n\n`;
markdownContent += `> 共${allArticles.length}篇文章,生成时间:${new Date().toLocaleString()}\n\n`;
allArticles.forEach(article => {
markdownContent += `- [${article.title}](${article.url})\n`;
});
// 输出结果
console.log('文章列表获取完成!');
const blob = new Blob([markdownContent], {type: 'text/markdown'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `CSDN文章列表_${userId}_${new Date().toISOString().slice(0,10)}.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
})();
3.2 使用说明
- 登录CSDN并进入个人博客主页
- 按F12打开开发者工具
- 切换到Console面板
- 粘贴上述代码并回车执行
- 脚本会自动下载Markdown格式的文件
4. 技术细节解析
4.1 数据获取优化
为提高获取效率,我们做了以下优化:
- 并行请求:使用Promise.all同时发起多个页面请求
- 失败重试:对失败的请求自动重试3次
- 进度显示:在控制台输出当前获取进度
优化后的并行请求代码片段:
javascript复制const pageRequests = [];
for (let i = 1; i <= totalPage; i++) {
pageRequests.push(getArticles(i));
}
const allPages = await Promise.all(pageRequests);
allArticles = allPages.reduce((acc, curr) => {
return curr ? acc.concat(curr.list) : acc;
}, []);
4.2 数据处理增强
原始数据需要经过以下处理:
- 去重:避免重复文章
- 排序:按发布时间倒序
- 过滤:移除已删除或私密文章
增强处理代码:
javascript复制allArticles = allArticles
.filter(article => article.status === 2) // 只保留已发布文章
.sort((a, b) => new Date(b.postTime) - new Date(a.postTime)) // 按发布时间排序
.reduce((unique, item) => {
return unique.some(art => art.articleId === item.articleId)
? unique
: [...unique, item];
}, []); // 去重
5. 扩展功能实现
5.1 添加文章分类
通过分析文章标签,可以为Markdown列表添加分类:
javascript复制// 获取文章标签函数
async function getArticleTags(articleId) {
const url = `https://blog.csdn.net/community/home-api/v1/get-tag-list?articleId=${articleId}`;
try {
const response = await fetch(url);
const data = await response.json();
return data.data || [];
} catch (error) {
console.error('获取标签失败:', error);
return [];
}
}
// 在生成Markdown时添加分类
const withTags = await Promise.all(allArticles.map(async article => {
const tags = await getArticleTags(article.articleId);
return {...article, tags};
}));
// 按分类生成Markdown
const byCategory = {};
withTags.forEach(article => {
const category = article.tags.length > 0 ? article.tags[0].name : '未分类';
if (!byCategory[category]) byCategory[category] = [];
byCategory[category].push(article);
});
let categorizedContent = `# CSDN文章分类列表\n\n`;
Object.entries(byCategory).forEach(([category, articles]) => {
categorizedContent += `## ${category}\n\n`;
articles.forEach(article => {
categorizedContent += `- [${article.title}](${article.url})\n`;
});
categorizedContent += '\n';
});
5.2 添加统计信息
在Markdown文件头部添加统计信息:
markdown复制## 文章统计
- 总文章数: ${allArticles.length}
- 最早文章: ${new Date(allArticles[allArticles.length-1].postTime).toLocaleDateString()}
- 最新文章: ${new Date(allArticles[0].postTime).toLocaleDateString()}
- 文章分类: ${Object.keys(byCategory).length}个
6. 常见问题与解决方案
6.1 脚本执行报错
问题现象:
- 控制台报跨域错误
- 返回数据为空
解决方案:
- 确保在个人博客主页执行脚本
- 检查是否已登录CSDN
- 尝试刷新页面后重新执行
6.2 获取文章不全
可能原因:
- 网络问题导致部分请求失败
- CSDN接口限制
解决方法:
- 增加重试机制
- 分批获取,每次获取5页数据
- 添加延迟避免请求过于频繁
优化后的重试逻辑:
javascript复制async function getArticlesWithRetry(page, retry = 3) {
let lastError;
for (let i = 0; i < retry; i++) {
try {
const data = await getArticles(page);
if (data) return data;
} catch (error) {
lastError = error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
console.error(`获取第${page}页失败:`, lastError);
return null;
}
6.3 Markdown格式问题
常见问题:
- 标题包含特殊字符导致格式错乱
- 链接失效
处理方法:
javascript复制function escapeMarkdown(text) {
return text.replace(/[\\`*_{}[\]()#+-.!]/g, '\\$&');
}
function validateUrl(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// 在生成链接时进行处理
allArticles.forEach(article => {
const safeTitle = escapeMarkdown(article.title);
const validUrl = validateUrl(article.url) ? article.url : '#无效链接';
markdownContent += `- [${safeTitle}](${validUrl})\n`;
});
7. 进阶使用技巧
7.1 定时自动备份
结合浏览器定时任务,可以每周自动备份文章列表:
javascript复制// 每周一早上8点自动运行
function scheduleBackup() {
const now = new Date();
const dayOfWeek = now.getDay(); // 0是周日
const hours = now.getHours();
if (dayOfWeek === 1 && hours === 8) {
console.log('执行定时备份...');
// 调用主逻辑
main();
}
}
// 每小时检查一次
setInterval(scheduleBackup, 60 * 60 * 1000);
7.2 与GitHub同步
将生成的Markdown文件自动提交到GitHub仓库:
javascript复制async function uploadToGitHub(content) {
const repo = 'your-repo';
const path = `CSDN-Backup/${new Date().toISOString().slice(0,10)}.md`;
const token = 'your-github-token';
const url = `https://api.github.com/repos/${repo}/contents/${path}`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `token ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: '自动备份CSDN文章列表',
content: btoa(unescape(encodeURIComponent(content)))
})
});
return response.json();
}
// 在主逻辑最后调用
const result = await uploadToGitHub(markdownContent);
console.log('GitHub上传结果:', result);
7.3 生成可视化报告
使用Markdown扩展语法生成更丰富的报告:
markdown复制## 年度文章统计
```mermaid
pie title 2023年文章分类分布
"前端" : 35
"后端" : 28
"数据库" : 20
"其他" : 17
月度发布趋势
| 月份 | 文章数量 |
|---|---|
| 1月 | 5 |
| 2月 | 8 |
| ... | ... |
| 12月 | 6 |
code复制
## 8. 安全与注意事项
1. **API调用频率**:避免过于频繁请求,建议间隔1秒以上
2. **数据隐私**:生成的Markdown文件妥善保存,避免泄露敏感信息
3. **脚本更新**:CSDN接口可能变更,定期检查脚本兼容性
4. **账号安全**:不要在公共电脑保存登录状态执行脚本
> 重要提示:此脚本仅限个人使用,请勿用于爬取他人文章数据
