windows.open()异步任务被浏览器拦截 / 任务出错

错误情况

最近帮朋友实现批量下载知网 pdf 论文的时候遇到了一个问题:

  • 利用油猴脚本实现批量下载时,知网会拦截下载操作并报错 来源应用不正确2
  • 但是脚本提取出的单个论文的 pdf 下载链接却可以直接通过点击链接单个下载。

原理解析

经过查阅脚本代码,分析两种方式的代码实现。

点击单个下载链接

<a href="${pdfLink}" target="_blank" class="diy-btn">PDF下载</a>
HTML

代码实现了一个下载链接,用户点击这个单个论文的下载链接可以实现 pdf 下载。

通过脚本批量下载

async function downloadSelected() {
//some code //

    for (const item of selectedItems) {
        const link = item.closest('tr').querySelector('a[href^="http"]');
        if (link) {
            // 打开链接
            window.open(link.href, '_blank');       // 核心代码!!!

            // 生成随机延迟时间:2-5秒(步长0.5秒)
            const randomDelay = Math.floor(Math.random() * 7) * 500 + 2000;
            console.log(`延迟 ${randomDelay / 1000} 秒后继续下载...`);

            // 延迟下一次下载,规避知网的机器人下载检测
            await delay(randomDelay);
        }
    }
JavaScript

可见为了规避知网的一些机器人屏蔽,采取了 async function 定义了一个异步处理函数。

  • async function 是 JavaScript 中用于定义异步函数的关键字。 异步函数是一种特殊的函数,它允许你使用 await 关键字暂停函数的执行,等待一个 Promise 对象 resolve 或 reject,然后再继续执行。 这使得异步代码的编写和阅读更加容易,避免了回调地狱。

在异步函数中使用 window.open(link.href, '_blank') 实现下载单个文献。

原因分析

比较两种下载方式,可见成功的方式是直接点击 <a> 标签,失败的方式是 JS 脚本里的 windows.open()
现今的浏览器为了安全性会对很多非用户操作进行拦截,尤其是异步的任务。
在异步任务中使用函数 window.open() 极易让浏览器判定为是非用户直接操作,从而拦截打开的页面。

解决方案(一定程度上)

为了防止被浏览器判定是机器操作而非是用户直接操作,我们在异步函数中也仍然采取点击 <a> 标签的方式来进行下载:
通过 link.click() 来模拟用户直接点击下载链接。
对相关代码段进行如下替换即可:

async function downloadSelected() {
//some code //

    for (const item of selectedItems) {
        const link = item.closest('tr').querySelector('a[href^="http"]');
        if (link) {
            // 打开链接
            link.click();       // 核心代码!!!

            // 生成随机延迟时间:2-5秒(步长0.5秒)
            const randomDelay = Math.floor(Math.random() * 7) * 500 + 2000;
            console.log(`延迟 ${randomDelay / 1000} 秒后继续下载...`);

            // 延迟下一次下载,规避知网的机器人下载检测
            await delay(randomDelay);
        }
    }
JavaScript

再次测试,成功实现自动化批量下载文献。