Ответ 1
Это невозможно, потому что обработчик событий resource.received
предоставляет только метаданные, такие как url
, headers
или status
, но не фактические данные. Основной обработчик событий 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
. Таким образом, вам не нужно использовать способ завершения вызова.
Вы также можете сделать это прямо внутри потока управления, как это (source: A: CasperJS waitForResource: как получить ресурс, который я ждал):
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. Вам нужно будет ввести собственную реализацию обработчика onreadystatechange
, собрать информацию в объекте window
, а затем собрать ее в другом вызове evaluate
.
Вы можете посмотреть XHR faker в файле sinon.js или использовать следующий полный прокси для XMLHttpRequest
(я смоделировал его после метода 3 из Как я могу создать обертку/прокси XMLHttpRequest?):
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);
});
или evaluate(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
для приема данных. Проксирование выполняется путем определения конкретной функции сеттера, которая записывает полученные данные в window.myAwesomeResponse
в контексте страницы. Единственное, что вам нужно сделать, это получить этот текст.
Запрос JSONP
Написание прокси для JSONP еще проще, если вы знаете префикс (функция для вызова с загруженным JSON, например, insert({"data":["Some", "JSON", "here"],"id":"asdasda")
). Вы можете перезаписать insert
в контексте страницы
-
после загрузки страницы
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();
-
или до получения запроса (если функция зарегистрирована непосредственно перед вызовом запроса)
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();