等待来自 CasperJS 中选择 <select> 字段的 AJAX 响应

Waiting for AJAX response from a selection of a <select> field in CasperJS

我想用 CasperJS 抓取这个网站: http://www.agoda.com/hotel-des-arts-saigon-mgallery-collection/hotel/ho-chi-minh-city-vn.html?checkin=2015-11-14&los=2&adults=2&childs=0&rooms=1

我想通过 CasperJS 更改货币选项。但是,货币选项包含在 <select></select> 标签中,该标签未嵌入表单中。当我随后阅读 table 时,它显示的是旧货币的价格。当货币更改时,价格通过 AJAX 加载。

如何使用 CasperJS?

这是我的代码:

var casper = require("casper").create({
    verbose: true,
    logLevel: 'error',
    pageSettings: {
        loadImages: false,
    }
});

   var utils = require('utils');

    var url = 'http://www.agoda.com/hotel-des-arts-saigon-mgallery-collection/hotel/ho-chi-minh-city-vn.html?checkin=2015-11-14&los=4&adults=2&childs=0&rooms=1';

    var names = [];
    var prices = [];
    var currency = [];


    function getName() {
        var rows = document.querySelectorAll('table#room-grid-table tbody tr td:first-child .info-container .room-name span');
        return Array.prototype.map.call(rows, function(e) {
            return e.innerHTML;
        });
    }

    function getPrice() {
        var price = document.querySelectorAll('table#room-grid-table tbody tr td:nth-child(3) .price span.sellprice');
        return Array.prototype.map.call(price, function(e) {
            return e.innerHTML;
        });
    }

    casper.start(url, function() {
        this.echo(this.getTitle());
    });

    casper.then(function() {
        this.click('select[id="currency-options"]');
    });

    casper.then(function() {
        this.click('option[value="AED"]');
    });

    casper.then(function() {
        names = this.evaluate(getName);
        prices = this.evaluate(getPrice);
    });

    casper.then(function() {
        utils.dump(names);
        utils.dump(prices);
    })

    casper.run();

Select 框:

<select id="currency-options" data-selenium="room-currency">
    <option value="AED">Arab Emirates Dirham (AED)</option>
    <option value="ARS">Argentine Peso (ARS)</option>
    ...
    <option value="USD" selected="">US Dollar (USD)</option>
</select>

Table:

<table id="room-grid-table">
    <tbody data-selenium="room-tbody" data-prebook-url="/NewSite/en-us/Hotel/Prebook/929399">
        <tr>
            <td>...</td>
            <td>...</td>
            <td>
                <div class="price" data-selenium="price">
                    <span class="currency" data-selenium="price-currency">USD</span>
                    <span class="crossout show-cor-tooltip">288.75</span>
                    <span class="sellprice">187.11</span>
                </div>
            </td>
        </tr>
        <tr>...</tr>
        ...
    </tbody>
</table>

单击 select 框的选项字段几乎不起作用,因为它不是 PhantomJS 中的可单击 UI 元素。您必须通过 DOM 以编程方式 select 您想要的选项。例如,这可以通过将 selectElement.selectedIndex 属性 设置为正确的索引来完成。设置好后,需要在select元素上触发change事件,否则不会向服务器发送AJAX请求。

这是从我的 :

复制的代码
casper.selectOptionByValue = function(selector, valueToMatch){
    this.evaluate(function(selector, valueToMatch){
        var select = document.querySelector(selector),
            found = false;
        Array.prototype.forEach.call(select.children, function(opt, i){
            if (!found && opt.value.indexOf(valueToMatch) !== -1) {
                select.selectedIndex = i;
                found = true;
            }
        });
        // dispatch change event in case there is some kind of validation
        var evt = document.createEvent("UIEvents"); // or "HTMLEvents"
        evt.initUIEvent("change", true, true);
        select.dispatchEvent(evt);
    }, selector, valueToMatch);
};

更改货币设置后需要等待AJAX请求完成。这可以通过不同的方式完成:

  • 静态等待时间 有效,但等待时间可能比请求需要的时间长:

    casper.wait(5000);
    
  • 等待特定的 select 或 出现效率更高,因为一旦 select 或被发现。例如,您可以等待 table 中的货币文本更改。这可以使用 XPath 轻松完成:

    var x = require('casper').selectXPath;
    var currency = 'AED';
    ...
    casper.waitForSelector(x("//*[@id='room-grid-table']//*[@class='currency' and contains(text(), '"+currency+"')]"));
    
  • 您可以等待特定select或table中的货币的文本更改。如果更改是即时的,这将不起作用,因为 CasperJS 需要一点时间来接受更改。 20ms应该够了。

    casper.waitForSelectorTextChange("#room-grid-table .currency");
    
  • 等待 AJAX 响应,它通过查看所有收到的资源来工作:

    casper.waitForResource("Main/GetRoomTypeDetailList").wait(50);
    

版本 2 的完整脚本:

var casper = require("casper").create({
    verbose: true,
    logLevel: 'error',
    pageSettings: {
        loadImages: false,
    }
});

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

var url = 'http://www.agoda.com/hotel-des-arts-saigon-mgallery-collection/hotel/ho-chi-minh-city-vn.html?checkin=2015-11-14&los=4&adults=2&childs=0&rooms=1';

var names = [];
var prices = [];
var currency = [];


function getName() {
    var rows = document.querySelectorAll('table#room-grid-table tbody tr td:first-child .info-container .room-name span');
    return Array.prototype.map.call(rows, function(e) {
        return e.innerHTML;
    });
}

function getPrice() {
    var price = document.querySelectorAll('table#room-grid-table tbody tr td:nth-child(3) .price span.sellprice');
    return Array.prototype.map.call(price, function(e) {
        return e.innerHTML;
    });
}

casper.selectOptionByValue = function(selector, valueToMatch){
    this.evaluate(function(selector, valueToMatch){
        var select = document.querySelector(selector),
            found = false;
        Array.prototype.forEach.call(select.children, function(opt, i){
            if (!found && opt.value.indexOf(valueToMatch) !== -1) {
                select.selectedIndex = i;
                found = true;
            }
        });
        // dispatch change event in case there is some kind of validation
        var evt = document.createEvent("UIEvents"); // or "HTMLEvents"
        evt.initUIEvent("change", true, true);
        select.dispatchEvent(evt);
    }, selector, valueToMatch);
};

casper.start(url, function() {
    this.echo(this.getTitle());
});

var currency = 'AED';
casper.then(function() {
    this.selectOptionByValue('select[id="currency-options"]', currency);
});

casper.waitForSelector(x("//*[@id='room-grid-table']//*[@class='currency' and contains(text(), '"+currency+"')]"));

casper.then(function() {
    names = this.evaluate(getName);
    prices = this.evaluate(getPrice);
});

casper.then(function() {
    utils.dump(names);
    utils.dump(prices);
})

casper.run();