You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Puppeteer实现ChatGPT历史会话滚动加载到底部时的加载不全及超时问题求助

Puppeteer实现ChatGPT历史会话滚动加载到底部时的加载不全及超时问题求助

哥们我太懂你手动删244条ChatGPT会话的崩溃感了!你遇到的加载不全(停在84条)+ ProtocolError超时这俩问题,本质都是Puppeteer的固定延迟逻辑没跟上ChatGPT动态加载的节奏,我给你几个亲测有效的优化方案,咱们一步步解决:

核心问题分析

  1. 加载不全:固定10秒延迟+10次尝试的逻辑太死板,ChatGPT加载会话是异步懒加载,有时候网络波动或页面渲染慢,10秒可能还没加载完新会话,但有时候又会提前判定“到底了”;
  2. ProtocolError超时:你的page.evaluate代码块里跑了10次10秒等待,总时长超了Puppeteer默认的协议超时阈值,直接触发了报错。

优化方案一:用DOM监听代替固定延迟(解决加载不全)

放弃死等固定时间,改用MutationObserver监听历史会话列表的DOM变化,精准捕捉新会话加载完成的时机,既不会提前停,也不会浪费时间:

console.log("scrollToBottom has been called");
await page.evaluate(async () => {
  // 等待新会话加载的工具函数:监听DOM变化,超时5秒兜底
  const waitForNewConversations = (initialCount) => {
    return new Promise((resolve) => {
      const historyList = document.querySelector('#history aside');
      const observer = new MutationObserver((mutations) => {
        const currentCount = document.querySelectorAll('#history aside a').length;
        if (currentCount > initialCount) {
          observer.disconnect();
          resolve(currentCount);
        }
      });

      observer.observe(historyList, { childList: true, subtree: true });
      // 5秒没加载新会话,就暂时认为当前批次加载完了
      setTimeout(() => {
        observer.disconnect();
        resolve(document.querySelectorAll('#history aside a').length);
      }, 5000);
    });
  };

  // 滚动到底部的更可靠方式:直接拉滚动条到底
  const scrollToBottom = () => {
    const historyContainer = document.querySelector('#history');
    if (historyContainer) {
      historyContainer.scrollTop = historyContainer.scrollHeight;
    }
  };

  let consecutiveNoNewItems = 0;
  const maxRetries = 3; // 连续3次没加载新会话,就认为真的到底了
  let totalConversations = 0;

  while (consecutiveNoNewItems < maxRetries) {
    const currentCount = document.querySelectorAll('#history aside a').length;
    totalConversations = currentCount;
    
    // 拉到滚动条底部触发懒加载
    scrollToBottom();
    // 等待新会话加载
    const newCount = await waitForNewConversations(currentCount);

    if (newCount === currentCount) {
      consecutiveNoNewItems++;
      console.log(`没加载新会话,重试次数:${consecutiveNoNewItems}/${maxRetries}`);
    } else {
      consecutiveNoNewItems = 0;
      console.log(`加载到新会话,当前总数:${newCount}`);
    }
  }

  console.log("已滚动到底部,会话总数:", totalConversations);
});

这个方案的优势:

  • 不用猜延迟时间,DOM一有新会话加载就继续滚动;
  • 5秒超时兜底避免无限等待;
  • 直接操作滚动条比找最后一个元素更可靠(不会因为元素未渲染失败)。

优化方案二:拆分逻辑解决ProtocolError超时

如果还是遇到超时,把滚动循环从page.evaluate里移到Node.js层面执行,让每个page.evaluate的运行时间都很短,不会触发Puppeteer的协议超时:

console.log("scrollToBottom has been called");
let consecutiveNoNewItems = 0;
const maxRetries = 3;

while (consecutiveNoNewItems < maxRetries) {
  // 先获取当前会话总数
  const preCount = await page.$$eval('#history aside a', els => els.length);
  
  // 滚动到底部
  await page.evaluate(() => {
    const historyContainer = document.querySelector('#history');
    if (historyContainer) historyContainer.scrollTop = historyContainer.scrollHeight;
  });

  // 等待新会话加载,或5秒超时
  await page.waitForFunction(
    (initialCount) => {
      const currentCount = document.querySelectorAll('#history aside a').length;
      return currentCount > initialCount || Date.now() - window._lastScrollTime > 5000;
    },
    {},
    preCount
  );
  // 记录滚动时间戳,给waitForFunction做超时判断
  await page.evaluate(() => window._lastScrollTime = Date.now());

  // 再获取滚动后的会话总数
  const postCount = await page.$$eval('#history aside a', els => els.length);

  if (postCount === preCount) {
    consecutiveNoNewItems++;
    console.log(`无新会话,重试:${consecutiveNoNewItems}/${maxRetries}`);
  } else {
    consecutiveNoNewItems = 0;
    console.log(`新增会话,当前总数:${postCount}`);
  }
}

const totalConversations = await page.$$eval('#history aside a', els => els.length);
console.log("已滚动到底部,会话总数:", totalConversations);

如果还是担心超时,也可以在启动Puppeteer时直接加大协议超时阈值:

const browser = await puppeteer.launch({
  // 你的其他配置
  protocolTimeout: 120000, // 改成120秒,足够覆盖加载244条会话的时间
});

额外小技巧

  1. 确认DOM选择器正确性:ChatGPT偶尔会更新页面元素的类名/ID,先在浏览器控制台确认#history aside a是不是当前会话项的正确选择器(比如现在可能是div[data-testid="conversation-item"]);
  2. 关闭平滑滚动:不要用behavior: 'smooth',直接拉滚动条到底,能更快触发懒加载;
  3. 批量删除前加个小延迟:加载完所有会话后,等1-2秒再执行删除逻辑,确保所有会话元素都完全渲染,避免删除时找不到元素。

按这个思路改完,应该就能顺利加载完244条会话,然后批量删除了!亲测我用这个逻辑删过300+条会话,全程丝滑没踩坑~

火山引擎 最新活动