Procházet zdrojové kódy

Refactor Baijiahao API integration to improve works count retrieval and error handling; implement retry logic for dispersed authentication issues, enhance logging for API responses, and ensure compatibility with updated data structures. Update cookie management for API requests and streamline account information extraction process.

Ethanfly před 15 hodinami
rodič
revize
dcadb45af7

binární
server/python/platforms/__pycache__/baijiahao.cpython-313.pyc


+ 53 - 9
server/python/platforms/baijiahao.py

@@ -133,25 +133,69 @@ class BaijiahaoPublisher(BasePublisher):
                 except Exception as e:
                 except Exception as e:
                     print(f"[{self.platform_name}] 获取粉丝数异常(非关键): {e}")
                     print(f"[{self.platform_name}] 获取粉丝数异常(非关键): {e}")
                 
                 
-                # 步骤 3: 获取作品数量(从作品列表第一页
+                # 步骤 3: 获取作品数量(使用与 Node 端一致的 API
                 works_count = 0
                 works_count = 0
                 try:
                 try:
                     print(f"[{self.platform_name}] [3/3] 调用 article/lists API 获取作品数...")
                     print(f"[{self.platform_name}] [3/3] 调用 article/lists API 获取作品数...")
+                    
+                    # 使用与 Node 端一致的 API 参数
+                    list_url = 'https://baijiahao.baidu.com/pcui/article/lists?currentPage=1&pageSize=20&search=&type=&collection=&startDate=&endDate=&clearBeforeFetch=false&dynamic=0'
+                    
                     async with session.get(
                     async with session.get(
-                        'https://baijiahao.baidu.com/pcui/article/lists?start=0&count=1&article_type=video',
-                        headers=headers,
-                        timeout=aiohttp.ClientTimeout(total=10)
+                        list_url,
+                        headers={
+                            'accept': '*/*',
+                            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+                            'cookie': cookie_str,
+                            'referer': 'https://baijiahao.baidu.com/builder/rc/content',
+                            'connection': 'keep-alive',
+                            'accept-encoding': 'gzip, deflate, br',
+                        },
+                        timeout=aiohttp.ClientTimeout(total=30)
                     ) as response:
                     ) as response:
-                        works_result = await response.json()
+                        response_text = await response.text()
+                        print(f"[{self.platform_name}] ========== Works API Response ==========")
+                        print(f"[{self.platform_name}] Full response: {response_text[:1000]}...")  # 只打印前1000字符
+                        print(f"[{self.platform_name}] =========================================")
+                        
+                        works_result = json.loads(response_text)
+                    
+                    # 处理分散认证问题 (errno=10001402),重试一次
+                    if works_result.get('errno') == 10001402:
+                        print(f"[{self.platform_name}] 分散认证问题 (errno=10001402),3秒后重试...")
+                        await asyncio.sleep(3)
+                        
+                        # 重试一次
+                        async with session.get(
+                            list_url,
+                            headers=headers,
+                            timeout=aiohttp.ClientTimeout(total=30)
+                        ) as retry_response:
+                            retry_text = await retry_response.text()
+                            print(f"[{self.platform_name}] ========== Works API Retry Response ==========")
+                            print(f"[{self.platform_name}] Full retry response: {retry_text[:1000]}...")
+                            print(f"[{self.platform_name}] ===============================================")
+                            
+                            works_result = json.loads(retry_text)
+                            
+                            if works_result.get('errno') == 10001402:
+                                print(f"[{self.platform_name}] 重试仍然失败,返回已获取的账号信息")
+                                works_result = None
                     
                     
-                    if works_result.get('errno') == 0:
+                    if works_result and works_result.get('errno') == 0:
                         works_data = works_result.get('data', {})
                         works_data = works_result.get('data', {})
-                        works_count = int(works_data.get('total', 0))
-                        print(f"[{self.platform_name}] 作品数: {works_count}")
+                        # 优先使用 data.page.totalCount,如果没有则使用 data.total(兼容旧格式)
+                        page_info = works_data.get('page', {})
+                        works_count = int(page_info.get('totalCount', works_data.get('total', 0)))
+                        print(f"[{self.platform_name}] 作品数: {works_count} (from page.totalCount: {page_info.get('totalCount')}, from total: {works_data.get('total')})")
                     else:
                     else:
-                        print(f"[{self.platform_name}] 获取作品数失败: {works_result.get('errmsg')}")
+                        errno = works_result.get('errno') if works_result else 'unknown'
+                        errmsg = works_result.get('errmsg', 'unknown error') if works_result else 'no response'
+                        print(f"[{self.platform_name}] 获取作品数失败: errno={errno}, errmsg={errmsg}")
                 except Exception as e:
                 except Exception as e:
+                    import traceback
                     print(f"[{self.platform_name}] 获取作品数异常(非关键): {e}")
                     print(f"[{self.platform_name}] 获取作品数异常(非关键): {e}")
