SUEN

揮一揮API,帶走所有json

算起來語雀也經營了不算短的時間,去年服務器宕機加之玉伯離開是棄絕該平台的直接原因,但根本上,還是因為意識到這個平台骨子裡是封閉的,自我審查到變態程度,更極難自己對接到主流的AI上;最近還發現又多了一個特定IP不能訪問的毛病。去年一年基本沒動,前年課程數據也都擱置在內僅懷舊。

新學期,論壇整理完畢,順手清理之前數據到語雀。看了下,語雀官方去年底更新了API獲取文檔的說明: https://www.yuque.com/yuque/developer/gyht993a76zg54mv 。仅旗舰版空间可使用,還故意僅寫一個思路,基本就是直白地在說,我不想你往外導數據。唉,開放點,就那麼難嗎⋯⋯

好吧,核心的課程數據也就在一個團隊,自己遷出自己,夠了。

客觀上,雖然是以團隊而非空間為導出單位,畢竟,可以一個個團隊導出所有數據,這就意味著,真有要遷移一個整體空間的要求時,收集到全部團隊API,做好時間間隔設置,是可以整體搬家的。當然,因為噁心的 獨有lakebook,導出的json最多就是存檔,幾乎是不可直接對接別家的。

這就很完美閉環,因為封閉,所以封閉;當初超星家的PDG都沒你噁心。

I generated an image with the prompt: ‘farewell image for a platform called Yuque, depicting its closed nature’.–By Grok

清理完本輪,算真的再見了。

text
import requests
import os
import re
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

API_TOKEN = ''
BASE_URL = 'https://www.yuque.com/api/v2'
TEAM_NAMESPACE = 'qrvbic'

headers = {
    'X-Auth-Token': API_TOKEN
}

# 設置重試策略
retry_strategy = Retry(
    total=5,  # 重試次數
    backoff_factor=1,  # 重試間隔時間
    status_forcelist=[429, 500, 502, 503, 504],  # 會觸發重試的 HTTP 狀態碼
    allowed_methods=["HEAD", "GET", "OPTIONS"]  # 適用於重試的 HTTP 方法
)

adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)

# 創建存放導出文件的文件夾
if not os.path.exists(TEAM_NAMESPACE):
    os.makedirs(TEAM_NAMESPACE)

# 獲取團隊中的所有 repository
def get_repos(team_namespace):
    url = f'{BASE_URL}/groups/{team_namespace}/repos'
    try:
        response = http.get(url, headers=headers, verify=False)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Error fetching repositories: {e}")
        return []
    return response.json()

# 獲取每個 repository 中的文檔
def get_docs(repo_id):
    url = f'{BASE_URL}/repos/{repo_id}/docs'
    try:
        response = http.get(url, headers=headers, verify=False)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Error fetching documents: {e}")
        return []
    return response.json()

# 將文檔導出為 Markdown 格式
def export_doc(repo_id, doc_id, title):
    url = f'{BASE_URL}/repos/{repo_id}/docs/{doc_id}?raw=1'
    try:
        response = http.get(url, headers=headers, verify=False)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print(f"Error exporting document '{title}': {e}")
        return

    # 處理文件名中的非法字符
    safe_title = re.sub(r'[\/:*?"<>|]', '_', title)
    
    # 創建 repository 對應的文件夾
    repo_dir = os.path.join(TEAM_NAMESPACE, str(repo_id))
    if not os.path.exists(repo_dir):
        os.makedirs(repo_dir)
    
    # 將文檔保存為 Markdown 文件
    file_path = os.path.join(repo_dir, f"{safe_title}.md")
    with open(file_path, "w") as f:
        f.write(response.text)

# 主邏輯
if __name__ == '__main__':
    # 獲取團隊下的所有 repository
    repos = get_repos(TEAM_NAMESPACE)
    
    if repos:
        # 遍歷每個 repository
        for repo in repos['data']:
            repo_id = repo['id']
            repo_name = repo['name']
            print(f"正在導出 repository: {repo_name}")
            
            # 獲取該 repository 下的所有文檔
            docs = get_docs(repo_id)
            
            # 導出每個文檔
            if docs:
                for doc in docs['data']:
                    title = doc['title']
                    doc_id = doc['id']
                    print(f"  導出文檔: {title}")
                    export_doc(repo_id, doc_id, title)

    print("導出完成!")