在 CasperJS 中异步单击时如何停止循环

How to stop a loop when clicking asynchronously in CasperJS

我正在使用 CasperJS 抓取多个页面,但我卡住了。

最大页数为 200,但如果下面的 XPath 不存在(对于第 200 页之前的页面),我想停止执行。

如何设置 i 变量?

var casper = require('casper').create();
var x = require('casper').selectXPath;

for (var i=1; i <=200; i++) {
    casper.wait(6000, function() {

        casper.thenClick(x('//*[@id="mArticle"]/div[2]/a['+i+']'), function (){
            console.log('Searching dic');
            words = words.concat(this.evaluate(getWords));
        });

    });
}

CasperJS 提供了 exists() 函数。因此,您可以像这样重写代码:

for (var i=1; i <=200; i++) {
    (function(i){
        casper.wait(6000, function() {
            var button = x('//*[@id="mArticle"]/div[2]/a['+i+']');
            if (!this.exists(button)) {
                this.echo(i + " not available");
                return; // the following `thenClick()` is not executed
            }
            this.thenClick(button, function (){
                console.log('Searching dic');
                words = words.concat(this.evaluate(getWords));
            });
        });
    })(i);
}

我还添加了一个 IIFE,这样您就可以在回调中获得正确的 i。有关详细信息,请参阅 JavaScript closure inside loops – simple practical example

这行得通,但是如果假设 link 100 不存在,那么 link 101 和 102 等也不存在,那么效率不是很高。您会等待很多时间(6 秒乘以 100)。在这种情况下,由于 CasperJS 的异步特性,您需要递归执行此操作:

function execOnce(casper, i, max){
    // end condition
    if (i === max) {
        return;
    }
    casper.wait(6000, function() {
        var button = x('//*[@id="mArticle"]/div[2]/a['+i+']');
        if (!this.exists(button)) {
            this.echo(i + " not available");
            return;
        }
        this.thenClick(button, function (){
            console.log('Searching dic');
            words = words.concat(this.evaluate(getWords));

            // recursive step
            execOnce(this, i+1, max);
        });
    });
};

casper.start(url);

// start the recursive chain
casper.then(function(){
    execOnce(this, 1, 200);
});

casper.run();

请注意,现在您已经递归地拥有它,您可以通过明确查看页面上有什么和没有什么来定义适当的结束条件。