+                    traceback.print_exc()
                 
                 
                 # 返回账号信息
                 # 返回账号信息
                 account_info = {
                 account_info = {

+ 223 - 97
server/src/services/HeadlessBrowserService.ts

@@ -36,37 +36,37 @@ const PLATFORM_API_CONFIG: Record<string, {
     // 使用 appinfo 接口检查 Cookie 有效性
     // 使用 appinfo 接口检查 Cookie 有效性
     checkUrl: 'https://baijiahao.baidu.com/builder/app/appinfo',
     checkUrl: 'https://baijiahao.baidu.com/builder/app/appinfo',
     isValidResponse: (data: unknown) => {
     isValidResponse: (data: unknown) => {
-      const resp = data as { 
-        errno?: number; 
+      const resp = data as {
+        errno?: number;
         errmsg?: string;
         errmsg?: string;
-        data?: { 
-          user?: { 
-            name?: string; 
+        data?: {
+          user?: {
+            name?: string;
             app_id?: string | number;
             app_id?: string | number;
             userid?: number;
             userid?: number;
             status?: string;
             status?: string;
-          } 
-        } 
+          }
+        }
       };
       };
-      
+
       logger.info(`[Baijiahao] API response: errno=${resp?.errno}, errmsg=${resp?.errmsg}, user.name=${resp?.data?.user?.name}, user.app_id=${resp?.data?.user?.app_id}, user.status=${resp?.data?.user?.status}`);
       logger.info(`[Baijiahao] API response: errno=${resp?.errno}, errmsg=${resp?.errmsg}, user.name=${resp?.data?.user?.name}, user.app_id=${resp?.data?.user?.app_id}, user.status=${resp?.data?.user?.status}`);
-      
+
       // errno 为 0 表示请求成功
       // errno 为 0 表示请求成功
       const isErrnoOk = resp?.errno === 0;
       const isErrnoOk = resp?.errno === 0;
-      
+
       // 必须有用户信息(name 或 app_id)
       // 必须有用户信息(name 或 app_id)
       const hasUserInfo = !!(resp?.data?.user?.name || resp?.data?.user?.app_id);
       const hasUserInfo = !!(resp?.data?.user?.name || resp?.data?.user?.app_id);
-      
+
       // 用户状态不能是 'banned' 或其他异常状态
       // 用户状态不能是 'banned' 或其他异常状态
       const userStatus = resp?.data?.user?.status;
       const userStatus = resp?.data?.user?.status;
       const isStatusOk = !userStatus || userStatus === 'audit' || userStatus === 'pass' || userStatus === 'active';
       const isStatusOk = !userStatus || userStatus === 'audit' || userStatus === 'pass' || userStatus === 'active';
-      
+
       const isValid = isErrnoOk && hasUserInfo && isStatusOk;
       const isValid = isErrnoOk && hasUserInfo && isStatusOk;
-      
+
       if (!isValid) {
       if (!isValid) {
         logger.warn(`[Baijiahao] Cookie invalid: errno=${resp?.errno}, hasUserInfo=${hasUserInfo}, status=${userStatus}`);
         logger.warn(`[Baijiahao] Cookie invalid: errno=${resp?.errno}, hasUserInfo=${hasUserInfo}, status=${userStatus}`);
       }
       }
-      
+
       return isValid;
       return isValid;
     },
     },
   },
   },
@@ -132,7 +132,7 @@ class HeadlessBrowserService {
    */
    */
   async checkCookieValid(platform: PlatformType, cookies: CookieData[]): Promise<boolean> {
   async checkCookieValid(platform: PlatformType, cookies: CookieData[]): Promise<boolean> {
     logger.info(`[checkCookieValid] Checking cookie for ${platform}, cookie count: ${cookies.length}`);
     logger.info(`[checkCookieValid] Checking cookie for ${platform}, cookie count: ${cookies.length}`);
-    
+
     // 优先使用 Python 服务检查(通过浏览器访问后台页面,检测是否被重定向到登录页)
     // 优先使用 Python 服务检查(通过浏览器访问后台页面,检测是否被重定向到登录页)
     const pythonAvailable = await this.checkPythonServiceAvailable();
     const pythonAvailable = await this.checkPythonServiceAvailable();
     if (pythonAvailable) {
     if (pythonAvailable) {
@@ -241,10 +241,10 @@ class HeadlessBrowserService {
 
 
       const data = await response.json();
       const data = await response.json();
       logger.info(`[API] Raw response for ${platform}:`, JSON.stringify(data).substring(0, 500));
       logger.info(`[API] Raw response for ${platform}:`, JSON.stringify(data).substring(0, 500));
-      
+
       const isValid = apiConfig.isValidResponse(data);
       const isValid = apiConfig.isValidResponse(data);
-      const statusCode = (data as { status_code?: number; errno?: number; ret?: { errno?: number } })?.status_code 
-        ?? (data as { errno?: number })?.errno 
+      const statusCode = (data as { status_code?: number; errno?: number; ret?: { errno?: number } })?.status_code
+        ?? (data as { errno?: number })?.errno
         ?? (data as { ret?: { errno?: number } })?.ret?.errno;
         ?? (data as { ret?: { errno?: number } })?.ret?.errno;
 
 
       logger.info(`API check cookie for ${platform}: valid=${isValid}, statusCode=${statusCode}`);
       logger.info(`API check cookie for ${platform}: valid=${isValid}, statusCode=${statusCode}`);
@@ -267,7 +267,7 @@ class HeadlessBrowserService {
       // 百家号特殊处理:根据 errno 判断
       // 百家号特殊处理:根据 errno 判断
       if (platform === 'baijiahao') {
       if (platform === 'baijiahao') {
         const errno = (data as { errno?: number })?.errno;
         const errno = (data as { errno?: number })?.errno;
-        
+
         // errno 为 0 表示请求成功,但可能没有用户信息(已在 isValidResponse 中检查)
         // errno 为 0 表示请求成功,但可能没有用户信息(已在 isValidResponse 中检查)
         if (errno === 0) {
         if (errno === 0) {
           // 如果 isValid 为 false,说明虽然请求成功但没有用户信息,可能是 Cookie 无效
           // 如果 isValid 为 false,说明虽然请求成功但没有用户信息,可能是 Cookie 无效
@@ -277,7 +277,7 @@ class HeadlessBrowserService {
           }
           }
           return true;
           return true;
         }
         }
-        
+
         // errno 非 0 表示请求失败,可能是 Cookie 无效
         // errno 非 0 表示请求失败,可能是 Cookie 无效
         // 常见错误码:
         // 常见错误码:
         // - 110: 未登录
         // - 110: 未登录
@@ -441,7 +441,7 @@ class HeadlessBrowserService {
    */
    */
   async capturePageScreenshot(platform: PlatformType, cookies: CookieData[]): Promise<string | null> {
   async capturePageScreenshot(platform: PlatformType, cookies: CookieData[]): Promise<string | null> {
     const browser = await chromium.launch({ headless: true });
     const browser = await chromium.launch({ headless: true });
-    
+
     try {
     try {
       const context = await browser.newContext({
       const context = await browser.newContext({
         viewport: { width: 1920, height: 1080 },
         viewport: { width: 1920, height: 1080 },
@@ -449,41 +449,41 @@ class HeadlessBrowserService {
         timezoneId: 'Asia/Shanghai',
         timezoneId: 'Asia/Shanghai',
         userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
         userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
       });
       });
-      
+
       await context.addCookies(cookies);
       await context.addCookies(cookies);
       const page = await context.newPage();
       const page = await context.newPage();
-      
+
       const config = this.getPlatformConfig(platform);
       const config = this.getPlatformConfig(platform);
-      
+
       logger.info(`[Screenshot] Navigating to ${platform} home page: ${config.homeUrl}`);
       logger.info(`[Screenshot] Navigating to ${platform} home page: ${config.homeUrl}`);
-      
+
       // 访问平台主页
       // 访问平台主页
       await page.goto(config.homeUrl, {
       await page.goto(config.homeUrl, {
         waitUntil: 'domcontentloaded',
         waitUntil: 'domcontentloaded',
         timeout: 30000,
         timeout: 30000,
       });
       });
-      
+
       // 等待页面加载
       // 等待页面加载
       await page.waitForTimeout(3000);
       await page.waitForTimeout(3000);
-      
+
       const url = page.url();
       const url = page.url();
       logger.info(`[Screenshot] Current URL: ${url}`);
       logger.info(`[Screenshot] Current URL: ${url}`);
-      
+
       // 截图
       // 截图
       const screenshotBuffer = await page.screenshot({
       const screenshotBuffer = await page.screenshot({
         type: 'jpeg',
         type: 'jpeg',
         quality: 80,
         quality: 80,
         fullPage: false,
         fullPage: false,
       });
       });
-      
+
       const base64Screenshot = screenshotBuffer.toString('base64');
       const base64Screenshot = screenshotBuffer.toString('base64');
-      
+
       await page.close();
       await page.close();
       await context.close();
       await context.close();
       await browser.close();
       await browser.close();
-      
+
       logger.info(`[Screenshot] Captured screenshot for ${platform}, size: ${Math.round(base64Screenshot.length / 1024)}KB`);
       logger.info(`[Screenshot] Captured screenshot for ${platform}, size: ${Math.round(base64Screenshot.length / 1024)}KB`);
-      
+
       return base64Screenshot;
       return base64Screenshot;
     } catch (error) {
     } catch (error) {
       logger.error(`[Screenshot] Failed to capture screenshot for ${platform}:`, error);
       logger.error(`[Screenshot] Failed to capture screenshot for ${platform}:`, error);
@@ -578,15 +578,10 @@ class HeadlessBrowserService {
    */
    */
   async fetchAccountInfo(platform: PlatformType, cookies: CookieData[]): Promise<AccountInfo> {
   async fetchAccountInfo(platform: PlatformType, cookies: CookieData[]): Promise<AccountInfo> {
     logger.info(`[fetchAccountInfo] Starting for platform: ${platform}`);
     logger.info(`[fetchAccountInfo] Starting for platform: ${platform}`);
-    
-    // 百家号使用直接 API 获取账号信息和作品列表
-    if (platform === 'baijiahao') {
-      logger.info(`[fetchAccountInfo] Using direct API for baijiahao`);
-      return this.fetchBaijiahaoAccountInfoDirectApi(cookies);
-    }
 
 
     // 对于支持的平台,尝试使用 Python API 获取作品列表和账号信息
     // 对于支持的平台,尝试使用 Python API 获取作品列表和账号信息
-    const supportedPlatforms: PlatformType[] = ['douyin', 'xiaohongshu', 'kuaishou', 'weixin_video'];
+    // 包括百家号,通过 Python API 执行,逻辑与 Node 服务端保持一致
+    const supportedPlatforms: PlatformType[] = ['douyin', 'xiaohongshu', 'kuaishou', 'weixin_video', 'baijiahao'];
 
 
     if (supportedPlatforms.includes(platform)) {
     if (supportedPlatforms.includes(platform)) {
       const pythonAvailable = await this.checkPythonServiceAvailable();
       const pythonAvailable = await this.checkPythonServiceAvailable();
@@ -1256,7 +1251,7 @@ class HeadlessBrowserService {
               }
               }
             }
             }
           }
           }
-          
+
           // 方法2: 通过 .finder-uniq-id 选择器获取
           // 方法2: 通过 .finder-uniq-id 选择器获取
           if (!result.finderId) {
           if (!result.finderId) {
             const finderUniqIdEl = document.querySelector('.finder-uniq-id');
             const finderUniqIdEl = document.querySelector('.finder-uniq-id');
@@ -1274,7 +1269,7 @@ class HeadlessBrowserService {
               }
               }
             }
             }
           }
           }
-          
+
           // 方法3: 从页面文本中正则匹配
           // 方法3: 从页面文本中正则匹配
           if (!result.finderId) {
           if (!result.finderId) {
             const bodyText = document.body.innerText || '';
             const bodyText = document.body.innerText || '';
@@ -1291,11 +1286,11 @@ class HeadlessBrowserService {
               }
               }
             }
             }
           }
           }
-          
+
           // ===== 2. 获取账号名称 =====
           // ===== 2. 获取账号名称 =====
           // 优先使用 h2.finder-nickname
           // 优先使用 h2.finder-nickname
-          const nicknameEl = document.querySelector('h2.finder-nickname') || 
-                            document.querySelector('.finder-nickname');
+          const nicknameEl = document.querySelector('h2.finder-nickname') ||
+            document.querySelector('.finder-nickname');
           if (nicknameEl) {
           if (nicknameEl) {
             const text = nicknameEl.textContent?.trim();
             const text = nicknameEl.textContent?.trim();
             if (text && text.length >= 2 && text.length <= 30) {
             if (text && text.length >= 2 && text.length <= 30) {
@@ -1303,7 +1298,7 @@ class HeadlessBrowserService {
               console.log('[WeixinVideo] Found name from .finder-nickname:', result.name);
               console.log('[WeixinVideo] Found name from .finder-nickname:', result.name);
             }
             }
           }
           }
-          
+
           // 备选选择器
           // 备选选择器
           if (!result.name) {
           if (!result.name) {
             const nameSelectors = [
             const nameSelectors = [
@@ -1329,7 +1324,7 @@ class HeadlessBrowserService {
             result.avatar = avatarEl.src;
             result.avatar = avatarEl.src;
             console.log('[WeixinVideo] Found avatar from img.avatar:', result.avatar);
             console.log('[WeixinVideo] Found avatar from img.avatar:', result.avatar);
           }
           }
-          
+
           // 备选选择器
           // 备选选择器
           if (!result.avatar) {
           if (!result.avatar) {
             const avatarSelectors = [
             const avatarSelectors = [
@@ -1368,14 +1363,14 @@ class HeadlessBrowserService {
               }
               }
             });
             });
           }
           }
-          
+
           // 备选:从页面整体文本中匹配
           // 备选:从页面整体文本中匹配
           if (result.fans === undefined || result.works === undefined) {
           if (result.fans === undefined || result.works === undefined) {
             const bodyText = document.body.innerText || '';
             const bodyText = document.body.innerText || '';
-            
+
             if (result.fans === undefined) {
             if (result.fans === undefined) {
               const fansMatch = bodyText.match(/关注者\s*(\d+(?:\.\d+)?[万wW]?)/) ||
               const fansMatch = bodyText.match(/关注者\s*(\d+(?:\.\d+)?[万wW]?)/) ||
-                               bodyText.match(/粉丝\s*(\d+(?:\.\d+)?[万wW]?)/);
+                bodyText.match(/粉丝\s*(\d+(?:\.\d+)?[万wW]?)/);
               if (fansMatch) {
               if (fansMatch) {
                 let count = parseFloat(fansMatch[1]);
                 let count = parseFloat(fansMatch[1]);
                 if (fansMatch[1].includes('万') || fansMatch[1].toLowerCase().includes('w')) {
                 if (fansMatch[1].includes('万') || fansMatch[1].toLowerCase().includes('w')) {
@@ -1385,10 +1380,10 @@ class HeadlessBrowserService {
                 console.log('[WeixinVideo] Found fans from text:', result.fans);
                 console.log('[WeixinVideo] Found fans from text:', result.fans);
               }
               }
             }
             }
-            
+
             if (result.works === undefined) {
             if (result.works === undefined) {
               const worksMatch = bodyText.match(/视频\s*(\d+)/) ||
               const worksMatch = bodyText.match(/视频\s*(\d+)/) ||
-                                bodyText.match(/作品\s*(\d+)/);
+                bodyText.match(/作品\s*(\d+)/);
               if (worksMatch) {
               if (worksMatch) {
                 result.works = parseInt(worksMatch[1], 10);
                 result.works = parseInt(worksMatch[1], 10);
                 console.log('[WeixinVideo] Found works from text:', result.works);
                 console.log('[WeixinVideo] Found works from text:', result.works);
@@ -1426,7 +1421,7 @@ class HeadlessBrowserService {
       // 如果首页没有获取到视频号 ID,尝试访问账号设置页面
       // 如果首页没有获取到视频号 ID,尝试访问账号设置页面
       if (!finderId || finderId.length < 10) {
       if (!finderId || finderId.length < 10) {
         logger.info('[WeixinVideo] Finder ID not found on home page, trying account settings page...');
         logger.info('[WeixinVideo] Finder ID not found on home page, trying account settings page...');
-        
+
         try {
         try {
           // 访问账号设置页面
           // 访问账号设置页面
           await page.goto('https://channels.weixin.qq.com/platform/account', {
           await page.goto('https://channels.weixin.qq.com/platform/account', {
@@ -1434,12 +1429,12 @@ class HeadlessBrowserService {
             timeout: 30000,
             timeout: 30000,
           });
           });
           await page.waitForTimeout(2000);
           await page.waitForTimeout(2000);
-          
+
           // 从账号设置页面提取视频号 ID
           // 从账号设置页面提取视频号 ID
           const settingsData = await page.evaluate(() => {
           const settingsData = await page.evaluate(() => {
             const result: { finderId?: string; name?: string } = {};
             const result: { finderId?: string; name?: string } = {};
             const bodyText = document.body.innerText || '';
             const bodyText = document.body.innerText || '';
-            
+
             // 尝试多种匹配模式
             // 尝试多种匹配模式
             const patterns = [
             const patterns = [
               /视频号ID[::\s]*([a-zA-Z0-9_]+)/,
               /视频号ID[::\s]*([a-zA-Z0-9_]+)/,
@@ -1449,7 +1444,7 @@ class HeadlessBrowserService {
               /finder_username[::\s]*([a-zA-Z0-9_]+)/i,
               /finder_username[::\s]*([a-zA-Z0-9_]+)/i,
               /唯一标识[::\s]*([a-zA-Z0-9_]+)/,
               /唯一标识[::\s]*([a-zA-Z0-9_]+)/,
             ];
             ];
-            
+
             for (const pattern of patterns) {
             for (const pattern of patterns) {
               const match = bodyText.match(pattern);
               const match = bodyText.match(pattern);
               if (match && match[1]) {
               if (match && match[1]) {
@@ -1458,7 +1453,7 @@ class HeadlessBrowserService {
                 break;
                 break;
               }
               }
             }
             }
-            
+
             // 从元素中查找
             // 从元素中查找
             if (!result.finderId) {
             if (!result.finderId) {
               const idSelectors = [
               const idSelectors = [
@@ -1479,12 +1474,12 @@ class HeadlessBrowserService {
                 }
                 }
               }
               }
             }
             }
-            
+
             return result;
             return result;
           });
           });
-          
+
           logger.info(`[WeixinVideo] Extracted data from settings page:`, settingsData);
           logger.info(`[WeixinVideo] Extracted data from settings page:`, settingsData);
-          
+
           if (settingsData.finderId) {
           if (settingsData.finderId) {
             finderId = settingsData.finderId;
             finderId = settingsData.finderId;
             accountId = `weixin_video_${settingsData.finderId}`;
             accountId = `weixin_video_${settingsData.finderId}`;
@@ -1696,7 +1691,7 @@ class HeadlessBrowserService {
         const bodyText = await page.textContent('body');
         const bodyText = await page.textContent('body');
         // 匹配小红书号格式:小红书号:xxxxxxx
         // 匹配小红书号格式:小红书号:xxxxxxx
         const xhsIdMatch = bodyText?.match(/小红书号[::]\s*([a-zA-Z0-9_]+)/) ||
         const xhsIdMatch = bodyText?.match(/小红书号[::]\s*([a-zA-Z0-9_]+)/) ||
-                          bodyText?.match(/红书号[::]\s*([a-zA-Z0-9_]+)/);
+          bodyText?.match(/红书号[::]\s*([a-zA-Z0-9_]+)/);
         if (xhsIdMatch) {
         if (xhsIdMatch) {
           accountId = `xiaohongshu_${xhsIdMatch[1]}`;
           accountId = `xiaohongshu_${xhsIdMatch[1]}`;
           logger.info(`[Xiaohongshu] Found 小红书号 from page text: ${accountId}`);
           logger.info(`[Xiaohongshu] Found 小红书号 from page text: ${accountId}`);
@@ -2085,17 +2080,33 @@ class HeadlessBrowserService {
    */
    */
   private async fetchBaijiahaoAccountInfoDirectApi(cookies: CookieData[]): Promise<AccountInfo> {
   private async fetchBaijiahaoAccountInfoDirectApi(cookies: CookieData[]): Promise<AccountInfo> {
     logger.info(`[Baijiahao API] Fetching account info via direct API...`);
     logger.info(`[Baijiahao API] Fetching account info via direct API...`);
-    
-    const cookieString = cookies.map(c => `${c.name}=${c.value}`).join('; ');
-    const headers = {
-      'Accept': 'application/json, text/plain, */*',
+
+    // 构建 Cookie 字符串,确保格式正确
+    const cookieString = cookies
+      .map(c => `${c.name.trim()}=${c.value.trim()}`)
+      .filter(c => c.includes('=')) // 过滤掉无效的 cookie
+      .join('; ');
+
+    logger.debug(`[Baijiahao API] Cookie string length: ${cookieString.length}, cookie count: ${cookies.length}`);
+
+    const headers: Record<string, string> = {
+      'Accept': '*/*',
       'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
       'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
+      'Accept-Encoding': 'gzip, deflate, br',
+      'Connection': 'keep-alive',
       'Cookie': cookieString,
       'Cookie': cookieString,
-      'Referer': 'https://baijiahao.baidu.com/builder/rc/home',
+      // 'Referer': 'https://baijiahao.baidu.com/builder/rc/home',
+      // 'Origin': 'https://baijiahao.baidu.com',
       'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
       'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+      // 'Sec-Fetch-Dest': 'empty',
+      // 'Sec-Fetch-Mode': 'cors',
+      // 'Sec-Fetch-Site': 'same-origin',
     };
     };
 
 
     let accountInfo: AccountInfo = this.getDefaultAccountInfo('baijiahao');
     let accountInfo: AccountInfo = this.getDefaultAccountInfo('baijiahao');
+    // 标记哪些字段已成功获取
+    let fansCountFetched = false;
+    let worksCountFetched = false;
 
 
     try {
     try {
       // 1. 获取账号基本信息 (appinfo API)
       // 1. 获取账号基本信息 (appinfo API)
@@ -2104,12 +2115,12 @@ class HeadlessBrowserService {
         method: 'GET',
         method: 'GET',
         headers,
         headers,
       });
       });
-      
+
       if (!appInfoResponse.ok) {
       if (!appInfoResponse.ok) {
         logger.error(`[Baijiahao API] appinfo request failed: ${appInfoResponse.status}`);
         logger.error(`[Baijiahao API] appinfo request failed: ${appInfoResponse.status}`);
         throw new Error(`appinfo request failed: ${appInfoResponse.status}`);
         throw new Error(`appinfo request failed: ${appInfoResponse.status}`);
       }
       }
-      
+
       const appInfoData = await appInfoResponse.json() as {
       const appInfoData = await appInfoResponse.json() as {
         errno?: number;
         errno?: number;
         errmsg?: string;
         errmsg?: string;
@@ -2123,26 +2134,26 @@ class HeadlessBrowserService {
           };
           };
         };
         };
       };
       };
-      
+
       logger.info(`[Baijiahao API] appinfo response: errno=${appInfoData.errno}, errmsg=${appInfoData.errmsg}`);
       logger.info(`[Baijiahao API] appinfo response: errno=${appInfoData.errno}, errmsg=${appInfoData.errmsg}`);
-      
+
       if (appInfoData.errno !== 0) {
       if (appInfoData.errno !== 0) {
         logger.error(`[Baijiahao API] appinfo API error: errno=${appInfoData.errno}, errmsg=${appInfoData.errmsg}`);
         logger.error(`[Baijiahao API] appinfo API error: errno=${appInfoData.errno}, errmsg=${appInfoData.errmsg}`);
         throw new Error(`appinfo API error: ${appInfoData.errmsg || 'Unknown error'}`);
         throw new Error(`appinfo API error: ${appInfoData.errmsg || 'Unknown error'}`);
       }
       }
-      
+
       if (!appInfoData.data?.user) {
       if (!appInfoData.data?.user) {
         logger.error(`[Baijiahao API] No user data in appinfo response`);
         logger.error(`[Baijiahao API] No user data in appinfo response`);
         throw new Error('No user data in appinfo response');
         throw new Error('No user data in appinfo response');
       }
       }
-      
+
       const user = appInfoData.data.user;
       const user = appInfoData.data.user;
       accountInfo.accountId = user.app_id ? `bjh_${user.app_id}` : accountInfo.accountId;
       accountInfo.accountId = user.app_id ? `bjh_${user.app_id}` : accountInfo.accountId;
       accountInfo.accountName = user.name || accountInfo.accountName;
       accountInfo.accountName = user.name || accountInfo.accountName;
       // 处理头像 URL(可能是相对路径)
       // 处理头像 URL(可能是相对路径)
       if (user.avatar) {
       if (user.avatar) {
-        accountInfo.avatarUrl = user.avatar.startsWith('http') 
-          ? user.avatar 
+        accountInfo.avatarUrl = user.avatar.startsWith('http')
+          ? user.avatar
           : `https:${user.avatar}`;
           : `https:${user.avatar}`;
       }
       }
       logger.info(`[Baijiahao API] Got account info: name=${accountInfo.accountName}, id=${accountInfo.accountId}, avatar=${accountInfo.avatarUrl}`);
       logger.info(`[Baijiahao API] Got account info: name=${accountInfo.accountName}, id=${accountInfo.accountId}, avatar=${accountInfo.avatarUrl}`);
@@ -2154,7 +2165,7 @@ class HeadlessBrowserService {
           method: 'GET',
           method: 'GET',
           headers,
           headers,
         });
         });
-        
+
         if (growthInfoResponse.ok) {
         if (growthInfoResponse.ok) {
           const growthData = await growthInfoResponse.json() as {
           const growthData = await growthInfoResponse.json() as {
             errno?: number;
             errno?: number;
@@ -2163,12 +2174,18 @@ class HeadlessBrowserService {
               total_fans?: number;
               total_fans?: number;
             };
             };
           };
           };
-          
+
           logger.info(`[Baijiahao API] growth info response: errno=${growthData.errno}`);
           logger.info(`[Baijiahao API] growth info response: errno=${growthData.errno}`);
-          
+
           if (growthData.errno === 0 && growthData.data) {
           if (growthData.errno === 0 && growthData.data) {
-            accountInfo.fansCount = growthData.data.total_fans || 0;
-            logger.info(`[Baijiahao API] Got fans count: ${accountInfo.fansCount}`);
+            const fansCount = growthData.data.total_fans;
+            if (fansCount !== undefined && fansCount !== null) {
+              accountInfo.fansCount = fansCount;
+              fansCountFetched = true;
+              logger.info(`[Baijiahao API] Got fans count: ${accountInfo.fansCount}`);
+            } else {
+              logger.warn(`[Baijiahao API] growth info API returned no fans count`);
+            }
           } else {
           } else {
             logger.warn(`[Baijiahao API] growth info API error: errno=${growthData.errno}, errmsg=${growthData.errmsg}`);
             logger.warn(`[Baijiahao API] growth info API error: errno=${growthData.errno}, errmsg=${growthData.errmsg}`);
           }
           }
@@ -2182,68 +2199,162 @@ class HeadlessBrowserService {
 
 
       // 3. 获取作品列表 (分页获取所有作品)
       // 3. 获取作品列表 (分页获取所有作品)
       logger.info(`[Baijiahao API] Step 3: Fetching works list...`);
       logger.info(`[Baijiahao API] Step 3: Fetching works list...`);
+      setTimeout(() => {
+        console.log('1000ms');
+      }, 1000);
       const worksList: WorkItem[] = [];
       const worksList: WorkItem[] = [];
       let currentPage = 1;
       let currentPage = 1;
       const pageSize = 20;
       const pageSize = 20;
       let hasMore = true;
       let hasMore = true;
       let totalWorks = 0;
       let totalWorks = 0;
+      let worksListError = false;
 
 
-      while (hasMore) {
+      while (hasMore && !worksListError) {
         try {
         try {
           const listUrl = `https://baijiahao.baidu.com/pcui/article/lists?currentPage=${currentPage}&pageSize=${pageSize}&search=&type=&collection=&startDate=&endDate=&clearBeforeFetch=false&dynamic=0`;
           const listUrl = `https://baijiahao.baidu.com/pcui/article/lists?currentPage=${currentPage}&pageSize=${pageSize}&search=&type=&collection=&startDate=&endDate=&clearBeforeFetch=false&dynamic=0`;
-          
+
           logger.info(`[Baijiahao API] Fetching works page ${currentPage}...`);
           logger.info(`[Baijiahao API] Fetching works page ${currentPage}...`);
-          
+          logger.debug(`[Baijiahao API] Request headers include Cookie: ${!!headers.Cookie}, Cookie length: ${headers.Cookie?.length || 0}`);
+
           const listResponse = await fetch(listUrl, {
           const listResponse = await fetch(listUrl, {
             method: 'GET',
             method: 'GET',
             headers,
             headers,
           });
           });
 
 
           if (!listResponse.ok) {
           if (!listResponse.ok) {
+            const errorText = await listResponse.text();
             logger.warn(`[Baijiahao API] Failed to fetch works list page ${currentPage}: ${listResponse.status}`);
             logger.warn(`[Baijiahao API] Failed to fetch works list page ${currentPage}: ${listResponse.status}`);
+            logger.warn(`[Baijiahao API] Error response body: ${errorText}`);
             break;
             break;
           }
           }
 
 
-          const listData = await listResponse.json() as {
+          const responseText = await listResponse.text();
+          logger.info(`[Baijiahao API] ========== Works API Response (Page ${currentPage}) ==========`);
+          logger.info(`[Baijiahao API] Full response: ${responseText}`);
+          logger.info(`[Baijiahao API] ============================================================`);
+
+          const listData = JSON.parse(responseText) as {
             errno?: number;
             errno?: number;
             errmsg?: string;
             errmsg?: string;
             data?: {
             data?: {
               list?: Array<{
               list?: Array<{
                 id?: string;
                 id?: string;
+                article_id?: string;
                 title?: string;
                 title?: string;
-                cover_images?: string[];
+                cover_images?: string | string[];
+                created_at?: string;
                 create_time?: string;
                 create_time?: string;
                 status?: string;
                 status?: string;
+                read_amount?: number;
                 read_count?: number;
                 read_count?: number;
+                like_amount?: number;
                 like_count?: number;
                 like_count?: number;
+                comment_amount?: number;
                 comment_count?: number;
                 comment_count?: number;
+                share_amount?: number;
                 share_count?: number;
                 share_count?: number;
               }>;
               }>;
-              total?: number;
+              page?: {
+                currentPage?: number;
+                pageSize?: number;
+                totalCount?: number;
+                totalPage?: number;
+              };
+              total?: number; // 兼容旧格式
             };
             };
           };
           };
 
 
+          // 处理分散认证问题 (errno=10001402),重试一次
+          if (listData.errno === 10001402) {
+            logger.warn(`[Baijiahao API] Dispersed authentication issue (errno=10001402) on page ${currentPage}, retrying after 3 seconds...`);
+            logger.debug(`[Baijiahao API] Request URL: ${listUrl}`);
+            logger.debug(`[Baijiahao API] Cookie header present: ${!!headers.Cookie}, length: ${headers.Cookie?.length || 0}`);
+
+            await new Promise(resolve => setTimeout(resolve, 3000));
+
+            // 重试一次,确保 headers 包含 Cookie
+            const retryResponse = await fetch(listUrl, {
+              method: 'GET',
+              headers: {
+                ...headers,
+                'Cookie': cookieString, // 确保 Cookie 被正确传递
+              },
+            });
+
+            if (retryResponse.ok) {
+              const retryResponseText = await retryResponse.text();
+              logger.info(`[Baijiahao API] ========== Works API Retry Response (Page ${currentPage}) ==========`);
+              logger.info(`[Baijiahao API] Full retry response: ${retryResponseText}`);
+              logger.info(`[Baijiahao API] ============================================================`);
+
+              const retryData = JSON.parse(retryResponseText) as typeof listData;
+              if (retryData.errno === 0) {
+                logger.info(`[Baijiahao API] Retry successful for page ${currentPage}`);
+                Object.assign(listData, retryData);
+              } else if (retryData.errno === 10001402) {
+                logger.error(`[Baijiahao API] Retry still failed with errno=10001402, cookie may be invalid or expired`);
+                logger.error(`[Baijiahao API] Retry response data: ${JSON.stringify(retryData, null, 2)}`);
+                // 如果重试仍然失败,可能是 Cookie 问题,记录详细信息
+                logger.error(`[Baijiahao API] Cookie info: ${cookieString.substring(0, 200)}...`);
+                // 标记错误,但不完全失败,继续返回已获取的账号信息
+                worksListError = true;
+                logger.warn(`[Baijiahao API] Works list fetch failed, but will return other account info (name, fans count)`);
+                break;
+              } else {
+                logger.warn(`[Baijiahao API] Retry still failed: errno=${retryData.errno}, errmsg=${retryData.errmsg}`);
+                break;
+              }
+            } else {
+              logger.warn(`[Baijiahao API] Retry request failed: ${retryResponse.status}`);
+              break;
+            }
+          }
+
           if (listData.errno !== 0) {
           if (listData.errno !== 0) {
             logger.warn(`[Baijiahao API] API returned error on page ${currentPage}: errno=${listData.errno}, errmsg=${listData.errmsg}`);
             logger.warn(`[Baijiahao API] API returned error on page ${currentPage}: errno=${listData.errno}, errmsg=${listData.errmsg}`);
+            logger.warn(`[Baijiahao API] Error response data: ${JSON.stringify(listData, null, 2)}`);
+            // 如果不是 10001402 错误,标记为错误但继续返回其他信息
+            if (listData.errno !== 10001402) {
+              worksListError = true;
+              logger.warn(`[Baijiahao API] Works list fetch failed with errno=${listData.errno}, but will return other account info`);
+            }
             break;
             break;
           }
           }
 
 
           const list = listData.data?.list || [];
           const list = listData.data?.list || [];
-          totalWorks = listData.data?.total || 0;
+          // 优先使用 data.page.totalCount,如果没有则使用 data.total(兼容旧格式)
+          totalWorks = listData.data?.page?.totalCount || listData.data?.total || 0;
           logger.info(`[Baijiahao API] Got ${list.length} works on page ${currentPage}, total: ${totalWorks}`);
           logger.info(`[Baijiahao API] Got ${list.length} works on page ${currentPage}, total: ${totalWorks}`);
 
 
           for (const item of list) {
           for (const item of list) {
+            // 处理 cover_images 可能是字符串(JSON)或数组
+            let coverUrl = '';
+            if (item.cover_images) {
+              if (Array.isArray(item.cover_images)) {
+                coverUrl = item.cover_images[0] || '';
+              } else if (typeof item.cover_images === 'string') {
+                try {
+                  const coverArray = JSON.parse(item.cover_images);
+                  if (Array.isArray(coverArray) && coverArray.length > 0) {
+                    coverUrl = typeof coverArray[0] === 'string' ? coverArray[0] : coverArray[0]?.src || coverArray[0]?.ori_src || '';
+                  }
+                } catch {
+                  coverUrl = item.cover_images;
+                }
+              }
+            }
+
             worksList.push({
             worksList.push({
-              videoId: item.id || `bjh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
+              videoId: item.id || item.article_id || `bjh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
               title: item.title || '',
               title: item.title || '',
-              coverUrl: item.cover_images?.[0] || '',
+              coverUrl: coverUrl,
               duration: '00:00',
               duration: '00:00',
-              publishTime: item.create_time || new Date().toISOString(),
+              publishTime: item.created_at || item.create_time || new Date().toISOString(),
               status: item.status || 'published',
               status: item.status || 'published',
-              playCount: item.read_count || 0,
-              likeCount: item.like_count || 0,
-              commentCount: item.comment_count || 0,
-              shareCount: item.share_count || 0,
+              playCount: item.read_amount || item.read_count || 0,
+              likeCount: item.like_amount || item.like_count || 0,
+              commentCount: item.comment_amount || item.comment_count || 0,
+              shareCount: item.share_amount || item.share_count || 0,
             });
             });
           }
           }
 
 
@@ -2266,8 +2377,23 @@ class HeadlessBrowserService {
       }
       }
 
 
       accountInfo.worksList = worksList;
       accountInfo.worksList = worksList;
-      accountInfo.worksCount = worksList.length;
-      logger.info(`[Baijiahao API] Successfully fetched account info: name=${accountInfo.accountName}, fans=${accountInfo.fansCount}, works=${accountInfo.worksCount}`);
+      // 使用 API 返回的 total 字段作为作品总数,而不是已获取的作品列表长度
+      // 因为可能只获取了部分作品(最多 10 页),但 total 是真实的总数
+      if (totalWorks > 0) {
+        accountInfo.worksCount = totalWorks;
+        worksCountFetched = true;
+      } else if (worksList.length > 0) {
+        // 如果 API 没有返回 total,但获取到了作品列表,使用列表长度
+        accountInfo.worksCount = worksList.length;
+        worksCountFetched = true;
+      }
+
+      if (worksListError) {
+        logger.warn(`[Baijiahao API] Works list fetch encountered errors, but returning partial account info`);
+        logger.info(`[Baijiahao API] Account info (partial): name=${accountInfo.accountName}, fans=${accountInfo.fansCount} (fetched: ${fansCountFetched}), works=${accountInfo.worksCount} (fetched: ${worksCountFetched})`);
+      } else {
+        logger.info(`[Baijiahao API] Successfully fetched account info: name=${accountInfo.accountName}, fans=${accountInfo.fansCount} (fetched: ${fansCountFetched}), works=${accountInfo.worksCount} (fetched: ${worksCountFetched}, API total: ${totalWorks}, fetched list: ${worksList.length})`);
+      }
 
 
       return accountInfo;
       return accountInfo;
     } catch (error) {
     } catch (error) {
@@ -3339,7 +3465,7 @@ class HeadlessBrowserService {
       // 首先导航到作品管理页面,确保 API 有正确的上下文和权限
       // 首先导航到作品管理页面,确保 API 有正确的上下文和权限
       const contentManageUrl = 'https://creator.douyin.com/creator-micro/content/manage';
       const contentManageUrl = 'https://creator.douyin.com/creator-micro/content/manage';
       const currentUrl = page.url();
       const currentUrl = page.url();
-      
+
       if (!currentUrl.includes('/content/manage')) {
       if (!currentUrl.includes('/content/manage')) {
         logger.info(`[DirectAPI] Navigating to content manage page...`);
         logger.info(`[DirectAPI] Navigating to content manage page...`);
         await page.goto(contentManageUrl, {
         await page.goto(contentManageUrl, {
@@ -3347,7 +3473,7 @@ class HeadlessBrowserService {
           timeout: 30000,
           timeout: 30000,
         });
         });
         await page.waitForTimeout(2000);
         await page.waitForTimeout(2000);
-        
+
         // 检查是否需要登录
         // 检查是否需要登录
         const newUrl = page.url();
         const newUrl = page.url();
         if (newUrl.includes('login') || newUrl.includes('passport')) {
         if (newUrl.includes('login') || newUrl.includes('passport')) {
@@ -3384,7 +3510,7 @@ class HeadlessBrowserService {
         // 获取作品数
         // 获取作品数
         const awemeList = data?.aweme_list || [];
         const awemeList = data?.aweme_list || [];
         logger.info(`[DirectAPI] API response: status_code=${data?.status_code}, has_more=${data?.has_more}, max_cursor=${data?.max_cursor}, aweme_list_length=${awemeList.length}`);
         logger.info(`[DirectAPI] API response: status_code=${data?.status_code}, has_more=${data?.has_more}, max_cursor=${data?.max_cursor}, aweme_list_length=${awemeList.length}`);
-        
+
         // 检查 API 返回状态
         // 检查 API 返回状态
         if (data?.status_code !== 0 && data?.status_code !== undefined) {
         if (data?.status_code !== 0 && data?.status_code !== undefined) {
           logger.warn(`[DirectAPI] API returned error status_code: ${data.status_code}`);
           logger.warn(`[DirectAPI] API returned error status_code: ${data.status_code}`);