原文链接:https://www.cnblogs.com/SnowPhoenix/p/15674155.html
从浏览器查看请求信息的时候,我们会发现有些请求有这样的特征:
?callback=jQuery17209708769256472376_1639142208410&jsonp=jsonp&_=1639142206313
的query;jQuery17209708769256472376_1639142208410({"code":0,"message":"","data":{"uid":123}})
,返回的字符串就是请求时url的callback调用了本身应该返回的数据;简单搜索一下,可以知道这个是jQuery在进行跨域请求的时候利用jsonp进行处理造成的现象。注意几点:
Referer
头,因为这是跨域请求,这也是为什么直接复制到Postman会失效的原因,Postman中加了Referer
头之后也能成功;_=1639142206313
其实就是时间戳,没什么好说的;但是这里有个问题,我们怎么模拟callback
的jQueryxxxxx
的生成呢?
既然我们知道这一套操作是跨域时的问题,那么我们的爬虫直接不进行跨域请求即可。即
callback
、jsonp
、_
(写时间戳的那个)这三个query;Referer
头,或者将Referer
头设置为当前请求的url的域名;当然,这样的解决方案不是很优雅,虽然我js水平很烂,也没学过jQuery,但我还是决定看看jQuery来看看这个这个jQueryxxxxx
是如何生成的。
用jsonp
和callback
来进行搜索,在jquery.js
文件里可以看到这样的代码:
// Default jsonp settings jQuery.ajaxSetup( { jsonp: "callback", jsonpCallback: function() { var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) ); this[ callback ] = true; return callback; } } );
前面oldCallbacks.pop()
应该是一个callback池,如果有空余的就不用产生新的了,不过我们需要关注的是新的是如何产生的:( jQuery.expando + "_" + ( nonce.guid++ ) )
继续找expando
,在jquery.js
文件里能够找到这样的代码:
jQuery.extend( { // Unique for each copy of jQuery on the page expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module isReady: true, error: function( msg ) { throw new Error( msg ); },
这就能够知道了,再看我们示例的jQuery17209708769256472376_1639142208410
,前面172
,就应该是版本号1.7.2
将非数字删去的结果,Math.random()
产生0-1间的浮点数,然后将小数点删去,得到了09708769256472376
,是十分符合的。
但是我通过多次在浏览器刷新,发现示例中的jQuery17209708769256472376_1639142208410
结尾1639142208410
显然是个时间戳,而不像是guid++
产生的结果。我目前看的源码是3.x
版本的源码,回去看看1.x
的源码,在jquery1.9.1.js
文件中可以看到相对应的代码:
// Default jsonp settings jQuery.ajaxSetup( { jsonp: "callback", jsonpCallback: function() { var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) ); this[ callback ] = true; return callback; } } );
这里用的是ajax_nonce++
,而往上找,就能找到它的定义:
ajax_nonce = jQuery.now(),
即为当前的时间戳。
import time import random class Constant: jQuery_Version = "1.7.2" def get_current_timestamp() -> int: return int(round(time.time() * 1000)) def jquery_mock_callback() -> str: "jQuery" + (Constant.jQuery_Version + str(random.random())).replace(".", "") + "_" + str(get_current_timestamp() - 1000)