原文链接:https://www.cnblogs.com/SnowPhoenix/p/15674155.html
问题描述
从浏览器查看请求信息的时候,我们会发现有些请求有这样的特征:
- 包含一个类似于
?callback=jQuery17209708769256472376_1639142208410&jsonp=jsonp&_=1639142206313
的query; - 返回的JSON数据
jQuery17209708769256472376_1639142208410({"code":0,"message":"","data":{"uid":123}})
,返回的字符串就是请求时url的callback调用了本身应该返回的数据; - 简单将这个url复制到Postman请求会失败;
简单搜索一下,可以知道这个是jQuery在进行跨域请求的时候利用jsonp进行处理造成的现象。注意几点:
- 需要设置
Referer
头,因为这是跨域请求,这也是为什么直接复制到Postman会失效的原因,Postman中加了Referer
头之后也能成功; -
_=1639142206313
其实就是时间戳,没什么好说的; - 想要获得原始的JSON数据,在获得返回数据后,直接前后的JSONP的padding删去即可;
但是这里有个问题,我们怎么模拟callback
的jQueryxxxxx
的生成呢?
参考
- Github | jquery/jquery;
- 从上面仓库的Release中下载的源码;
替代解决方案
既然我们知道这一套操作是跨域时的问题,那么我们的爬虫直接不进行跨域请求即可。即
- 删去
callback
、jsonp
、_
(写时间戳的那个)这三个query; - 删去
Referer
头,或者将Referer
头设置为当前请求的url的域名;
当然,这样的解决方案不是很优雅,虽然我js水平很烂,也没学过jQuery,但我还是决定看看jQuery来看看这个这个jQueryxxxxx
是如何生成的。
jQuery源码
用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(),
即为当前的时间戳。
python代码
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)