ajax - 如何使用 casperjs 捕获和处理 XHR 响应中的数据?

标签 ajax google-chrome web-scraping phantomjs casperjs

网页上的数据是动态显示的,似乎检查 html 中的每个更改并提取数据是一项非常艰巨的任务,并且还需要我使用非常不可靠的 XPath。所以我希望能够从 XHR 数据包中提取数据。

我希望能够从XHR数据包中提取信息并生成要发送到服务器的“XHR”数据包。 提取信息部分对我来说更重要,因为可以通过使用 casperjs 自动触发 html 元素来轻松处理信息的发送。

我附上了我的意思的屏幕截图。 enter image description here

响应选项卡中的文本是我之后需要处理的数据。 (此 XHR 响应已从服务器收到。)

最佳答案

这并不容易实现,因为 resource.received 事件处理程序仅提供 urlheadersstatus 等元数据,而不是实际的数据。底层 phantomjs 事件处理程序的行为方式相同。

<小时/>

无状态 AJAX 请求

如果ajax调用无状态,您可以重复请求

casper.on("resource.received", function(resource){
    // somehow identify this request, here: if it contains ".json"
    // it also also only does something when the stage is "end" otherwise this would be executed two times
    if (resource.url.indexOf(".json") != -1 && resource.stage == "end") {
        var data = casper.evaluate(function(url){
            // synchronous GET request
            return __utils__.sendAJAX(url, "GET");
        }, resource.url);
        // do something with data, you might need to JSON.parse(data)
    }
});
casper.start(url); // your script

您可能想要将事件监听器添加到 resource.requested 。这样您就不需要通过其他方式来完成调用。

