当我打开太多页面并忽略最后一个 URL 时,PhantomJS 崩溃

Posted

技术标签:

【中文标题】当我打开太多页面并忽略最后一个 URL 时,PhantomJS 崩溃【英文标题】:PhantomJS crashes when I open too many pages and ignores the last URL 【发布时间】:2015-12-05 07:20:56 【问题描述】:

系统:Windows 8.1 64bit,带有来自主页的二进制文件,版本 2.0

我有一个每行 1 个 URL 的 .txt 文件,我阅读每一行并打开页面,搜索特定的 url.match(出于隐私原因在代码中更改了域) - 如果找到,打印找到的 JSON,中止请求,卸载页面。 我的 .txt 文件包含 12500 个链接,出于测试目的,我将其拆分为前 10/100/500 个 url。

问题 1:如果我尝试 10 个 url,它会打印 9,然后使用 40-50% cpu

问题 2:如果我尝试 100 个 url,它会打印 98 个,之后出于任何原因使用 40-50% cpu,然后在 2-3 分钟后崩溃。

问题 3: 98 个链接(打印 96,使用 40-50% cpu,然后也崩溃)和 500 个链接也是如此

TXT 文件: https://www.dropbox.com/s/eeiy12ku5k15226/sitemaps.7z?dl=1

98、100 和 500 链接的故障转储: https://www.dropbox.com/s/ilvbg8lv1bizjti/Crash%20dumps.7z?dl=1

console.log('Hello, world!');
var fs = require('fs');
var stream = fs.open('100sitemap.txt', 'r');
var line = stream.readLine();
var webPage = require('webpage');
var i = 1;

while(!stream.atEnd() || line != "") 
     //console.log(line);
    var page = webPage.create();
    page.settings.loadImages = false;
    page.open(line, function() );
    //console.log("opened " + line);
    page.onResourceRequested = function(requestData, request) 
        //console.log("BEFORE: " +requestData.url);
        var match = requestData.url.match(/example.com\/ac/g)
        //console.log("Match: " + match);
        //console.log("Line: " + line);
        //console.log("Match: " + match);
        if (match != null) 
            var targetString = decodeURI(JSON.stringify(requestData.url));
            var klammerauf = targetString.indexOf("");
            var jsonobjekt = targetString.substr(klammerauf,   (targetString.indexOf("") - klammerauf) + 1);
            targetJSON = (decodeURIComponent(jsonobjekt));
            console.log(i);
            i++;
            console.log(targetJSON);
            console.log("");
            request.abort();
            page.close();
        
    ;
    var line = stream.readLine();


//console.log("File closed");
//stream.close();

【问题讨论】:

【参考方案1】:

并发请求

你真的不应该在循环中加载页面,因为循环是一个同步结构,而page.open() 是异步的。这样做,你会遇到内存消耗猛增的问题,因为所有的 URL 都是同时打开的。如果列表中有 20 个或更多 URL,这将是一个问题。

函数级作用域

另一个问题是 javascript 具有函数级作用域。这意味着即使您在 while 块内定义了 page 变量,它也是全局可用的。由于它是全局定义的,因此您会遇到 PhantomJS 的异步特性的问题。 page.onResourceRequested 函数定义中的 page 很可能与用于打开触发回调的 URL 的 page 不同。查看更多关于 here 的信息。一种常见的解决方案是使用 IIFE 将 page 变量绑定到仅一次迭代,但您需要重新考虑整个方法。

内存泄漏

您还有内存泄漏,因为当page.onResourceRequested 事件中的 URL 不匹配时,您并没有中止请求,也没有清理页面实例。您可能希望对所有 URL 执行此操作,而不仅仅是与您的特定正则表达式匹配的 URL。

轻松修复

一个快速的解决方案是定义一个执行一次迭代并在当前迭代完成时调用下一次迭代的函数。您还可以为所有请求重复使用一个 page 实例。

var page = webPage.create();

function runOnce()
    if (stream.atEnd()) 
        phantom.exit();
        return;
    
    var url = stream.readLine();
    if (url === "") 
        phantom.exit();
        return;
    

    page.open(url, function() );

    page.onResourceRequested = function(requestData, request) 
        /**...**/

        request.abort();

        runOnce();
    ;


runOnce();

【讨论】:

“一个快速的解决方案是定义一个执行一次迭代并在当前迭代完成时调用下一次迭代的函数” - 遗憾的是,这是不可能的,因为数量很大,它需要异步我需要检查的 URL。 它是异步的。如果你想让它更快,那么你需要实现一个页面池。简单地为您拥有的每个 URL 启动一个选项卡是不可行的。 如何实现这样的页面池?

以上是关于当我打开太多页面并忽略最后一个 URL 时,PhantomJS 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Apache 服务器忽略 .htaccess

.NEW MVC 在传递参数并在新选项卡中打开时忽略弹出窗口的大小

rails ajax URL更改

下拉列表刷新

忽略 URL CSRF Guard

请求太多 - Geoplugin