Puppeteer:如何存储会话(包括 cookie、页面状态、本地存储等)并稍后继续?

Posted

技术标签:

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

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

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

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

【问题讨论】:

关于cookies:link 另见How to manage log in session through headless chrome? 【参考方案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,对吧?你是怎么设置的?

以上是关于Puppeteer:如何存储会话(包括 cookie、页面状态、本地存储等)并稍后继续?的主要内容,如果未能解决你的问题,请参考以下文章

puppeteer 中缺少请求标头

在 Cookie 中存储 Express 会话

PHP cURL 没有存储会话 cookie...如何解决这个问题?

Puppeteer 类型 node_modules/puppeteer/lib/types"' 没有导出成员 'Cookie'

如何手动解密 Rails 5 会话 cookie?

Ngrok 无法提供 PHP 会话 cookie: