如何使此脚本在页面重新加载后恢复执行?

3

初步背景介绍

我被要求在一个我不拥有并且没有API访问权限的网站上手动执行非常重复的操作。

唯一能自动化这些操作的方法就是编写一些JavaScript代码,并在浏览器上执行它以自动执行我本来会手动进行的操作。

如果此问题已经在其他地方得到解答,请提前谅解,因为我只是后端开发人员,对于前端知识的了解还很有限,我没有找到任何相似的解决方法。

问题说明

假设我需要逐个向表单提交多个条目,我已经编写了以下代码(仅为演示目的而过于简化):

//This array of Json objects is produced by an upstream service
var inputs = [
    {
       ...
    },
    {
       ...
    },
    {
       ...
    }
]

for (i = 0; i < inputs.length; i++) {
    fillSomeForms(inputs[i])
    clickSubmit() //<-- this will make the page reload, and so the script execution stop
}

我遇到的问题很基础:第一个 for 迭代后,当我调用 clickSubmit() 时,页面重新加载(因为提交是 POST,紧接着是跳转到“提交下一个”页面),所以 JS 停止执行。
我已经尝试在网上寻找类似的问题,并且我看到人们会调整 localStorage 以便恢复他们的脚本执行。 然而,这似乎假设脚本是前端代码的资源,这对我来说并不是事实(我并不拥有该代码,我只是将此 JS 注入浏览器的开发者控制台并执行它以节省时间)。
有没有办法达到这个目的?我不一定要找到一个干净的解决方案,只是想找到能够使其工作并节省我们一些繁琐劳动的东西(我在这里做的事情都不干净,但系统管理员不想提供访问平台实际提供的 REST API 的权限)。

2
另一个解决方案是使用Fetch API来模拟整个过程,而不是实际填写表单并发送它,但是如果没有更多的上下文,很难说这是否可行(例如:取决于CSRF实现)。 - BJRINT
您可以尝试采用这里提供的思路 https://dev59.com/QW025IYBdhLWcg3w_bCr,动态创建(迭代)表单的版本,填充其值,将其目标定向到新窗口,在父页面中使用js提交,删除表单,关闭窗口,并根据需要重复执行。 - Dave Pritlove
2个回答

1
当您注入控制台时,将页面的副本加载到iframe中,并从该副本提交表单:
const inputs = [ /* a convenient inputs array */ ];
const pageCopy = document.body.appendChild( document.createElement( "iframe" ) );
pageCopy.addEventListener( "load", () => {
    //The page copy has finished loading / reloading, let's submit more stuff
    if( inputs.length > 0 ) {
        const moreInput = inputs.pop();
        console.log( "Submitting inputs: ", moreInput );
        //this shouldn't work, but let's clone the current DOM into the iframe...
        pageCopy.contentDocument.body.parentElement.innerHTML =
            document.body.parentElement.innerHTML;
        fillSomeFormsInPageCopy( pageCopy.contentDocument, moreInput );
        pageCopy.contentDocument.querySelector( "#submitButtonId" ).click();
        console.log( "Clicked submit. Will wait for iframe to finish reloading..." );
        //Okay, we clicked and the iframe is reloading. This event will fire again as soon as it's done reloading, ready to submit more form data
    }
    else if( inputs.length === 0 ) {
        console.log( "Finished submitting all the inputs in the array!" );
    }
} );
pageCopy.src = document.location.href;

请理解我无法测试这段代码。(我甚至不确定click()事件是否可以跨越iframe边界触发,因为安全问题,但我希望它能够。)
希望您能够理解如何使用pageCopy的文档来查找表单元素并设置它们的值。例如,您可以使用
pageCopy.contentDocument.getElementById( "form-entry-id-1" ).value =
    moreInput[ "form-entry-id-1" ];

我认为这是一个不错的方式。我正在尝试您的建议,所以我已经修改了我的函数“fillSomeFormsInPageCopy”,从变量X中检索元素,而不是使用关键字document,然后当我调用该函数时,我将这个变量X传递给“pageCopy.contentDocument”。但是,它无法找到页面DOM中通常应该存在的第一个元素。我错过了什么? - Matteo NNZ
@MatteoNNZ 问题在于由于提交按钮很可能使用页面URL上的操作,因此iframe的URL必须正确。我无法想象为什么它会加载失败。我得去上班了...而且如果没有看到你所看到的东西,我就不能提供更多帮助...所以这是我的最后建议。尝试这样做:设置pageCopy的src,等待pageCopy加载完毕,然后尝试克隆当前页面,使用pageCopy.contentDocument.body.parentElement.innerHTML = document.body.parentElement.innerHTML,填写表单并提交。(更新了示例代码。)祝好运! - Michael G
我受到了你提出的启发,但是我没有在同一页中使用iframe,而是打开了一个新标签页。你可以查看我发布的答案。如果你将那个答案的内容嵌入到你的答案中,我会接受你的答案并删除我的(因为最终是你引导我走上了正确的道路:))。 - Matteo NNZ
什么?!不,不,不。那不是我的想法,我也不知道为什么你的代码能够工作。about:blank 是一个不同的域名,这是可以保证的。这难道不与域隔离旨在防止的一切相悖吗? :-| (但我肯定会尝试你的发现,所以谢谢你发布它。) - Michael G
就像我说的,我对自己做了什么毫无头绪,只是在受到你的启发后混合了一些答案(创建一个iframe并从那里发布),除了创建一个iframe之外,我打开了一个新标签页,在其中复制了我的主页面源代码。我不会感到惊讶,我正在与之交互的网站没有强制执行域隔离,这也许就是为什么它能够工作的原因。无论如何,我已经接受了我的答案(因为它实际上是有效的),但非常感谢你的提示! - Matteo NNZ
显示剩余5条评论

0

如果有人在未来遇到类似问题,我最终通过每次循环打开一个新标签页(并在该标签页中工作)来解决了这个问题。

像这样:

while (inputs.length > 0) {
    const singleInput = inputs.pop();
    const newWindow = window.open('about:blank', '_blank');
    newWindow.addEventListener('load', () => {
        newWindow.document.body.parentElement.innerHTML = document.body.parentElement.innerHTML;
        fillForm(newWindow.document, singleInput) //<-- the function fill form uses the document in parameter to perform the different get/set
        newWindow.document.getElementById("submit-button").click();
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接