有道云笔记文件解析PHP接口代码

原创软件 · 16 天前 · 87 人浏览
有道云笔记文件解析PHP接口代码

说明

做文件更新的时候,一个好用而又简单的下载路径是比较重要的。
本来考虑的是在软件中做解析,但如果官方接口更改的话,那么无法更新的话,软件主体就需要客户手动进行更新了。这样更为麻烦。
也可以根据下面的PHP代码,直接做一个请求接口,返回第一个附件的下载地址用来做自动更新。这样也更为省事。

测试图片

2025-06-12_10-52-33.png

测试地址

https://share.note.youdao.com/s/SAGUBzGP

使用说明

将PHP代码上传至服务器中,加入参数:share=SAGUBzGP
如:https://xxx.com/youdao.php?share=SAGUBzGP

PHP代码

<?php
if (isset($_GET['api']) && isset($_GET['share'])) {
    header('Content-Type: application/json; charset=utf-8');

    function jsonResponse($code, $data) {
        echo json_encode(['code' => $code, 'data' => $data], JSON_UNESCAPED_UNICODE);
        exit;
    }

    $shareId = $_GET['share'];
    if (!preg_match('/^[a-zA-Z0-9]+$/', $shareId)) {
        jsonResponse(1, '无效 share 参数');
    }

    $headers = @get_headers('https://note.youdao.com/s/' . $shareId, 1);
    if (!$headers || !isset($headers['Location'])) {
        jsonResponse(1, '未能解析 note ID');
    }
    $loc = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location'];
    parse_str(parse_url($loc, PHP_URL_QUERY), $params);
    if (empty($params['id'])) {
        jsonResponse(1, '未解析到 ID');
    }
    $noteId = $params['id'];

    $apiUrl  = "https://note.youdao.com/yws/api/note/{$noteId}?sev=j1&editorType=1&editorVersion=new-json-editor";
    $jsonStr = @file_get_contents($apiUrl);
    if (!$jsonStr) {
        jsonResponse(1, '获取内容失败');
    }
    $resp = json_decode($jsonStr, true);
    if (json_last_error() !== JSON_ERROR_NONE || empty($resp['content'])) {
        jsonResponse(1, '解析失败');
    }

    $data = json_decode($resp['content'], true);
    if (json_last_error() !== JSON_ERROR_NONE || empty($data['5'])) {
        jsonResponse(1, '缺少数据');
    }

    $results = [];
    foreach ($data['5'] as $item) {
        if (!isset($item['4']['fn'], $item['4']['re'])) continue;

        $name = $item['4']['fn'];
        $src  = $item['4']['re'];
        $finalUrl = '';

        $ch = curl_init($src);
        curl_setopt_array($ch, [
            CURLOPT_NOBODY         => true,
            CURLOPT_HEADER         => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_USERAGENT      => 'Mozilla/5.0'
        ]);
        $headerStr = curl_exec($ch);
        curl_close($ch);

        if (preg_match('/Location:\s*(\S+)/i', $headerStr, $match)) {
            $finalUrl = trim($match[1]);
        } else {
            $finalUrl = $src;
        }

        $results[] = ['name' => $name, 'url' => $finalUrl];
    }

    if (!empty($results)) {
        jsonResponse(0, $results);
    }
    jsonResponse(1, '未找到附件');
}
?>

<!DOCTYPE html>
<html lang="zh-CN" class="scroll-smooth">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>有道云笔记下载</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    @layer utilities {
      .bg-gradient { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
    }
    [v-cloak] { display: none; }
  </style>
</head>
<body class="min-h-screen bg-gray-50 flex flex-col">
  <header class="bg-gradient text-white py-12 text-center">
    <h1 class="text-4xl font-extrabold">有道云笔记下载</h1>
    <p class="mt-2 text-lg opacity-90">获取云笔记中的所有文件链接</p>
  </header>
  <main id="app" v-cloak class="flex-grow container mx-auto px-4 py-8">
    <div v-if="!loaded && !error" class="flex justify-center items-center">
      <svg class="animate-spin h-8 w-8 text-purple-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
        <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
        <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
      </svg>
      <p class="ml-3 text-gray-700">加载中,请稍候...</p>
    </div>
    <div v-if="error" class="text-center text-red-600 font-semibold">❌ {{ error }}</div>
    <div v-if="files.length" class="space-y-4">
      <div v-for="file in files" :key="file.url" class="p-4 bg-white rounded-lg shadow hover:shadow-lg transition">
        <div class="flex items-center justify-between gap-4">
          <div class="max-w-full overflow-hidden">
            <p class="font-medium text-gray-800">{{ file.name }}</p>
            <a :href="file.url"
               class="text-sm text-indigo-600 hover:underline inline-block truncate max-w-full"
               target="_blank"
               :title="file.url">{{ file.url }}</a>
          </div>
          <button @click="open(file.url)"
                  class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 whitespace-nowrap">
            下载
          </button>
        </div>
      </div>
    </div>
  </main>
  <footer class="text-center py-4 text-sm text-gray-500">精易论坛 LaoDan © 2025</footer>

  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
  <script>
    const { createApp } = Vue;
    createApp({
      data() { return { files: [], error: '', loaded: false }; },
      methods: {
        open(url) { window.open(url, '_blank'); },
        fetchData(id) {
          fetch(`?api=1&share=${encodeURIComponent(id)}`)
            .then(r => r.json())
            .then(d => d.code === 0 ? this.files = d.data : this.error = d.data)
            .catch(() => this.error = '网络错误')
            .finally(() => this.loaded = true);
        }
      },
      mounted() {
        const id = new URLSearchParams(location.search).get('share');
        if (!id) { this.error = '缺少 share 参数'; this.loaded = true; return; }
        this.fetchData(id);
      }
    }).mount('#app');
  </script>
</body>
</html>