PhantomJS 2.1.1中onLoadFinished无因触发两次的问题排查
PhantomJS 2.1.1中onLoadFinished重复触发两次的问题分析与解决
我来帮你拆解这个问题——你遇到的onLoadFinished被触发两次的情况,在PhantomJS里其实挺常见的,结合你的代码和输出,咱们一步步理清原因和解决办法。
先把你的代码整理后贴出来(方便看逻辑):
var system = require('system'); var page = require('webpage').create(); var server = require('webserver').create(); var someUrl = "http://sportfun.bg"; var port = 3000; // 屏幕分辨率设置 page.viewportSize = { width: 1920, height: 1080 }; // User Agent设置 page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36'; page.onConsoleMessage = function(msg) { system.stderr.writeLine('Console: ' + msg); }; var service = server.listen(port, function (request, response) { var POST = request.post; page.open(someUrl, function (status) { if (status !== 'success') { console.log('Unable to post!'); } else { console.log("Here0"); page.onLoadFinished = function(status) { if(status === "success"){ console.log("Here 1"); response.statusCode = 200; response.headers = { 'Cache': 'no-cache', 'Content-Type': 'text/plain;charset=utf-8' }; response.write("We good"); response.close(); } }; } }); }); console.log("Server started!");
为什么会触发两次onLoadFinished?
主要有两个最可能的诱因:
- 目标页面存在重定向:比如
http://sportfun.bg可能自动跳转到HTTPS版本,或者其他子页面。PhantomJS的onLoadFinished会在每次页面加载完成时触发,包括重定向后的页面,所以会出现两次触发的情况。 - 全局页面的事件监听器未清理:你的
page对象是全局创建的,每次收到POST请求时都会重新绑定onLoadFinished。如果前一次请求的页面加载还未完成,或者监听器没有被覆盖/移除,就会导致同一个事件被多次监听触发。
先验证原因
你可以添加一个onUrlChanged事件来排查是否有重定向:
page.onUrlChanged = function(targetUrl) { console.log('URL changed to: ' + targetUrl); };
启动服务器发请求后,如果输出两次不同的URL,那就是重定向导致的;如果URL始终没变,那就是监听器重复绑定的问题。
解决办法
针对重定向场景
如果你只关心最终加载完成的页面,可以在onLoadFinished里判断当前URL是否是目标最终地址,处理完后立即解绑事件:
page.onLoadFinished = function(status) { if(status === "success" && page.url === "https://sportfun.bg"){ // 替换成实际的最终URL console.log("Here 1"); // 后续响应处理逻辑 response.statusCode = 200; response.headers = { 'Cache': 'no-cache', 'Content-Type': 'text/plain;charset=utf-8' }; response.write("We good"); response.close(); // 处理完后清空监听器,避免下次触发 page.onLoadFinished = null; } };
针对监听器重复绑定场景
最稳妥的方式是每次请求创建独立的page实例,而不是复用全局的page。这样每个请求都有独立的页面上下文,彻底避免事件冲突:
var service = server.listen(port, function (request, response) { var POST = request.post; // 每次请求创建新的page实例 var page = require('webpage').create(); page.viewportSize = { width: 1920, height: 1080 }; page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36'; page.onConsoleMessage = function(msg) { system.stderr.writeLine('Console: ' + msg); }; page.open(someUrl, function (status) { if (status !== 'success') { console.log('Unable to post!'); response.close(); page.close(); // 关闭页面释放资源 } else { console.log("Here0"); page.onLoadFinished = function(status) { if(status === "success"){ console.log("Here 1"); response.statusCode = 200; response.headers = { 'Cache': 'no-cache', 'Content-Type': 'text/plain;charset=utf-8' }; response.write("We good"); response.close(); page.close(); // 处理完成后关闭页面 } }; } }); });
如果不想每次创建新page,也可以在每次绑定onLoadFinished前先清空之前的监听器:
page.open(someUrl, function (status) { if (status !== 'success') { console.log('Unable to post!'); response.close(); } else { console.log("Here0"); // 先清空之前的监听器 page.onLoadFinished = null; page.onLoadFinished = function(status) { if(status === "success"){ console.log("Here 1"); // 响应处理逻辑 response.statusCode = 200; response.headers = { 'Cache': 'no-cache', 'Content-Type': 'text/plain;charset=utf-8' }; response.write("We good"); response.close(); // 处理完再次清空监听器 page.onLoadFinished = null; } }; } });
内容的提问来源于stack exchange,提问作者Venelin
相关产品推荐
相关产品推荐

