如何使用casperjs来捕获和处理来自XHR响应的数据?
网页上的数据是dynamic显示的,似乎检查HTML中每一个变化和提取数据是一项非常艰巨的任务,也需要我使用非常不可靠的XPath。 所以我希望能够从XHR
数据包中提取数据。
我希望能够从XHR
数据包中提取信息,并生成要发送到服务器的“XHR”数据包。 提取信息部分对我来说更重要,因为信息的发送可以通过使用casperjs自动触发html元素来轻松处理。
我附上我的意思的截图。
响应选项卡中的文本是我以后需要处理的数据。 (这个XHR响应已经从服务器收到。)
这是不容易的,因为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
。 这样你就不需要通话来完成通话。
你也可以在控制stream内部这样做(来源: 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请求
如果它不是无状态的 ,则需要replaceXMLHttpRequest的实现。 您将需要注入您自己的onreadystatechange
处理程序的实现,收集页面window
对象中的信息,然后在另一个evaluate
调用中收集它。
您可能需要查看sinon.js中的XHR faker,或者使用以下完整的XMLHttpRequest
代理(我在如何创buildXMLHttpRequest包装器/代理的方法3中对其进行build模):
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)
。
控制stream程如下所示:
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创build一个代理,以便每次在页面上使用它时,我都可以使用它。 您抓取的页面使用xhr.onreadystatechange
callback来接收数据。 代理是通过定义一个特定的setter函数完成的,它将接收到的数据写入页面上下文中的window.myAwesomeResponse
。 你唯一需要做的就是检索这个文本。
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();
-
或者在请求被接收之前(如果该function在请求被调用之前被注册)
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();
我可能迟到了,但是答案可能会帮助像我这样的人,他以后会陷入这个问题。
我必须从PhantomJS开始,然后转移到CasperJS,但最终与SlimerJS解决。 Slimer基于Phantom,与Casper兼容,并可以使用与“response.body”部分相同的onResponseReceived方法将响应主体发回给您。
参考: https : //docs.slimerjs.org/current/api/webpage.html#webpage-onresourcereceived
另外,您还可以直接下载内容并稍后进行操作。 以下是我用来检索JSON并将其保存在本地的脚本示例:
var casper = require('casper').create({ pageSettings: { webSecurityEnabled: false } }); var url = 'https://twitter.com/users/username_available?username=whatever'; casper.start('about:blank', function() { this.download(url, "hop.json"); }); casper.run(function() { this.echo('Done.').exit(); });