【问题标题】:Phantomjs opening many pagesPhantomjs 打开许多页面
【发布时间】:2025-12-19 14:35:10
【问题描述】:

我在 phantomjs 中打开多个网页时遇到了一些问题,我首先打开一个包含一些链接的网站,我也想打开这些链接,并将每个 URL 中的一段文本保存到我的 jobs_list 中里面有很多物体。在所有 URL 都运行之后,我想退出 phantomjs。但是现在它永远不会退出,而且我无法从第二个函数接收数据。

var webPage = require('webpage');
var page = webPage.create();
var jobs_list = [];

page.open('url', function (status) {
    page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
        page.onConsoleMessage = function(msg) {
            console.log(msg);
        };
        var list = page.evaluate(function() {

            var jobs = [];
            var job;

            $('.test').each(function(){
                $(this).find('span').each(function(){
                    var job_link = $(this).find('a');
                    var url = job_link.attr("href");

                    job = {title : job_link.text(), url : url, location : ""};
                    jobs.push(job);
                })
            });
            return jobs;
        });
        var i = 0;
        jobs_list = list;
        next_page(i);
    });
});


function next_page(i){
    if (i <= (jobs_list.length-1)) {
        var current_job = jobs_list[i];
        var url = current_job.url;

        page.open(url, function (status) {
            page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {


                var location = page.evaluate(function() {
                    var job_location;
                    $('.job-location').each(function(){
                        $(this).find('li').each(function(){
                            job_location = $(this).text();
                        })
                    })
                    console.log(job_location);
                    return job_location;
                });
                jobs_list[i].location = location;

                if(i == (jobs_list.length-1)) {
                    phantom.exit(0);
                }
            });
        });
        console.log(i, current_job.title);

        next_page(++i);
    }
}

【问题讨论】:

    标签: javascript phantomjs


    【解决方案1】:

    问题在于page.open 调用是异步的。如果您仔细查看您的 next_page 函数,它可以缩短为:

    function next_page(i){
        if (i <= (jobs_list.length-1)) {
            var current_job = jobs_list[i];
            var url = current_job.url;
    
            page.open(url, function (status) {
                ...
            });
            console.log(i, current_job.title);
    
            next_page(++i);
        }
    }
    

    这意味着next_page(++i);page.open(url, ...) 甚至设法加载第一个HTML 内容之前执行。此调用导致下一个page.open(url, ...) 立即执行,从而覆盖先前的请求。而且您永远不会以这种方式获得任何数据。

    你必须做两件事:

    • next_page(++i); 调用移至一页执行完毕的位置
    • 减少条件检查次数

    我建议:

    function next_page(i){
        if (i <= (jobs_list.length-1)) {
            var current_job = jobs_list[i];
            var url = current_job.url;
    
            page.open(url, function (status) {
                page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {
    
                    var location = page.evaluate(function() {
                        var job_location;
                        $('.job-location').each(function(){
                            $(this).find('li').each(function(){
                                job_location = $(this).text();
                            })
                        })
                        console.log(job_location);
                        return job_location;
                    });
                    jobs_list[i].location = location;
    
                    console.log(i, current_job.title);
                    next_page(++i);
                });
            });
        } else {
            phantom.exit(0);
        }
    }
    

    那是相当老的 jQuery 版本。也许您想加载更新的版本。如果页面已经包含 jQuery,您可能会通过加载另一个 jQuery 来破坏页面。在这种情况下,根本不要加载额外的 jQuery 版本。

    【讨论】:

    • 谢谢你的回答,你让我开心!
    • 执行return next_page(++i) 并在else 子句中有一个return 并在初始呼叫后使用phantom.exit(0) 会有什么不同吗?是否会出现任何类型的内存泄漏,就像您使用完整堆栈时那样?
    • @CoolBlue 我不太确定你的意思。在我的代码和 OP 的代码中,next_page(++i) 是周围函数中的最后一条语句。退不退都无所谓。虽然,在某些情况下可以做到这一点。
    • 嗨。不,我只是对退出 phantomjs 而不解开 next_page 函数的递归感到好奇。当您退出时,由于递归,您有 jobs_list.length 堆栈帧和执行上下文。在我询问的 mod 中,我只是使用 return next_page(++i) 作为一种紧凑的方式在完成时通过调用堆栈回退(在遇到我在 else 子句中建议的 return 之后),这与返回值无关.我只是对您在退出 phantomjs 之前解开(或不解开)调用堆栈的想法感兴趣。
    • @CoolBlue 我不确定调用堆栈是如何在 PhantomJS'page.open 中准确实现的,但通常异步函数的回调函数会得到一个新的调用堆栈,所以你一般不会'不必担心。我知道我已经回答了一个问题,我试图在 PhantomJS 中为 setTimeout 找到调用堆栈的最大大小,并且在 hour without success 之后我已经停止。