目 录CONTENT

文章目录

【夸克网盘目录树】将夸克网盘链接的所有文件目录导出并建站

JIN
JIN
2026-02-13 / 0 评论 / 0 点赞 / 29 阅读 / 0 字
广告 广告

夸克网盘目录树生成:

效果演示:


一、打开链接,开始爬取

测试:对13.5TB共81000个文件夹的网课视频目录扫描,爬取4层目录树举例

若只需要导出3层修改 const MAX_DEPTH=4; 后面参数为3即可

3层仅需20秒左右
4层需5-10分钟

(async () => {

/* 自动读取分享 */
const raw=JSON.parse(sessionStorage.getItem("_share_args"))?.value;
if(!raw){alert("请在分享文件列表页运行");return;}

const pwd=raw.pwd_id;
const st=encodeURIComponent(raw.stoken);

console.log("扫描分享:",pwd);

/* 参数 */
const MAX_DEPTH=4;
const CONCURRENCY=8;
const RETRY=4;

let visited=new Set();
let queue=[{fid:"0",path:"",depth:0}];
let results=[];

function sleep(t){return new Promise(r=>setTimeout(r,t));}

async function fetchPage(fid,page){
    for(let i=0;i<RETRY;i++){
        try{
            const r=await fetch(
`https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail?pr=ucpro&fr=pc&pwd_id=${pwd}&stoken=${st}&pdir_fid=${fid}&force=0&_page=${page}&_size=50`,
            {credentials:"include"});
            if(r.status===400) throw "参数失效";
            return await r.json();
        }catch(e){
            await sleep(800+Math.random()*1200);
        }
    }
    return null;
}

async function scanDir(task){
if(task.depth>MAX_DEPTH)return;
let page=1;

while(true){
const j=await fetchPage(task.fid,page);
if(!j?.data?.list?.length)break;

for(const f of j.data.list){
const full=task.path+"/"+f.file_name;
results.push(full);

if(f.dir&&!visited.has(f.fid)){
visited.add(f.fid);
queue.push({fid:f.fid,path:full,depth:task.depth+1});
}
}
page++;
await sleep(120+Math.random()*180);
}
}

async function worker(id){
while(queue.length){
const task=queue.shift();
if(!task)break;
await scanDir(task);

if(id===0)
console.log(`已扫:${results.length} | 目录:${visited.size} | 队列:${queue.length}`);
}
}

console.log("开始扫描…");
await Promise.all(Array.from({length:CONCURRENCY},(_,i)=>worker(i)));

console.log("完成:",results.length);

const txt=results.join("\n");

try{await navigator.clipboard.writeText(txt);}catch(e){}

const blob=new Blob([txt]);
const a=document.createElement("a");
a.href=URL.createObjectURL(blob);
a.download="quark目录.txt";
a.click();

})();

二、将.txt文件转成.html

2.1、thonny-merger-3tree-G

此步thonny代码作用:将多个 从夸克分享链接爬取的.txt目录树文件 合并成1个森林并且转换成html文件

合并几棵树就粘贴进去几棵树的文件路径

下面以3棵树举列

import json
import os

# ====== 配置区域 ======
# 输入文件列表
files = [
    r"C:\Users\JinRC\Downloads\quark目录 (1).txt",
    r"C:\Users\JinRC\Downloads\quark目录 (2).txt",
    r"C:\Users\JinRC\Downloads\quark目录 (3).txt"
]

# 输出文件路径
output_file = r"C:\Users\JinRC\Downloads\directory_tree_ultra.html"

# ====================
# 1. 高效构建目录树字典
# ====================
def build_tree(file_paths):
    tree = {}
    for file_path in file_paths:
        if not os.path.exists(file_path):
            print(f"警告:找不到文件 {file_path}")
            continue
  
        with open(file_path, "r", encoding="utf-8") as f:
            for line in f:
                path = line.strip().strip("/")
                if not path:
                    continue
  
                parts = path.split("/")
                current = tree
                for part in parts:
                    if part not in current:
                        current[part] = {}
                    current = current[part]
    return tree

print("正在解析文件结构,请稍候...")
dir_structure = build_tree(files)
print("结构解析完成,正在生成压缩数据...")

# 将字典转换为压缩的 JSON 字符串 (ensure_ascii=False 保证中文不转码,减少体积)
json_data = json.dumps(dir_structure, ensure_ascii=False, separators=(',', ':'))

