解决HtmlUnit执行JS报错提示ScriptException
问题描述
HtmlUnit作为一款比Selenium更轻量的HeadLess的Java版本浏览器模拟器,不需要在服务器上安装部署浏览器及其Driver程序。
但是,众所周知,HtmlUnit对JS脚本的支持并不是很友好,GitHub中大部分的issue都和JS执行错误有关,作者也一直在升级JS Engine。
笔者在实际使用过程中(HtmlUnit 4.1.0版本),也遇到了JS执行问题,并且issue、StackOverflow和google均未找到较好的解决方案。
具体问题原因,是由于原站使用了ES6中的async
、await
、then
等关键字和方法。
org.htmlunit.ScriptException: missing ; before statement (https://www.xxx.com/includes/js/common.js#322)
at org.htmlunit.javascript.JavaScriptEngine$HtmlUnitContextAction.run(JavaScriptEngine.java:963)
at org.htmlunit.corejs.javascript.Context.call(Context.java:585)
at org.htmlunit.corejs.javascript.ContextFactory.call(ContextFactory.java:481)
at org.htmlunit.javascript.HtmlUnitContextFactory.callSecured(HtmlUnitContextFactory.java:313)
at org.htmlunit.javascript.JavaScriptEngine.compile(JavaScriptEngine.java:735)
at org.htmlunit.javascript.JavaScriptEngine.compile(JavaScriptEngine.java:110)
at org.htmlunit.html.HtmlPage.loadJavaScriptFromUrl(HtmlPage.java:1124)
at org.htmlunit.html.HtmlPage.loadExternalJavaScriptFile(HtmlPage.java:1015)
解决方法
开始逆向思考,既然有这些不支持的关键字导致JS执行报错,能否在页面及JS资源加载时,修改网页及JS资源的源代码,过滤不支持的关键字。
通过翻阅HtmlUnit官网文档,发现有个ScriptPreProcessor
接口,可以通过钩子修改JS的执行代码,实现代码如下:
this.browser.setScriptPreProcessor((htmlPage, sourceCode, sourceName, lineNumber, htmlElement) -> {
// 处理HtmlUnit不支持ES6的async、await关键字,对js源码进行处理
if (StringUtils.contains(sourceName, "tools.js")) {
sourceCode = sourceCode.replace("async ", StringUtils.EMPTY);
sourceCode = sourceCode.replace("await ", StringUtils.EMPTY);
}
return sourceCode;
});
大家可以根据自己遇到的具体问题,对sourceCode
进行处理。
最后再引申一下,如果有些JS对象不支持,可以分析原站JS代码,通过Java代替实现JS的逻辑,再通过变量赋值、修改JS源码的方式进行处理。