// 获取抖音 Cookie 和 sec_user_id 的 Playwright 脚本 // 用法: node get-cookie.js // 会自动打开 Chrome 浏览器,请扫码登录抖音 import { chromium } from "playwright"; import { writeFileSync, readFileSync } from "fs"; import { resolve, dirname } from "path"; import { fileURLToPath } from "url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const CONFIG_PATH = resolve(__dirname, "config.json"); async function main() { console.log("🚀 启动浏览器..."); const browser = await chromium.launch({ channel: "chrome", headless: false, args: ["--disable-blink-features=AutomationControlled"], }); const context = await browser.newContext({ viewport: { width: 1366, height: 768 }, userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", }); const page = await context.newPage(); // 监听网络请求,捕获 sec_user_id let capturedSecUserId = null; page.on("request", (request) => { const url = request.url(); if (url.includes("follow") || url.includes("aweme")) { try { const postData = request.postData(); if (postData && postData.includes("sec_user_id")) { const parsed = JSON.parse(postData); if (parsed.sec_user_id) { capturedSecUserId = parsed.sec_user_id; console.log("✅ 从请求中捕获到 sec_user_id:", capturedSecUserId); } } } catch (e) { // 忽略解析错误 } } }); // 也监听响应,从 body 中提取 sec_user_id page.on("response", async (response) => { const url = response.url(); if (url.includes("user/self") || url.includes("aweme")) { try { const body = await response.text(); const match = body.match(/"sec_uid":"([^"]+)"/); if (match && !capturedSecUserId) { capturedSecUserId = match[1]; console.log("✅ 从响应中捕获到 sec_user_id:", capturedSecUserId); } } catch (e) { // 忽略 } } }); console.log("📱 正在打开抖音登录页..."); await page.goto("https://www.douyin.com/", { waitUntil: "domcontentloaded", timeout: 30000, }); console.log("🔑 请在浏览器中扫码登录抖音..."); console.log(" (等待登录完成,检测到登录后会自动继续)"); // 等待登录成功:检测 cookie 中是否有 sessionid await waitForLogin(context); console.log("✅ 登录成功!"); // 导航到个人主页以触发 sec_user_id 相关请求 console.log("📋 正在访问个人主页获取 sec_user_id..."); await page.goto("https://www.douyin.com/user/self?from_tab_name=main", { waitUntil: "networkidle", timeout: 30000, }); // 等待页面加载 await page.waitForTimeout(3000); // 尝试从页面数据中提取 sec_user_id if (!capturedSecUserId) { capturedSecUserId = await extractSecUserIdFromPage(page); } // 如果还没获取到,尝试点击关注按钮触发请求 if (!capturedSecUserId) { console.log("🖱️ 尝试触发关注列表请求以获取 sec_user_id..."); try { // 点击关注按钮 const followBtn = page.locator('[data-e2e="follow-count"]').first(); if (await followBtn.isVisible({ timeout: 5000 })) { await followBtn.click(); await page.waitForTimeout(3000); } } catch (e) { console.log(" 未能找到关注按钮,尝试其他方式..."); } // 再次尝试从请求中获取 if (!capturedSecUserId) { capturedSecUserId = await extractSecUserIdFromPage(page); } } // 获取所有 cookies const cookies = await context.cookies(); const cookieString = cookies .map((c) => `${c.name}=${c.value}`) .join("; "); console.log("\n========================================"); console.log("📋 获取结果:"); console.log("========================================"); console.log("Cookie:", cookieString.substring(0, 200) + "..."); console.log("sec_user_id:", capturedSecUserId || "❌ 未获取到"); console.log("========================================\n"); // 读取现有配置 let config = {}; try { config = JSON.parse(readFileSync(CONFIG_PATH, "utf8")); } catch (e) { console.log(" 未找到现有配置,将创建新配置"); } // 更新配置 config.cookie = cookieString; if (capturedSecUserId) { config.sec_user_id = capturedSecUserId; } writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 4), "utf8"); console.log(`💾 配置已保存到: ${CONFIG_PATH}`); if (!capturedSecUserId) { console.log("\n⚠️ 未能自动获取 sec_user_id"); console.log(" 请参考 README 手动获取:"); console.log(" 1. 按 F12 打开开发者工具 -> Network"); console.log(" 2. 筛选 'follow'"); console.log(" 3. 点击关注按钮,在请求 Payload 中找到 sec_user_id"); console.log(" 4. 手动填入 config.json"); } await browser.close(); console.log("👋 浏览器已关闭,脚本执行完毕"); } // 等待用户登录成功 async function waitForLogin(context) { return new Promise((resolve) => { const checkInterval = setInterval(async () => { const cookies = await context.cookies(); const hasSession = cookies.some((c) => c.name === "sessionid"); if (hasSession) { clearInterval(checkInterval); resolve(); } }, 2000); }); } // 从页面中提取 sec_user_id async function extractSecUserIdFromPage(page) { // 方法1: 从页面 URL 提取 const url = page.url(); const urlMatch = url.match(/user\/([^?]+)/); if (urlMatch && urlMatch[1] !== "self") { return urlMatch[1]; } // 方法2: 从页面 HTML 中的 __INITIAL_STATE__ 提取 try { const html = await page.content(); const match = html.match(/"sec_uid":"([^"]+)"/); if (match) return match[1]; } catch (e) { // 忽略 } // 方法3: 从 window.__INITIAL_STATE__ 提取 try { const secUid = await page.evaluate(() => { const state = window.__INITIAL_STATE__; if (state?.user?.sec_uid) return state.user.sec_uid; return null; }); if (secUid) return secUid; } catch (e) { // 忽略 } return null; } main().catch((err) => { console.error("❌ 脚本执行出错:", err.message); process.exit(1); });