# ====================
# 2. 注入现代化前端模板
# ====================
html_template = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Resource Navigator</title>
<style>
    :root {{
        --bg: #0f172a;           /* 深蓝背景 */
        --panel: #1e293b;        /* 面板颜色 */
        --text: #e2e8f0;         /* 主文字 */
        --text-dim: #94a3b8;     /* 次级文字 */
        --accent: #3b82f6;       /* 提亮蓝 */
        --hover: #334155;        /* 悬停色 */
        --border: #334155;       /* 边框线 */
        --line: #475569;         /* 树状连线 */
    }}

    * {{ box-sizing: border-box; outline: none; }}
  
    body {{
        margin: 0; padding: 0;
        background: var(--bg);
        color: var(--text);
        font-family: 'Segoe UI', -apple-system, Roboto, sans-serif;
        height: 100vh;
        overflow: hidden;
        display: flex;
        flex-direction: column;
    }}

    /* 顶部导航栏 (Mac 风格) */
    .header {{
        padding: 12px 20px;
        background: rgba(30, 41, 59, 0.8);
        backdrop-filter: blur(10px);
        border-bottom: 1px solid var(--border);
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-shrink: 0;
    }}

    .window-controls {{
        display: flex;
        gap: 8px;
    }}
    .dot {{ width: 12px; height: 12px; border-radius: 50%; }}
    .dot.red {{ background: #ef4444; }}
    .dot.yellow {{ background: #f59e0b; }}
    .dot.green {{ background: #22c55e; }}

    .search-box {{
        position: relative;
        width: 300px;
    }}
    .search-input {{
        width: 100%;
        background: var(--bg);
        border: 1px solid var(--border);
        color: var(--text);
        padding: 6px 12px 6px 35px;
        border-radius: 6px;
        font-size: 13px;
        transition: all 0.2s;
    }}
    .search-input:focus {{
        border-color: var(--accent);
        box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
    }}
    .search-icon {{
        position: absolute;
        left: 10px; top: 50%;
        transform: translateY(-50%);
        width: 14px; height: 14px;
        fill: var(--text-dim);
    }}

    /* 目录树容器 */
    .tree-container {{
        flex: 1;
        overflow-y: auto;
        padding: 20px;
        scrollbar-width: thin;
        scrollbar-color: var(--border) var(--bg);
    }}
  
    .tree-container::-webkit-scrollbar {{ width: 8px; }}
    .tree-container::-webkit-scrollbar-thumb {{ background: var(--border); border-radius: 4px; }}

    /* 树节点样式 */
    ul {{
        list-style: none;
        padding-left: 20px; /* 缩进 */
        margin: 0;
        position: relative;
    }}

    /* 连线系统 */
    ul::before {{
        content: '';
        position: absolute;
        left: 0px; top: 0; bottom: 0;
        width: 1px;
        background: var(--line);
        opacity: 0.5;
    }}
  
    /* 根节点不需要左侧长线 */
    #root-ul::before {{ display: none; }}

    li {{ position: relative; }}

    .row {{
        display: flex;
        align-items: center;
        padding: 5px 8px;
        border-radius: 4px;
        cursor: pointer;
        user-select: none;
        transition: background 0.15s;
        margin-bottom: 2px;
        position: relative;
    }}

    .row:hover {{ background: var(--hover); }}

    /* 节点横线 */
    .has-line::before {{
        content: '';
        position: absolute;
        left: -20px; top: 18px;
        width: 20px; height: 1px;
        background: var(--line);
        opacity: 0.5;
    }}

    /* 图标 */
    .icon {{
        width: 18px; height: 18px;
        margin-right: 8px;
        flex-shrink: 0;
    }}
    .icon-folder {{ fill: #fbbf24; }} /* 黄色文件夹 */
    .icon-file {{ fill: #94a3b8; }}   /* 灰色文件 */
    .arrow {{
        width: 14px; height: 14px;
        margin-right: 4px;
        fill: var(--text-dim);
        transition: transform 0.2s;
    }}
    .expanded .arrow {{ transform: rotate(90deg); }}
  
    .name {{
        font-size: 14px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }}
  
    /* 搜索高亮 */
    .highlight {{
        color: var(--accent);
        font-weight: bold;
    }}
  
    .hidden {{ display: none !important; }}

</style>
</head>
<body>

<svg style="display:none">
    <symbol id="icon-arrow" viewBox="0 0 24 24"><path d="M10 17l5-5-5-5v10z"/></symbol>
    <symbol id="icon-folder" viewBox="0 0 24 24"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></symbol>
    <symbol id="icon-file" viewBox="0 0 24 24"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
    <symbol id="icon-search" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></symbol>
</svg>

<div class="header">
    <div class="window-controls">
        <div class="dot red"></div>
        <div class="dot yellow"></div>
        <div class="dot green"></div>
    </div>
    <div style="font-size: 14px; font-weight: 600; color: var(--text-dim); margin-left:12px;">资源资源库 Pro</div>
    <div style="flex:1"></div>
    <div class="search-box">
        <svg class="search-icon"><use href="#icon-search"></use></svg>
        <input type="text" class="search-input" id="searchInput" placeholder="搜索文件...">
    </div>
</div>

<div class="tree-container" id="treeRoot">
    <ul id="root-ul"></ul>
</div>

<script>
    // 1. 接收 Python 注入的数据
    const treeData = {json_data};
  
    const rootUl = document.getElementById('root-ul');
    const searchInput = document.getElementById('searchInput');

    // 2. 核心渲染函数:只生成 HTML 字符串,不频繁操作 DOM
    function createNodeHtml(name, data, isRoot = false) {{
        const isFolder = Object.keys(data).length > 0;
        const iconId = isFolder ? '#icon-folder' : '#icon-file';
        const arrowHtml = isFolder 
            ? `<svg class="arrow"><use href="#icon-arrow"></use></svg>` 
            : `<span style="width:18px; display:inline-block"></span>`; // 占位符
  
        // 将数据暂存在 DOM 属性中,方便后续点击时读取
        // 注意:这里我们不直接把 data 写入 HTML 属性,因为太大了
        // 我们用路径索引的方式(但在简单实现中,我们可以直接利用 JS 的闭包特性)
  
        return `
            <li class="${{isFolder ? 'folder' : 'file'}}">
                <div class="row ${{isRoot ? '' : 'has-line'}}" onclick="toggle(this)">
                    ${{arrowHtml}}
                    <svg class="icon ${{isFolder ? 'icon-folder' : 'icon-file'}}"><use href="${{iconId}}"></use></svg>
                    <span class="name">${{name}}</span>
                </div>
                ${{isFolder ? '<ul style="display:none"></ul>' : ''}}
            </li>
        `;
    }}

    // 3. 懒加载逻辑
    function renderLevel(container, data) {{
        // 对键名排序:文件夹在前,文件在后
        const keys = Object.keys(data).sort((a, b) => {{
            const aIsFolder = Object.keys(data[a]).length > 0;
            const bIsFolder = Object.keys(data[b]).length > 0;
            if (aIsFolder && !bIsFolder) return -1;
            if (!aIsFolder && bIsFolder) return 1;
            return a.localeCompare(b, 'zh-CN');
        }});

        let html = '';
        keys.forEach(key => {{
            html += createNodeHtml(key, data[key], container.id === 'root-ul');
        }});
        container.innerHTML = html;
  
        // 绑定数据到 DOM 元素,以便点击时能找到对应的子数据
        Array.from(container.children).forEach(li => {{
            const name = li.querySelector('.name').textContent;
            li._childData = data[name];
        }});
    }}

    // 初始化渲染第一层
    renderLevel(rootUl, treeData);

    // 4. 交互逻辑
    window.toggle = function(rowDiv) {{
        const li = rowDiv.parentElement;
        if (!li.classList.contains('folder')) return; // 是文件就不管

        const ul = li.querySelector('ul');
        const arrow = rowDiv.querySelector('.arrow');
  
        // 如果是第一次展开,需要渲染子内容
        if (ul.innerHTML === '') {{
             renderLevel(ul, li._childData);
        }}

        const isHidden = ul.style.display === 'none';
        ul.style.display = isHidden ? 'block' : 'none';
  
        if (isHidden) {{
            rowDiv.classList.add('expanded');
        }} else {{
            rowDiv.classList.remove('expanded');
        }}
    }};

    // 5. 简单搜索功能 (纯前端过滤)
    searchInput.addEventListener('input', (e) => {{
        const term = e.target.value.toLowerCase();
        if(!term) {{
            // 清空搜索时,简单起见,刷新页面恢复初始状态(或者重新渲染根目录)
            if(rootUl._isSearching) {{
                rootUl.innerHTML = '';
                renderLevel(rootUl, treeData);
                rootUl._isSearching = false;
            }}
            return;
        }}
  
        // 搜索模式:暴力递归搜索并扁平化显示
        rootUl._isSearching = true;
        const results = [];
  
        function search(node, path) {{
            for (const key in node) {{
                const currentPath = path ? path + '/' + key : key;
                if (key.toLowerCase().includes(term)) {{
                    // 找到匹配
                    const isFolder = Object.keys(node[key]).length > 0;
                    results.push({{ name: key, path: currentPath, isFolder: isFolder }});
                }}
                if (Object.keys(node[key]).length > 0) {{
                    search(node[key], currentPath);
                }}
            }}
        }}
  
        search(treeData, '');
  
        // 渲染搜索结果
        let html = '';
        if(results.length === 0) {{
            html = '<div style="padding:20px; text-align:center; color:#64748b">未找到相关文件</div>';
        }} else {{
            // 限制显示前100条防止卡顿
            results.slice(0, 100).forEach(item => {{
                const iconId = item.isFolder ? '#icon-folder' : '#icon-file';
                html += `
                <li>
                    <div class="row" style="padding-left:0">
                        <svg class="icon ${{item.isFolder ? 'icon-folder' : 'icon-file'}}"><use href="${{iconId}}"></use></svg>
                        <div>
                            <div class="name highlight">${{item.name}}</div>
                            <div style="font-size:12px; color:#64748b">${{item.path}}</div>
                        </div>
                    </div>
                </li>`;
            }});
            if(results.length > 100) {{
                 html += '<div style="padding:10px; text-align:center; color:#64748b">结果过多,仅显示前100条...</div>';
            }}
        }}
        rootUl.innerHTML = html;
    }});

</script>
</body>
</html>
"""

# 写入文件
with open(output_file, "w", encoding="utf-8") as f:
    f.write(html_template)

print(f"完成!已生成极速版目录树:{output_file}")
print(f"文件大小优化预计已减少 90%,且界面已升级为 Pro 版。")

2.2、thonny-mk-1tree-C

此步thonny代码作用:将单个.txt目录树文件 转换成html文件

import html

input_file = r"C:\Users\JinRC\Downloads\quark目录 (5).txt"
output_file = r"C:\Users\JinRC\Downloads\quark目录 (5)tree.html"


# ---------- 构建树 ----------
tree = {}

with open(input_file, "r", encoding="utf-8") as f:
    for line in f:
        path = line.strip().strip("/")
        if not path:
            continue

        parts = path.split("/")
        node = tree
        for p in parts:
            node = node.setdefault(p, {})


# ---------- 生成HTML ----------
def render(node):
    html_parts = ["<ul>"]
    for name, child in sorted(node.items()):
        safe = html.escape(name)

        if child:
            html_parts.append(
                f'<li class="folder"><span class="toggle">📁 {safe}</span>'
            )
            html_parts.append(render(child))
            html_parts.append("</li>")
        else:
            html_parts.append(f"<li class='file'>📄 {safe}</li>")

    html_parts.append("</ul>")
    return "\n".join(html_parts)


html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>目录树</title>

<style>
body {{
    font-family: Consolas, monospace;
    background:#0f172a;
    color:#e5e7eb;
}}

ul {{
    list-style:none;
    padding-left:18px;
}}

.folder > ul {{
    display:none;
}}

.toggle {{
    cursor:pointer;
    color:#60a5fa;
}}

.file {{
    color:#d1d5db;
}}
</style>

<script>
document.addEventListener("click",function(e){{
    if(e.target.classList.contains("toggle")){{
        let ul=e.target.parentNode.querySelector("ul");
        if(ul) ul.style.display=ul.style.display=="none"?"block":"none";
    }}
}});
</script>

</head>
<body>
<h2>资源目录</h2>
{render(tree)}
</body>
</html>
"""

with open(output_file, "w", encoding="utf-8") as f:
    f.write(html_content)

print("完成 →", output_file)

三、在halo的编辑器中嵌入html代码

3.1、森林代码(涵盖搜素)

执行的是2.1的多棵树,下载这串代码粘贴进halo编辑器


📥 下载 G-3tree.txt

3.2、单颗树代码(无搜素功能)

执行的是2.2的单棵树,下载这串代码粘贴进halo编辑器


📥 下载 c-1tree.txt

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
广告 广告

评论区