Headless Google Chrome:如何防止网站知道他们的 window 是否被聚焦

Headless Google Chrome: How to prevent sites to know whether their window is focused or not

有没有办法让网站知道它们是否可见?

也许是命令行标志?我在这里查过,但找不到合适的 https://peter.sh/experiments/chromium-command-line-switches/.

我认为他们使用页面可见性 API:https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API

页面可见度API

根据文档 Page Visibility API 当文档不可见时,通过让页面避免执行不必要的任务来尝试节省资源和提高性能时会派上用场。

根据Page Visibility Level 2 W3C Editor's Draft

The Page Visibility API defines a means to programmatically determine the visibility state of the top-level browsing context, and to be notified if the visibility state changes. Without knowing the visibility state of a page, web developers have been designing web pages as if they are always visible. This not only results in higher machine resource utilization, but it prevents web developers from making runtime decisions based on whether the web page is visible to the user. Designing web pages with knowledge of the page's visibility state can result in improved user experiences and power efficient sites.

With this API, web applications can choose to alter their behavior based on whether they are visible to the user or not. For example, this API can be used to scale back work when the page is no longer visible.


可见性状态

顶级浏览上下文的文档可以处于以下可见状态之一:

  • 隐藏:文档在任何屏幕上都不可见。
  • 可见:文档在至少一个屏幕上至少部分可见。

可见性状态通过 VisibilityState 枚举反映在 API 中,如下所示:

enum VisibilityState {
  "hidden", "visible"
};

扩展程序

本规范扩展文档接口如下:

partial interface Document {
  readonly attribute boolean hidden;
  readonly attribute VisibilityState visibilityState;
  attribute EventHandler onvisibilitychange;
};

An usage example

为了改善用户体验和优化 CPU 以及电源效率,应用程序可以在 visible, and automatically pause the playback when the application is hidden:

时自动播放视频
const videoElement = document.getElementById("videoElement");

// Autoplay the video if application is visible
if (document.visibilityState === "visible") {
  videoElement.play();
}

// Handle page visibility change events
function handleVisibilityChange() {
  if (document.visibilityState === "hidden") {
    videoElement.pause();
  } else {
    videoElement.play();
  }
}

document.addEventListener('visibilitychange', handleVisibilityChange);

此外,@MichaelMahemoff 在这个 blog 中进一步指出,多标签浏览现在是当前的规范,你不能假设用户正在观看你的应用只是因为它是 运行 并且新的 页面可见性 API 可让您的应用发现它是否可见。您可以使用 API 来减少不必要的网络 activity 和计算。如果您正在使用任何当前的 ChromeChromium 构建,如果当前页面被隐藏,您可以在控制台中尝试document.webkitHidden。然而,document.webkitVisibilityState 将 return 一个指示当前状态的字符串,visiblehidden,以及prerendered。一个新的 webkitvisibilitychange 事件将在任何这些变化时触发,例如当用户打开您应用的标签页或离开时。


解决方案

所有这些交互都可以使用 visibility.js 来观察,它是 页面可见性 API 的包装器,并允许您确定您的网页是否对用户可见或隐藏在后台选项卡或预呈现中。它还允许您在 JavaScript 逻辑中使用页面可见性状态,并通过禁用不必要的计时器和 AJAX 请求来提高浏览器性能,或改善用户界面体验(例如,通过在用户访问时停止视频播放或幻灯片放映)切换到另一个浏览器选项卡)。

Firefox 特定解决方案:另一种 Firefox 特定解决方案可以是使用 Firefox 的 Disable Page Visibility API 扩展,它会禁用所有页面的 API。

Here you can find a detailed discussion on How to install extension permanently in geckodriver

Chrome具体解决方案:另一种Chrome具体解决方案可以是使用Chrome中的Don't Make Me Watch扩展网上商店。

Here you can find a detailed discussion on How to load extension within chrome driver in selenium with python

如果您的目标是欺骗可见性API,则在相关页面或框架中注入这段脚本:

await page.evaluate(`
    Object.defineProperty(window.document,'hidden',{get:function(){return false;},configurable:true});
    Object.defineProperty(window.document,'visibilityState',{get:function(){return 'visible';},configurable:true});
    window.document.dispatchEvent(new Event('visibilitychange'));
`);

它首先覆盖window.hidden使其成为returnfalse。然后,如果页面已经隐藏,它会触发 visibilitychange 事件以通知文档。

或者在创建文档后立即覆盖 API:

await page.evaluateOnNewDocument(`
    Object.defineProperty(window.document,'hidden',{get:function(){return false;},configurable:true});
    Object.defineProperty(window.document,'visibilityState',{get:function(){return 'visible';},configurable:true});
`);