您也可以在控制流内部执行此操作,如下所示(来源:A: CasperJS waitForResource: how to get the resource i've waited for):

casper.start(url);

var res, resData;
casper.waitForResource(function check(resource){
    res = resource;
    return resource.url.indexOf(".json") != -1;
}, function then(){
    resData = casper.evaluate(function(url){
        // synchronous GET request
        return __utils__.sendAJAX(url, "GET");
    }, res.url);
    // do something with the data here or in a later step
});

casper.run();
<小时/>

有状态 AJAX 请求

如果它不是无状态,则需要替换 XMLHttpRequest 的实现。您需要注入(inject)自己的 onreadystatechange 处理程序实现,收集页面 window 对象中的信息,然后在另一个 evaluate 调用中收集它.

您可能需要查看 XHR faker in sinon.js 或使用以下完整的 XMLHttpRequest 代理(我根据 How can I create a XMLHttpRequest wrapper/proxy? 中的方法 3 对其进行建模):

function replaceXHR(){
    (function(window, debug){
        function args(a){
            var s = "";
            for(var i = 0; i < a.length; i++) {
                s += "\t\n[" + i + "] => " + a[i];
            }
            return s;
        }
        var _XMLHttpRequest = window.XMLHttpRequest;

        window.XMLHttpRequest = function() {
            this.xhr = new _XMLHttpRequest();
        }

        // proxy ALL methods/properties
        var methods = [ 
            "open", 
            "abort", 
            "setRequestHeader", 
            "send", 
            "addEventListener", 
            "removeEventListener", 
            "getResponseHeader", 
            "getAllResponseHeaders", 
            "dispatchEvent", 
            "overrideMimeType"
        ];
        methods.forEach(function(method){
            window.XMLHttpRequest.prototype[method] = function() {
                if (debug) console.log("ARGUMENTS", method, args(arguments));
                if (method == "open") {
                    this._url = arguments[1];
                }
                return this.xhr[method].apply(this.xhr, arguments);
            }
        });

        // proxy change event handler
        Object.defineProperty(window.XMLHttpRequest.prototype, "onreadystatechange", {
            get: function(){
                // this will probably never called
                return this.xhr.onreadystatechange;
            },
            set: function(onreadystatechange){
                var that = this.xhr;
                var realThis = this;
                that.onreadystatechange = function(){
                    // request is fully loaded
                    if (that.readyState == 4) {
                        if (debug) console.log("RESPONSE RECEIVED:", typeof that.responseText == "string" ? that.responseText.length : "none");
                        // there is a response and filter execution based on url
                        if (that.responseText && realThis._url.indexOf("whatever") != -1) {
                            window.myAwesomeResponse = that.responseText;
                        }
                    }
                    onreadystatechange.call(that);
                };
            }
        });

        var otherscalars = [
            "onabort",
            "onerror",
            "onload",
            "onloadstart",
            "onloadend",
            "onprogress",
            "readyState",
            "responseText",
            "responseType",
            "responseXML",
            "status",
            "statusText",
            "upload",
            "withCredentials",
            "DONE",
            "UNSENT",
            "HEADERS_RECEIVED",
            "LOADING",
            "OPENED"
        ];
        otherscalars.forEach(function(scalar){
            Object.defineProperty(window.XMLHttpRequest.prototype, scalar, {
                get: function(){
                    return this.xhr[scalar];
                },
                set: function(obj){
                    this.xhr[scalar] = obj;
                }
            });
        });
    })(window, false);
}

如果您想从一开始就捕获 AJAX 调用,则需要将其添加到第一个事件处理程序中

casper.on("page.initialized", function(resource){
    this.evaluate(replaceXHR);
});

在需要时评估(replaceXHR)

控制流程如下所示:

function replaceXHR(){ /* from above*/ }

casper.start(yourUrl, function(){
    this.evaluate(replaceXHR);
});

function getAwesomeResponse(){
    return this.evaluate(function(){
        return window.myAwesomeResponse;
    });
}

// stops waiting if window.myAwesomeResponse is something that evaluates to true
casper.waitFor(getAwesomeResponse, function then(){
    var data = JSON.parse(getAwesomeResponse());
    // Do something with data
});

casper.run();

如上所述,我为 XMLHttpRequest 创建了一个代理,以便每次在页面上使用它时,我都可以用它做一些事情。您抓取的页面使用 xhr.onreadystatechange 回调来接收数据。代理是通过定义一个特定的 setter 函数来完成的,该函数将接收到的数据写入页面上下文中的 window.myAwesomeResponse 中。您唯一需要做的就是检索此文本。

<小时/>

JSONP 请求

如果您知道前缀(使用加载的 JSON 调用的函数,例如 insert({"data":["Some", "JSON", "here"] 的话,为 JSONP 编写代理就更容易了,“id”:“asdasda”))。您可以在页面上下文中覆盖插入

  1. 页面加载后

    casper.start(url).then(function(){
        this.evaluate(function(){
            var oldInsert = insert;
            insert = function(json){
                window.myAwesomeResponse = json;
                oldInsert.apply(window, arguments);
            };
        });
    }).waitFor(getAwesomeResponse, function then(){
        var data = JSON.parse(getAwesomeResponse());
        // Do something with data
    }).run();
    
  2. 或在收到请求之前(如果该函数是在调用请求之前注册的)

    casper.on("resource.requested", function(resource){
        // filter on the correct call
        if (resource.url.indexOf(".jsonp") != -1) {
            this.evaluate(function(){
                var oldInsert = insert;
                insert = function(json){
                    window.myAwesomeResponse = json;
                    oldInsert.apply(window, arguments);
                };
            });
        }
    }).run();
    
    casper.start(url).waitFor(getAwesomeResponse, function then(){
        var data = JSON.parse(getAwesomeResponse());
        // Do something with data
    }).run();
    

关于ajax - 如何使用 casperjs 捕获和处理 XHR 响应中的数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24555370/

相关文章:

python selenium 输入搜索查询然后等待

javascript - 我想使用 javascript 或 jquery 更改内容

php - 有什么安全的方法可以允许跨站点 AJAX 请求吗?

ios - WebRTC 在 Chrome 21 for iOS 中可用吗?

android - Chrome 远程调试无法发现任何 USB 设备

爬虫包: Rcrawler not crawling some websites

javascript - 如何使用 id 从 javascript 访问 HTML 元素,该 html 元素属于另一个 html 文档

c# - 文本更改事件后如何保持对文本框的关注

css - Chrome 在 z 索引方面是否正在退化(或者我做错了什么)?

python - 递归 Scrapy 爬取问题