24

我的目标是使用以下方法执行 PhantomJS:

// adding $op and $er for debugging purposes
exec('phantomjs script.js', $op, $er);
print_r($op);
echo $er;

然后在里面script.js,我打算用multiplepage.open()来截取不同页面的截图,比如:

var url = 'some dynamic url goes here';
page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 1');  
    page.render('./slide1.png');            
});

page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 2');  
    page.render('./slide2.png');        
});

page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 3');  
    page.render('./slide3.png');        
    phantom.exit(); //<-- Exiting phantomJS only after opening all 3 pages
});

在运行exec时,我在页面上得到以下输出:

Array ( [0] => opening page 3 ) 0

结果我只得到了第三页的截图。我不确定为什么 PhantomJS 会跳过第一和第二个代码块(从console.log()应该从第一个和第二个块输出的丢失消息中可以看出)并且只执行第三个代码块。

4

4 回答 4

44

问题是第二个page.open在第一个完成之前被调用,这可能会导致多个问题。您希望逻辑大致如下(假设文件名作为命令行参数给出):

function handle_page(file){
    page.open(file,function(){
        ...
        page.evaluate(function(){
            ...do stuff...
        });
        page.render(...);
        setTimeout(next_page,100);
    });
}
function next_page(){
    var file=args.shift();
    if(!file){phantom.exit(0);}
    handle_page(file);
}
next_page();

没错,它是递归的。这可确保page.open在您转到下一个文件之前,以 100 毫秒的宽限期完成传递给的函数的处理。

顺便说一句,你不需要一直重复

page = require('webpage').create();
于 2013-06-08T15:17:58.507 回答
8

我已经尝试了接受的答案建议,但它不起作用(至少不适用于 v2.1.1)。

准确地说,接受的答案在某些时候有效,但我仍然遇到零星的 page.open() 调用失败,大约 90% 的时间是在特定数据集上。

我找到的最简单的答案是为每个 url 实例化一个新的页面模块。

// first page
var urlA = "http://first/url"
var pageA = require('webpage').create()

pageA.open(urlA, function(status){
    if (status){
        setTimeout(openPageB, 100) // open second page call
    } else{
        phantom.exit(1)
    }
})

// second page
var urlB = "http://second/url"
var pageB = require('webpage').create()

function openPageB(){
    pageB.open(urlB, function(){
        // ... 
        // ...
    })
}

关闭方法的页面模块api文档中的以下内容说

关闭(){无效}

关闭页面并释放与其关联的内存堆。调用后不要使用页面实例。

由于一些技术限制,网页对象可能不会被完全垃圾收集。当反复使用同一个对象时,经常会遇到这种情况。调用此函数可能会停止增加的堆分配。

基本上在我测试了 close() 方法之后,我决定对不同的 open() 调用使用相同的网页实例太不可靠了,需要说明。

于 2016-03-15T10:23:59.373 回答
2

您可以使用递归:

var page = require('webpage').create();

// the urls to navigate to
var urls = [
    'http://phantomjs.org/',
    'https://twitter.com/sidanmor',
    'https://github.com/sidanmor'
];

var i = 0;

// the recursion function
var genericCallback = function () {
    return function (status) {
        console.log("URL: " + urls[i]);
        console.log("Status: " + status);
        // exit if there was a problem with the navigation
        if (!status || status === 'fail') phantom.exit();

        i++;

        if (status === "success") {

            //-- YOUR STUFF HERE ---------------------- 
            // do your stuff here... I'm taking a picture of the page
            page.render('example' + i + '.png');
            //-----------------------------------------

            if (i < urls.length) {
                // navigate to the next url and the callback is this function (recursion)
                page.open(urls[i], genericCallback());
            } else {
                // try navigate to the next url (it is undefined because it is the last element) so the callback is exit
                page.open(urls[i], function () {
                    phantom.exit();
                });
            }
        }
    };
};

// start from the first url
page.open(urls[i], genericCallback());
于 2016-11-14T12:20:52.173 回答
1

使用排队进程,示例:

var page = require('webpage').create();

// Queue Class Helper
var Queue = function() {
    this._tasks = [];
};
Queue.prototype.add = function(fn, scope) {
    this._tasks.push({fn: fn,scope: scope});
    return this;
};
Queue.prototype.process = function() {
    var proxy, self = this;
    task = this._tasks.shift();
    if(!task) {return;}
    proxy = {end: function() {self.process();}};
    task.fn.call(task.scope, proxy);
    return this;        
};
Queue.prototype.clear = function() {
    this._tasks = []; return this;
};

// Init pages .....  
var q = new Queue();       

q.add(function(proxy) {
  page.open(url1, function() {
    // page.evaluate
    proxy.end();
  });            
});

q.add(function(proxy) {
  page.open(url2, function() {
    // page.evaluate
    proxy.end();
  });            
});


q.add(function(proxy) {
  page.open(urln, function() {
    // page.evaluate
    proxy.end();
  });            
});

// .....

q.add(function(proxy) {
  phantom.exit()
  proxy.end();
});

q.process();

我希望这是有用的,问候。

于 2017-02-22T21:17:44.253 回答