当前位置: 代码迷 >> JavaScript >> 同步CasperJS操作期间的异步调用 1.自己发送请求 2.让浏览器处理请求
  详细解决方案

同步CasperJS操作期间的异步调用 1.自己发送请求 2.让浏览器处理请求

热度:29   发布时间:2023-06-13 11:50:18.0

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

这是我试图完成的:

  1. 登录
  2. 获得所有单位
  3. 解析他们的细节
  4. (我的问题)2个单元的详细信息由ajax调用提供
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
  • 所有者
  • BEINGFOLLOWED(对帖子的链接进行的自动ajax调用)
  • PLACEDABIDON(对帖子的链接进行的自动ajax调用)

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

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

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

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

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

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

1.自己发送请求

您需要在页面上下文内部(在evaluate()内部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倍。 您可以使用全局 。

  相关解决方案