再见,洛谷博客!——下载洛谷博客文章(JavaScript)

控制台部分

在洛谷首页 F12 打开控制台,复制代码到控制台即可使用。由于技术受限,最终只能下载一个 .json 文件,文件中有所有博客的标题和内容。代码由豆包生成。

关于报错:如果报错是 Error: Cannot fetch blog ${bid}! 则一般是因为这篇文章已经被删除,可以查看 log 确认这一点;若不是这个报错,则请重新运行或向我报告。

// 定义常量
const url = "https://www.luogu.com.cn";
console.log("Warning: When the program is running, don't log out your luogu account...");

const uid = "{}"

let restCount = 0;

// 获取博客内容的函数
async function fetchBlogContent(bid) {
    // 全局计数器加 1
    restCount += 1;
    if (restCount % 10 === 0) {
        console.log("Please wait for 5 seconds...");
        // 等待 5 秒,不然可能请求失败或者封号
        await new Promise(resolve => setTimeout(resolve, 5000));
    }
    // 发送请求获取博客详情
    const response = await fetch(`${url}/api/blog/detail/${bid}`);
    const result = await response.json();
    // 处理响应状态
    if (result.status === 200) {
        const data = result.data;
        return {
            source: `${url}/blog/_post/${bid}`,
            content: data.Content,
            identifier: data.Identifier,
            postTime: data.PostTime,
            title: data.Title,
            type: data.Type
        };
    } else {
        console.error(`Error: Cannot fetch blog ${bid}!`);
        console.log(result);
        return {};
    }
}

files = []

// 保存博客内容的函数
async function saveBlogContent(bid) {
    const result = await fetchBlogContent(bid);
    if (Object.keys(result).length === 0) {
        return;
    }
    // 格式化时间
    const postTime = new Date(result.postTime * 1000).toISOString().replace('T', ' ').substring(0, 19);
    const titleLine = `# ${result.title}\n`;
    const metaLine = `posted on ${postTime} | under ${result.type} | [source](${result.source})\n\n`;
    const contentLine = `${result.content}\n`;
    files.push({
      title: `${result.identifier}.md`,
      content: titleLine + metaLine + contentLine
    })
    console.log(`Save blog ${bid} successfully...`);
}

// 获取博客列表的函数
async function getBlogLists(uid) {
    const response = await fetch(`${url}/api/blog/userBlogs`);
    const result = (await response.json()).blogs;
    const lists = [];
    const pageNumber = Math.ceil(result.count / result.perPage);
    for (let i = 1; i <= pageNumber; i++) {
        const pageResponse = await fetch(`${url}/api/blog/userBlogs?page=${i}`);
        const pageResult = (await pageResponse.json()).blogs;
        for (const blog of pageResult.result) {
            lists.push(blog.id);
        }
    }
    return lists;
}

// 主函数,用于获取博客列表并保存博客内容
async function main() {
    console.log(`Now start fetching blogs of user ${uid}.`);
    const blogIds = await getBlogLists(uid);
    console.log(`${blogIds.length} blogs in total.`)
    console.log(blogIds)
    for (const bid of blogIds) {
        await saveBlogContent(bid);
    }
    console.log(files)
    // 将 files 转为 json 格式,提供给用户下载
    const jsonData = JSON.stringify(files, null, 2);
    const blob = new Blob([jsonData], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'blogs.json';
    a.click();
    URL.revokeObjectURL(url);
    console.log(`Blogs of user ${uid} have already saved.`);
    console.log("Thank you for using my tool. Author: Luogu @yukimianyan.");
}

// 调用主函数
main().catch(error => console.error('An error occurred:', error));

本地部分

配套使用的以下代码,将下载的 .json 文件转化为若干文件。以下代码为 python 代码,需要预先 pip install json(也可能不需要),然后需要手动修改最后三行的文件路径。同样地,代码由豆包生成。

注意!!!专栏上线后创建的文章,其 identifier(标识符)不是人类可读的(例如有一篇 't1733148542yKpVVX9dhqj1dYRz.md' 是我的游记),对此我真是一点办法都没有。如果你动手能力比较强你可以把文件名改为标题 + .md,但是这样会有转义字符问题,所以我没有这样做。

import json
import os

def json_to_files(json_file_path, output_directory):
    # 检查输出目录是否存在,如果不存在则创建
    if not os.path.exists(output_directory):
        os.makedirs(output_directory)
    
    try:
        # 打开 JSON 文件并加载数据
        with open(json_file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        # 遍历 JSON 数组中的每个对象
        for item in data:
            title = item.get('title')
            content = item.get('content')
            
            if title and content:
                # 构建文件的完整路径
                file_path = os.path.join(output_directory, title)
                # 打开文件并写入内容
                with open(file_path, 'w', encoding='utf-8') as output_file:
                    output_file.write(content)
                print(f"文件 {title} 已成功创建。")
            else:
                print("跳过无效的对象,缺少 'title' 或 'content' 属性。")
    
    except FileNotFoundError:
        print(f"未找到 JSON 文件: {json_file_path}")
    except json.JSONDecodeError:
        print(f"JSON 文件格式错误: {json_file_path}")

# 示例用法
if __name__ == "__main__":
    json_file_path = "blogs.json"  # 替换为实际的 JSON 文件路径
    output_directory = "./blogs"  # 替换为实际的输出目录
    json_to_files(json_file_path, output_directory)
posted @   caijianhong  阅读(255)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示