同步CasperJS操作期间的异步调用

时间:2015-07-17 20:37:42

标签: javascript asynchronous phantomjs screen-scraping casperjs

分配麻烦后(第一个计时器nodejs和casperjs / phantomjs)它开始工作。我用curl(php)做了这个工作。

这是我试图完成的事情:

  1. 登录
  2. 获取所有单位
  3. 解析他们的详细信息
  4. (我的问题)2个单位的详细信息由ajax电话
  5. 提供
    casper.start(url, function() {
          this.evaluate(function() {
              document.querySelector("input[name='username']").value = "username";
              document.querySelector("input[name='password']").value = "passwrd";
              document.querySelector("#login").click();
         });
         console.log("Logged in..");
    });
    
    var processPage = function() {
        console.log("Get all units..");
        var units = this.evaluate(getUnits);
        allUnits.push(units);
    
        if (!this.evaluate(isLastPage)) {
            this.thenClick('.paging li:last-child a').then(function() {
                currentPage++;
                console.log("Stepping to page.. " + currentPage);
                this.waitFor(function() {
                    return currentPage === this.evaluate(getSelectedPage);
                }, processPage, terminate);
            });
        } else{
            require('utils').dump(allUnits);
            casper.then(function() {
                 this.capture('test.png');
            });
            console.log("Total unit count: " + allUnits.length);
        }
    };
    
    
    
     casper.waitForSelector('.units', processPage, terminate);
     casper.run();
    

    在下面的函数中,我解析行,我想添加由ajax提取的2个细节,但我不知道该怎么做。 (异步)

    function getUnits() {
        var rows = document.querySelectorAll('.units');
        var units = [];
    
        for (var i = 0, row; row = rows[i]; i++) {
            var aID = row.querySelector('a').getAttribute('href').split('/');
            unit['id'] = aID[2];
            //add other details for the unit
    
            **//Do a async call to the 2 external links with the ID and add the details to the unit**
    
            units.push(unit);
        } 
    
        return units;
    
    };
    

    重要的是要注意的是,最后我想在单位上运行另一个函数,但是在运行之前必须已经提取所有函数...

    修改

    登录后页面显示一个表格,我正在使用该表格

    • ID
    • OWNER
    • STREET
    • BEINGFOLLOWED (对帖子的链接进行的自动ajax调用)
    • PLACEDABIDON(对链接进行的自动ajax调用 交)

    我试图通常使用casper获取最后2个字段但有时它得到了值,有时它没有(请求有时太慢)

    想知道我是怎么知道如何获得这些字段而不等待每一行(单位)直到它获得价值。 (所以每个单位都应该为他们获取自己的值并将其填入他们的对象。所以可能需要回调?

    或者我可以做我自己的请求我只需要ID和cookie来做帖子(链接将ID和Cookie作为参数)并获取详细信息并填写它但我不知道该怎么做或者如果第一个解决方案效果更好,或者即使这是可能的......

    最重要的是,在所有单位都有他们的详细信息后,它应继续使用应用程序的逻辑...

1 个答案:

答案 0 :(得分:3)

由于PhantomJS(和CasperJS)有两个上下文,因此很容易打破执行流程。

我看到两种解决问题的方法。

1。自己发送请求

您需要在页面上下文(evaluate()内)触发请求,并让外部上下文等待结果。我假设您可以在页面上下文中获得成功回调。

您必须将外部请求的结果放在全局某处,以便外部上下文可以访问它。例如,修改您的getUnits()函数,如下所示:

function getUnits() {
    var rows = document.querySelectorAll('.units');
    var units = [];
    window.__externalRequestResults = [[], []];

    for (var i = 0, row; row = rows[i]; i++) {
        var aID = row.querySelector('a').getAttribute('href').split('/');
        unit['id'] = aID[2];
        //add other details for the unit

        //Do a async call to the 2 external links with the ID and add the details to the unit
        (function(i){
            var xhr = new XMLHttpRequest();
            xhr.open("GET", someURLwithParameters, true);
            xhr.onreadystatechange = function(){
                if (xhr.readyState === 4) { // DONE
                    __externalRequestResults[0][i] = xhr.responseText;
                }
            };

            xhr = new XMLHttpRequest();
            xhr.open("GET", someOtherURLwithParameters, true);
            xhr.onreadystatechange = function(){
                if (xhr.readyState === 4) { // DONE
                    __externalRequestResults[1][i] = xhr.responseText;
                }
            };
            xhr.send();
        })(i);

        units.push(unit);
    } 

    return units;
};

现在您可以检索即时结果,然后等待其他结果:

var processPage = function() {
    console.log("Get all units..");
    var units = this.evaluate(getUnits);
    var numberOfRows = this.getElementsInfo("table tr").length; // TODO: fix selector
    var externalUnits;
    this.waitFor(function test(){
        externalUnits = this.getGlobal("__externalRequestResults");
        for(var i = 0; i < numberOfRows; i++) {
            if (externalUnits[0][i] == null || externalUnits[1][i] == null) {
                return false
            }
        }
        return true;
    }, function _then(){
        allUnits.push(units);
        allUnits.push(externalUnits); // TODO: maybe a little differently

        if (!this.evaluate(isLastPage)) {
            //... as before
        } else{
            //... as before
        }
    }, terminate);
};

当两个附加数据列表的数量与表格的行数相同且所有数据都已填充时,将触发等待期间的结束。这会创建一个稀疏数组,并且Array#push()无法使用,因为不同的Ajax请求的排序顺序可能与发送顺序不同。

2。让浏览器处理请求

在第二种情况下,让CasperJS等待所有数据进入。挑战是编写一个执行此操作的检查功能。

var processPage = function() {
    console.log("Get all units..");
    var numberOfRows = this.getElementsInfo("table tr").length; // TODO: fix selector
    this.waitFor(function test(){
        var data1, data2;
        for(var i = 1; i <= numberOfRows; i++) {
            data1 = this.fetchText("table tr:nth-child("+i+") td:nth-child(4)") || "";
            data2 = this.fetchText("table tr:nth-child("+i+") td:nth-child(5)") || "";
            if (data1.trim() === "" || data2.trim() === "") {
                return false
            }
        }
        return true;
    }, function _then(){
        var units = this.evaluate(getUnits);
        allUnits.push(units);

        if (!this.evaluate(isLastPage)) {
            //... as before
        } else{
            //... as before
        }
    }, terminate);
};

现在,您甚至不需要在getUnits()内发出Ajax请求,只需收集所有静态信息。

不要忘记让失败的等待超时足够大,以便所有Ajax请求及时完成。例如,加载所有ajax请求的时间比正常时间大3或4倍。您可以使用全局casper.options.waitTimeout