首页 > 其他分享 >有道云笔记数据备份相关

有道云笔记数据备份相关

时间:2024-02-11 16:55:05浏览次数:33  
标签:cookie 数据备份 String 有道 private static 笔记 new import

PC客户端导出

最新版有导出全部文件的功能,但限制90天一次,除非开通会员(无限次)

开放平台API

开放平台地址

申请理由

我是一个个人开发者,API不是在APP或网站中使用,主要是作为个人的学习,测试及自己笔记的整理归档等。

主页为 https 协议,但 提交接口 为 http 协议,请求被浏览器阻止了,我们可以 F12 手动修改 html 元素,之后再提交

应用图片必须按照要求的格式和大小来上传,不然报错:图片格式不正确,可以使用 在线图片裁剪工具 来帮忙裁剪。

还是报错:未知错误,此功能可能有道云也不维护了。

使用接口模拟网页端调用

根据父目录查询子文件列表

https://note.youdao.com/yws/api/personal/file/F98AA8589CDA4035BEF23A271A295E46

根据文件ID下载文件内容

https://note.youdao.com/yws/api/personal/sync

具体代码

点击查看代码
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Feign;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.form.FormEncoder;
import lombok.Data;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import java.io.*;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

/**
 * 使用接口模拟网页版调用下载markdown类型笔记
 */
public class TestYoudaoyunNoteBackup2 {

    private static final String BASE_URL = "https://note.youdao.com/yws/api";
    private static final String CSTK = "lUwgyhL9"; // 从网页端请求中获取
    private static final String ROOT_FILEID = "WEBa042d0b14d7ac9f04727385b8d4c0811"; // 从网页端 url 中获取
    private static final StatisticsInfo STATISTICS_INFO = new StatisticsInfo();

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // 忽略未知属性
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public static void main(String[] args) throws IOException {
        String cookie = getCookie();
        YoudaoyunNoteClient client = createClient();
        ;
        String rootFilePath = getRootFilePath();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        System.out.println(new Date());
        listEntryRecursiveAndFlatMap(client, ROOT_FILEID, cookie, rootFilePath, "");
        stopWatch.stop();
        System.out.println("花费时间:" + stopWatch.getTime() + "ms");
        System.out.println("统计信息:" + STATISTICS_INFO);
    }

    /**
     * 单线程备份,文件名为当前文件名称+上级目录名称
     */
    private static void listEntryRecursiveAndFlatMap(YoudaoyunNoteClient client, String parentFileId, String cookie,
                                                     String rootFilePath, String parentFileName) {
        ListByParentIdResponse response = client.listByParentId(parentFileId, true, true, 100, 1, false, "listPageByParentId", CSTK, cookie);
        for (FileInfo fileInfo : response.getEntries()) {
            FileEntry fileEntry = fileInfo.getFileEntry();
            String name = fileEntry.getName();
            Boolean dir = fileEntry.getDir();
            String fileId = fileEntry.getId();
            if (StringUtils.isNotBlank(parentFileName)) {
                name = parentFileName + "-" + name;
            }
            File file = new File(rootFilePath, name);
            if (dir) {
                boolean mkdirs = file.mkdirs();
                if (!mkdirs) {
                    throw new RuntimeException("创建文件夹失败");
                }
                STATISTICS_INFO.folderQuantityIncr();
                listEntryRecursiveAndFlatMap(client, fileId, cookie, file.getAbsolutePath(), name);
            } else {
                if (fileEntry.isMarkdown()) {
                    String fileContent = client.sync("download", fileId, -1, true, CSTK, cookie);
                    writeToFile(file, fileContent);
                    STATISTICS_INFO.fileQuantityIncr();
                }
            }
        }
    }

    private static String getRootFilePath() {
        String targetFilePath = "/Users/xxx/Desktop/files/youdaoyunnotebackup";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss");
        String currentFormatTime = dateTimeFormatter.format(LocalDateTime.now());
        String rootFilePath = targetFilePath + "/" + currentFormatTime;
        File file = new File(rootFilePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        return rootFilePath;
    }

    private static void writeToFile(File targetFile, String fileContent) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(targetFile))) {
            bw.write(fileContent);
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("文件下载成功-> " + targetFile.getAbsolutePath() + "  " + new Date());
    }

    private static String getCookie() throws IOException {
        // 从网页端请求中获取
        InputStream inputStream = TestYoudaoyunNoteBackup2.class.getClassLoader().getResourceAsStream("youdaoyun/cookie.txt");
        return IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
    }

    private static YoudaoyunNoteClient createClient() {
        Feign.Builder builder = Feign.builder()
                .decoder(new MyDecoder())
                .contract(new SpringMvcContract())
                .encoder(new FormEncoder());
        return builder.target(YoudaoyunNoteClient.class, BASE_URL);
    }

    interface YoudaoyunNoteClient {
        /**
         * 根据父文件ID查询子文件列表
         *
         * @param parentFileId 父文件ID
         * @param all          是否查询全部 既包含文件夹也包含文件
         * @param f            未知
         * @param len          查询长度
         * @param sort         是否排序
         * @param isReverse    是否倒序
         * @param method       服务器端执行方法
         * @param cstk         未知 用户信息相关
         * @param cookie       用户信息
         * @return
         */
        @GetMapping("/personal/file/{parentFileId}")
        ListByParentIdResponse listByParentId(@PathVariable("parentFileId") String parentFileId,
                                              @RequestParam("all") Boolean all,
                                              @RequestParam("f") Boolean f,
                                              @RequestParam("len") Integer len,
                                              @RequestParam("sort") Integer sort,
                                              @RequestParam("salt") Boolean isReverse,
                                              @RequestParam("method") String method,
                                              @RequestParam("cstk") String cstk,
                                              @RequestHeader("Cookie") String cookie);

        @PostMapping(value = "/personal/sync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
        String sync(@RequestParam("method") String method,
                    @RequestPart("fileId") String fileId,
                    @RequestPart("version") Integer version,
                    @RequestPart("read") Boolean read,
                    @RequestPart("cstk") String cstk,
                    @RequestHeader("Cookie") String cookie);
    }

    static class MyDecoder implements Decoder {

        @Override
        public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
            if (String.class.equals(type)) {
                return Util.toString(response.body().asReader(Util.UTF_8));
            }
            InputStream inputStream = response.body().asInputStream();
            return OBJECT_MAPPER.readValue(inputStream, ResolvableType.forType(type).getRawClass());
        }
    }

    @Data
    static class ListByParentIdResponse {
        private List<FileInfo> entries;
    }

    @Data
    static class FileInfo {
        private FileEntry fileEntry;
    }

    @Data
    static class FileEntry {

        private String name;
        private Boolean dir;
        private String id;
        //0表示markdown 5表示word
        private String noteType;

        @JsonIgnore
        public boolean isMarkdown() {
            return "0".equals(noteType);
        }
    }

    @Data
    static class StatisticsInfo {
        private int folderQuantity;
        private int fileQuantity;

        private void folderQuantityIncr() {
            folderQuantity++;
        }

        private void fileQuantityIncr() {
            fileQuantity++;
        }
    }
}

