1. 项目概述:批量导出CSDN文章为Markdown列表
作为一名技术博主,我经常需要整理自己在CSDN发布的文章列表。无论是制作个人作品集、迁移博客平台,还是单纯想备份文章目录,将CSDN文章列表转为Markdown格式都是个高频需求。手动复制粘贴每篇文章标题和链接不仅耗时费力,还容易出错。经过多次实践,我总结出一套稳定可靠的自动化方案,能够一键生成包含所有文章标题和URL的Markdown列表。
这个方案特别适合以下场景:
- 需要向雇主或客户展示技术博客成果时
- 准备将博客迁移到其他平台前的目录整理
- 创建个人知识库的索引目录
- 定期备份文章列表以防平台数据丢失
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案选型与原理分析
2.1 浏览器控制台方案 vs 爬虫方案
实现CSDN文章列表导出主要有两种技术路线:
-
浏览器控制台方案:
- 直接在当前页面通过JavaScript操作DOM
- 优点:无需额外环境,即时执行
- 缺点:受CSDN前端改版影响大
-
Python爬虫方案:
- 使用requests/BeautifulSoup等库抓取页面
- 优点:更稳定,可定制性强
- 缺点:需要Python环境
经过实测,CSDN的个人文章列表页结构相对稳定,且考虑到大多数用户更倾向于快速解决方案,本文将重点介绍浏览器控制台方案。对于需要批量处理大量文章或有高级需求的用户,可以在掌握基础方案后自行扩展为爬虫方案。
2.2 CSDN页面结构分析
以Chrome浏览器访问CSDN个人主页为例,文章列表通常呈现为以下结构:
html复制<div class="mainContent">
<div class="article-list">
<div class="article-item-box">
<h4><a href="文章链接">文章标题1</a></h4>
</div>
<div class="article-item-box">
<h4><a href="文章链接">文章标题2</a></h4>
</div>
<!-- 更多文章... -->
</div>
</div>
关键点在于定位到.article-item-box元素,然后提取其中的h4 > a标签内容。值得注意的是,CSDN可能会对这部分DOM结构进行调整,因此实际使用时可能需要根据当前页面结构微调选择器。
3. 详细实现步骤
3.1 基础版实现代码
打开CSDN个人文章列表页(通常为https://blog.csdn.net/你的用户名/article/list/页码),按F12打开开发者工具,切换到Console标签页,输入以下代码:
javascript复制// 获取所有文章项
const articles = document.querySelectorAll('.article-item-box');
let markdownContent = '';
articles.forEach(item => {
const titleElement = item.querySelector('h4 a');
if (titleElement) {
const title = titleElement.innerText.trim();
const url = titleElement.href;
markdownContent += `- [${title}](${url})\n`;
}
});
// 输出结果
console.log(markdownContent);
// 复制到剪贴板
navigator.clipboard.writeText(markdownContent).then(() => {
console.log('Markdown内容已复制到剪贴板');
});
这段代码会:
- 查找页面中所有文章项
- 提取每篇文章的标题和链接
- 格式化为Markdown列表项
- 自动复制结果到剪贴板
3.2 增强版实现代码
基础版虽然能用,但存在几个问题:
- 无法处理分页
- 没有包含发布时间等元信息
- 对异常情况处理不足
以下是增强版代码:
javascript复制async function getCSDNArticles() {
let allArticles = [];
let currentPage = 1;
const username = window.location.pathname.split('/')[1];
while (true) {
try {
const response = await fetch(`https://blog.csdn.net/${username}/article/list/${currentPage}`);
const text = await response.text();
// 使用DOMParser解析HTML
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
const articles = doc.querySelectorAll('.article-item-box');
if (articles.length === 0) break;
articles.forEach(item => {
const titleElement = item.querySelector('h4 a');
const dateElement = item.querySelector('.date');
const readCountElement = item.querySelector('.read-num');
if (titleElement) {
allArticles.push({
title: titleElement.innerText.trim(),
url: titleElement.href,
date: dateElement ? dateElement.innerText.trim() : '未知日期',
readCount: readCountElement ? readCountElement.innerText.trim() : '0'
});
}
});
currentPage++;
// 避免请求过快被封
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error('获取数据出错:', error);
break;
}
}
// 生成Markdown
let markdownContent = `# CSDN文章列表(共${allArticles.length}篇)\n\n`;
allArticles.forEach(article => {
markdownContent += `- [${article.title}](${article.url}) - ${article.date} - 阅读量:${article.readCount}\n`;
});
// 复制到剪贴板
try {
await navigator.clipboard.writeText(markdownContent);
console.log('Markdown内容已复制到剪贴板');
console.log(markdownContent);
} catch (error) {
console.error('复制失败,请手动复制:', error);
console.log(markdownContent);
}
return allArticles;
}
// 执行函数
getCSDNArticles();
增强版新增功能:
- 自动抓取所有分页
- 包含发布时间和阅读量信息
- 添加了错误处理和请求间隔
- 生成更完整的Markdown文档
4. 常见问题与解决方案
4.1 选择器失效问题
问题现象:
控制台报错"无法读取null的属性querySelector"或返回结果为空。
可能原因:
- CSDN改版导致DOM结构变化
- 页面未完全加载就执行脚本
- 个人主页使用了特殊模板
解决方案:
- 检查当前页面元素结构,更新选择器
- 确保页面完全加载后执行(可尝试setTimeout延迟执行)
- 使用更通用的选择器,如
document.querySelectorAll('[class*="article-item"]')
4.2 跨域请求限制
问题现象:
增强版代码在fetch请求时报跨域错误。
原因分析:
浏览器安全策略禁止向不同源的地址发起fetch请求。
解决方案:
- 改用浏览器插件方案(如Tampermonkey)
- 使用本地代理服务器
- 降级使用基础版逐页处理
4.3 分页判断不准确
问题现象:
脚本无限循环或提前终止。
优化方案:
改进分页终止条件判断:
javascript复制// 在获取文章列表后添加以下判断
const nextPageBtn = doc.querySelector('.pagination a:last-child');
if (!nextPageBtn || nextPageBtn.innerText.includes('下一页') === false) {
break;
}
5. 进阶技巧与优化建议
5.1 使用浏览器插件自动化
对于需要频繁导出文章列表的用户,建议将脚本封装为浏览器插件。以下是Tampermonkey脚本示例:
javascript复制// ==UserScript==
// @name CSDN文章导出工具
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 一键导出CSDN文章列表为Markdown
// @author YourName
// @match https://blog.csdn.net/*/article/list/*
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
// 添加导出按钮
const exportBtn = document.createElement('button');
exportBtn.textContent = '导出Markdown';
exportBtn.style.position = 'fixed';
exportBtn.style.right = '20px';
exportBtn.style.bottom = '20px';
exportBtn.style.zIndex = 9999;
exportBtn.style.padding = '8px 16px';
exportBtn.style.backgroundColor = '#fc5531';
exportBtn.style.color = 'white';
exportBtn.style.border = 'none';
exportBtn.style.borderRadius = '4px';
exportBtn.style.cursor = 'pointer';
exportBtn.addEventListener('click', async () => {
const articles = document.querySelectorAll('.article-item-box');
let markdownContent = '';
articles.forEach(item => {
const titleElement = item.querySelector('h4 a');
if (titleElement) {
const title = titleElement.innerText.trim();
const url = titleElement.href;
markdownContent += `- [${title}](${url})\n`;
}
});
try {
await GM_setClipboard(markdownContent);
alert('文章列表已复制到剪贴板!');
} catch (error) {
console.error('复制失败:', error);
prompt('请手动复制以下内容:', markdownContent);
}
});
document.body.appendChild(exportBtn);
})();
5.2 添加文章分类标签
如果文章有分类标签,可以通过以下代码提取:
javascript复制const categoryElement = item.querySelector('.article-type');
const category = categoryElement ? categoryElement.innerText.trim() : '未分类';
然后在Markdown中添加分类信息:
markdown复制- [${title}](${url}) [${category}]
5.3 导出为CSV格式
有时可能需要Excel格式的列表,可以修改输出格式:
javascript复制let csvContent = "标题,链接,日期,阅读量\n";
allArticles.forEach(article => {
csvContent += `"${article.title}","${article.url}","${article.date}","${article.readCount}"\n`;
});
6. 安全与注意事项
-
频率控制:执行增强版脚本时,务必设置合理的请求间隔(建议≥1秒),避免被CSDN反爬机制封禁IP。
-
数据验证:导出的链接建议进行有效性检查,可以使用以下代码验证链接:
javascript复制async function checkLink(url) { try { const response = await fetch(url, { method: 'HEAD' }); return response.status === 200; } catch { return false; } } -
隐私考虑:如果文章包含敏感内容,建议在本地处理数据,避免使用第三方在线工具转换Markdown。
-
版权声明:虽然是自己创作的文章,但批量导出时建议保留原始出处信息,避免版权纠纷。
-
备份策略:重要文章建议不仅保存链接,还应保存完整内容。可以使用
puppeteer等工具实现完整文章抓取。
这套方案我已经在多个项目中实际使用,特别是在准备技术面试和整理作品集时特别有用。对于有编程基础的用户,可以进一步扩展功能,比如添加自动分类、按阅读量排序、生成统计图表等。如果遇到任何问题,欢迎在评论区交流讨论。
