【问题标题】:Asynchronous call during a synchronous CasperJS operation同步 CasperJS 操作期间的异步调用
【发布时间】:2015-07-17 20:37:41
【问题描述】:

在遇到麻烦(第一个计时器 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;

};

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

编辑

登录后页面显示一个表格,我得到的表格是这样的

  • 身份证
  • 所有者
  • 街道
  • 被关注 (对带有帖子的链接进行的自动 ajax 调用)
  • PLACEDABIDON(对与 发布)

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

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

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

最重要的是,在所有单元都有其详细信息之后,它应该继续应用程序的逻辑......

【问题讨论】:

    标签: javascript asynchronous phantomjs screen-scraping casperjs


    【解决方案1】:

    由于 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

    【讨论】:

    • 目前还不清楚如何调用另一个页面......因为在函数内部调用 Casper 不起作用......你能展示一下你是如何做到的吗?
    • 我的印象是您正在进行 ajax 调用。你现在想要的是完全不同的东西。
    • 加载的页面有一些我需要的细节,但是 2 是通过 Ajax 从另一个页面(在网络中签入)调用的,也许我需要再等一会儿才能得到它们?所以也许我需要你说的异步回调吗?
    • 那么页面本身会执行这些请求吗?如果是这样,那么您将需要以某种方式捕获结果。 PhantomJS 可以观察请求,但不会暴露这些请求的内容。
    • 登录后,页面立即显示每个单元的 6 个字段,但有时 2 个字段不显示,因为它们是由 ajax 调用获取的(有时速度很慢)。所以我的问题是如何获取它们与 casperjs。在 php curl 中,我对每个单元的 url(ajax 调用)进行了同步调用以填写详细信息。以确保我得到它们而不是等待它们显示。但这很慢,因为我必须等待到达单位才能继续下一个..所以我想异步执行。但是在继续其余流程之前需要完成所有单元(我使用数据逻辑)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 2017-09-09
    • 2023-03-16
    • 2019-05-27
    相关资源
    最近更新 更多