【问题标题】:Puppeteer: how to store a session (including cookies, page state, local storage, etc) and continue later?Puppeteer:如何存储会话(包括 cookie、页面状态、本地存储等)并稍后继续?
【发布时间】:2020-01-19 02:31:31
【问题描述】:

是否可以有一个 Puppeteer 脚本打开并与页面交互,然后按原样保存该浏览器会话,并让另一个脚本加载并从那里继续?

“浏览器会话”是指当前加载的页面,包括页面状态(DOM 空间和 javascript 变量等)、cookie、本地存储、整个 shebang。基本上它需要的一切都在前一个脚本停止的地方继续。

如果没有,那么至少可以导出和导入 cookie 和本地存储吗?所以我可以重新加载特定页面并继续处理,保持所有登录或会话数据完好无损。

【问题讨论】:

标签: session cookies browser local-storage puppeteer


【解决方案1】:

我不能肯定地说,但由于 Puppeteer“只是”Chrome DevTools 协议 (cdp) 的包装器,并且 cpd 没有执行您要求的本机“命令”,因此这是不可能的为整个shebang做这件事。

但你有选择。一个不错的选择是为下一个脚本重新使用相同的浏览器。您只需将“userDataDir”选项传递给 puppeteer.launch 命令。示例:puppeteer.launch({ userDataDir: '/tmp/myChromeSession' });。每个使用它的 puppeteer 脚本都将使用相同的浏览器,因此它们将共享“永久”cookie。 “会话”cookie(或有过期时间的)肯定会被删除,但这是 cookie 应该工作的方式。

Excerpt关于用户数据目录:

用户数据目录包含配置文件数据,例如历史, 书签、cookie 以及其他每次安装的本地状态。

尽管此参考文献没有写任何关于 Web 存储的内容,但它也保存在用户数据目录中。所以,使用这个选项你很高兴。我认为这是您情况的最佳选择。

您还有其他选择,例如仅复制 cookie 和存储(localStorage 和 sessionStorage)。

使用 puppeteer 复制 cookie

对于 puppeteer,这个过程非常痛苦:您必须指定要从中获取 cookie 的每个来源。例如,如果您的网站嵌入了第三方的东西,如 google 登录或跟踪,您必须从“google.com”、“.google.com”、“www.google.com”等复制 cookie。这非常非常愚蠢而痛苦。无论如何,要复制 cookie 来源 https://a.b.c,问题:const abcCookies = await page.cookies('https://a.b.c'); 要恢复它们:await page.setCookie(...abcCookies);。由于它们是json,您可以将它们序列化并保存到磁盘,以便以后恢复。

使用 CDP 复制 cookie

let { cookies } = await page._client.send('Network.getAllCookies');

参考:Network.getAllCookies

要恢复它们,请使用Network.setCookies cdp 方法。同样,您可以序列化这些 cookie 并保存到磁盘以便稍后恢复。

复制存储(localStorage 和 sessionStorage)

您可以通过const ls = await page.evaluate(() => JSON.stringify(localStorage));const ss = await page.evaluate(() => JSON.stringify(sessionStorage)); 转移您自己的原始存储。但是,出于安全原因,您不能访问其他源存储。不知道 CDP 等价物并认为它还不存在。

网页缓存

如果您的站点有服务人员,它很可能会将内容保存在 Web Cache API 上。我不知道保存这些缓存数据是否有意义,但如果对您很重要,您也可以传输这些缓存,但不要使用 puppeteer apis 或 cdp。您必须自己使用Cache api并使用page.evaluate传输缓存。

索引数据库

如果要复制 IndexedDB 内容,可以使用 cdp IndexedDB 域方法(如“IndexedDB.requestData”)获取任何来源的数据,但不能设置/恢复此数据。 :) 但是,您可以在自己的来源中使用 page.evaluate 以编程方式恢复数据。

【讨论】:

    【解决方案2】:

    Icrespilho的回答很有价值。他给读者留下了两个练习,我做了一个:IndexedDB。

    复制 IndexedDB

    他写道:

    如果您想复制 IndexedDB 内容,您可以使用 cdp IndexedDB 域方法(如“IndexedDB.requestData”)获取任何来源的数据,但您无法设置/恢复此数据。 :) 但是,您可以在自己的来源中使用 page.evaluate 以编程方式恢复数据。

    我已将数据读取为:

    const indexedDB = await page.evaluate(async () => {
      const result = {};
      const databases = await window.indexedDB.databases();
    
      const connect = (database) => new Promise(function (resolve, _) {
        const request = window.indexedDB.open(database.name, database.version);
        request.onsuccess = _ => resolve(request.result);
      });
    
      const getAll = (db, objectStoreName) => new Promise(function (resolve, _) {
        const request = db.transaction([objectStoreName]).objectStore(objectStoreName).getAll();
        request.onsuccess = _ => resolve(request.result);
      });
    
      for (i = 0; i < databases.length; i++) {
        const db = await connect(databases[i])
        const dbName = db.name;
        result[dbName] = {}
        for (j = 0; j < db.objectStoreNames.length; j++) {
          const objectStoreName = db.objectStoreNames[j];
          result[dbName][objectStoreName] = []
          const values = await getAll(db, objectStoreName);
          result[dbName][objectStoreName] = values;
        }
    
      }
      return result;
    });
    

    我希望这对任何人都有帮助。

    【讨论】:

    • 您正在阅读IndexedDB,对吧?你是怎么设置的?
    猜你喜欢
    • 2020-12-26
    • 1970-01-01
    • 2015-06-21
    • 1970-01-01
    • 1970-01-01
    • 2014-11-28
    • 2013-05-09
    • 1970-01-01
    • 2012-02-12
    相关资源
    最近更新 更多