使用别人封装的脚本

youdaonote-pull github

使用 Python 编写,底层也是对上述两个接口的封装,我们的 Java 实现也是参考了此脚本。

参考

有道云笔记 备份与导出
youdaonote-pull github

标签:cookie,数据备份,String,有道,private,static,笔记,new,import
From: https://www.cnblogs.com/strongmore/p/17131245.html

相关文章

  • Ubuntu 设置合上笔记本盖子不休眠的方法
    Ubuntu设置合上笔记本盖子不休眠的方法编辑下列文件:sudogedit/etc/systemd/logind.conf​​#HandlePowerKey按下电源键后的行为,默认poweroff​​#HandleSleepKey按下挂起键后的行为,默认suspend​​#HandleHibernateKey按下休眠键后的行为,默认hibernate​​#HandleLidS......
  • LINQ学习笔记
    查询表达式varlst=newList<int>{1,3,5,7,9,2,4,6,8,0};varres= fromninlst wheren%2==0&&n>=4 orderbyn selectn;链式表达式varlst=newList<int>{1,3,5,7,9,2,4,6,8,0};varres=lst .Where(n=>......
  • 读千脑智能笔记10_人类智能存在的风险
    1. 人类智能存在的风险1.1. “末日时钟”1.1.1. 核战争引发的大火列为地球毁灭的主要原因1.1.2. 气候变化列为人类自我毁灭的第二大潜在原因1.2. 除非我们刻意加入自私的驱动力、动机或情感,否则智能机器并不会威胁到人类的生存1.2.1. 人类在不远的将来会创造出更多的......
  • 2024/2/10学习进度笔记
    RDD,学名可伸缩的分布式数据集(ResilientDistributedDataset)。是一种对数据集形态的抽象,基于此抽象,使用者可以在集群中执行一系列计算,而不用将中间结果落盘。而这正是之前MR抽象的一个重要痛点,每一个步骤都需要落盘,使得不必要的开销很高。对于分布式系统,容错支持是必不可少的。......
  • [spring] spring学习笔记(3): 通过注解实现依赖注入
    注解Annotation注解是代码中的一种特殊标记,java中的格式为@Anno_Name(pro=value)注解可以被使用在方法,类和属性上;在spring中,使用注解来实现自动装配,可以简化Bean的配置,基本步骤如下:引入依赖开启组件扫描使用注解定义Bean注入依赖引入依赖在新建的spring项目下的src/main......
  • fast.ai 深度学习笔记(三)
    深度学习2:第1部分第6课原文:medium.com/@hiromi_suenaga/deep-learning-2-part-1-lesson-6-de70d626976c译者:飞龙协议:CCBY-NC-SA4.0来自fast.ai课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢Jeremy和Rachel给了我这个......
  • 【学习笔记】李宏毅 2023 春机器学习课程听课记录
    1ChatGPT原理剖析ChatGPT的社会化:学会文字接龙人类引导文字接龙方向模仿人类喜好用增强式学习向模拟老师学习1.1预训练(Pre-train)ChatGPT真正在做的事情本质上是文字接龙,将其看成一个函数\(f(x)\),其中的\(x\)自变量可以是用户输入的一个语句,得到的函数就是接下来......
  • fast.ai 机器学习笔记(一)
    机器学习1:第1课原文:medium.com/@hiromi_suenaga/machine-learning-1-lesson-1-84a1dc2b5236译者:飞龙协议:CCBY-NC-SA4.0来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢Jeremy和Rachel给了我这个学习的机会。......
  • fast.ai 机器学习笔记(四)
    机器学习1:第11课原文:medium.com/@hiromi_suenaga/machine-learning-1-lesson-11-7564c3c18bbb译者:飞龙协议:CCBY-NC-SA4.0来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢Jeremy和Rachel给了我这个学习的机会......
  • fast.ai 机器学习笔记(三)
    机器学习1:第8课原文:medium.com/@hiromi_suenaga/machine-learning-1-lesson-8-fa1a87064a53译者:飞龙协议:CCBY-NC-SA4.0来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢Jeremy和Rachel给了我这个学习的机会。......