douxiaoyun.js/get-cookie.js
2026-07-17 18:46:36 +08:00

211 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 获取抖音 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);
});