← 返回会话

export_chrome_bookmarks.py

3.3 KB · 纯文本展示
#!/usr/bin/env python3
"""把 Chrome 当前配置档的 Bookmarks JSON 转成 Netscape 书签 HTML。"""
import json
import os
import sys
import time
from html import escape

CHROME_BOOKMARKS = os.path.expanduser(
    "~/Library/Application Support/Google/Chrome/Default/Bookmarks"
)
ROOT_TITLES = {
    "bookmark_bar": "书签栏",
    "other": "其他书签",
    "synced": "移动设备书签",
}


def load(path):
    with open(path, encoding="utf-8") as fh:
        return json.load(fh)


def counters(node):
    """统计子树里的文件夹数(不含根)与书签数。"""
    folders = 0
    urls = 0
    for child in node.get("children") or []:
        if child.get("type") == "url":
            urls += 1
        elif child.get("type") == "folder":
            folders += 1
            sub = counters(child)
            folders += sub[0]
            urls += sub[1]
    return folders, urls


def emit(node, out, indent):
    pad = "    " * indent
    for child in node.get("children") or []:
        if child.get("type") == "url":
            name = escape(child.get("name") or child.get("url", ""), quote=True)
            url = escape(child.get("url", ""), quote=True)
            add_date = str(child.get("date_added") or "")
            out.append(
                f'{pad}<DT><A HREF="{url}" ADD_DATE="{add_date}">{name}</A>'
            )
        elif child.get("type") == "folder":
            name = escape(child.get("name") or "", quote=True)
            add_date = str(child.get("date_added") or "")
            out.append(f'{pad}<DT><H3 ADD_DATE="{add_date}">{name}</H3>')
            out.append(f"{pad}<DL><p>")
            emit(child, out, indent + 1)
            out.append(f"{pad}</DL><p>")


def main():
    data = load(CHROME_BOOKMARKS)
    roots = data["roots"]
    total_folders = 0
    total_urls = 0
    lines = [
        "<!DOCTYPE NETSCAPE-Bookmark-file-1>",
        "<!-- This is an automatically generated file.",
        "     It will be read and overwritten.",
        "     DO NOT EDIT! -->",
        '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">',
        "<TITLE>Bookmarks</TITLE>",
        "<H1>Bookmarks</H1>",
        "<DL><p>",
    ]
    for key in ("bookmark_bar", "other", "synced"):
        root = roots.get(key)
        if not root:
            continue
        folders, urls = counters(root)
        total_folders += folders
        total_urls += urls
        if not root.get("children"):
            continue
        lines.append(f'    <DT><H3>{ROOT_TITLES[key]}</H3>')
        lines.append("    <DL><p>")
        emit(root, lines, 2)
        lines.append("    </DL><p>")
    lines.append("</DL><p>")

    stamp = time.strftime("%Y%m%d-%H%M%S")
    out_path = os.path.expanduser(f"~/Downloads/chrome-bookmarks-export-{stamp}.html")
    with open(out_path, "w", encoding="utf-8") as fh:
        fh.write("\n".join(lines) + "\n")

    print(json.dumps({
        "export_path": out_path,
        "bookmarks": total_urls,
        "folders_excluding_roots": total_folders,
        "source_profile": "Default",
        "source_file": CHROME_BOOKMARKS,
        "source_mtime": time.strftime(
            "%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(CHROME_BOOKMARKS))
        ),
    }, ensure_ascii=False))


if __name__ == "__main__":
    sys.exit(main())