有个网址是喜欢股票的都喜欢看的,但他的数据没有混淆,接口是直接可以获取的,但是不能确定数据的来源,这里就单独对如何获取数据来源做一个单独讲解,某财富网,是一个很好的锻炼debug能力的实战项目。
01
网址:
aHR0cDovL3F1b3RlLmVhc3Rtb25leS5jb20vY29uY2VwdC9zejAwMjEwOC5odG1s
接口:
aHR0cDovL3B1c2gyaGlzLmVhc3Rtb25leS5jb20vYXBpL3F0L3N0b2NrL2tsaW5lL2dldD9jYj1qUXVlcnkxMTI0MDg3MDQwMTU1NDQxMzU2NjRfMTY1ODMyMTczMDQ3MCZmaWVsZHMxPWYxLGYyLGYzLGY0LGY1LGY2JmZpZWxkczI9ZjUxLGY1MixmNTMsZjU0LGY1NSxmNTYsZjU3LGY1OCxmNTksZjYwLGY2MSZ1dD03ZWVhM2VkY2FlZDczNGJlYTljYmZjMjQ0MDllZDk4OSZrbHQ9MTAxJmZxdD0xJnNlY2lkPTAuMDAyMTA4JmJlZz0wJmVuZD0yMDUwMDAwMCZfPTE2NTgzMjE3MzA0OTA=
逆向参数:
– cb
– ut
02
01
抓包找接口
我们要获取这里的数据,直接search里面的几个数据并不能直接找到,(举例)我们尝试search“平均成本”这个字段,然后下断确定即可。
可以看到是从A.percentChips[70]这获取的,所以要去看A对象的数据。
直接找上文,看看在哪里定义的A对象或者是传入的A参数。
在5462行发现该对象是调用一个方法获取的数据,直接去查看this.cyqCalc这个对象数据中是否有标志性的数据用于搜索。
比如就获取第一个数据中的“161895008.37”
今天这个是反推法,用结果推接口位置。
可以找到接口数据了data里一样,说明这个接口是我们要的数据获取接口。
然后去寻找请求参数是否加密需要逆向。
02
参数浅析
浅析一下:
cb:jQuery里的数据+”_”+13位时间戳
ut:看着是个加密的
secId:是”0.”+股票ID
end:不知道是否固定
_:是13位时间戳
03
开始逆向参数
我们可以直接搜索这个api请求,因为这么长+参数的get请求,当然也可以通过XHR下断,小猜了一下就是可以直接搜索到的,所以我们直接搜索“/api/qt/stock/kline/get”。
在搜索里寻找一下。
发现请求参数除了时间戳和cb没有其他都对得上,nice啊,这说明就是这断源码直接下断就好了,ut像是一个函数获取的,先看ut参数。
鼠标放上去跳转到函数位置查看一下,怎么生成出来的。
发现是一个返回函数数据的,我们将断点打进来,查看一下走的是哪个表达式。
发现走的是第二个获取这个对象的name是”quoteut”的数据返回的,那再往里点看一下。
发现返回的是一个方法名是“quoteut”的固定值啊,那可以确定他不会变了。
这个值可以写死了。
看看end参数咋来的,返回去调试看看。
发现是写死的e5=5个0,又对上了,发现没啥要逆向的参数了,都是写死的。
我们看一下cb=?这个为啥一直都没有设置但是请求过程中却有这个数据呢,这个一般在jQuery的文件里,在请求的时候会自动插入。
这里有个小技巧,就是搜索“extend”这个关键词,一般是由“jQuery.extend”得来,但是有时候是混淆的对象,有时候是$所以直接搜extend查看一下。
这里找到参数是咋生成的啦,还有一个m是咱不知道的,往上文找一下。
发现是写死的”1.12.4″,放心的生成就好了。
04
Python爬虫
import requests
import time
import random
headers = {
'Cookie': 'qgqp_b_id=96d6e6751a2f0e0ef83bde7b8768bab7; HAList=a-sz-002108-%u6CA7%u5DDE%u660E%u73E0; em-quote-version=topspeed; st_si=39126136647263; st_asi=delete; st_pvi=01981213275515; st_sp=2022-06-09%2011%3A36%3A06; st_inirUrl=https%3A%2F%2Fwww.baidu.com%2Flink; st_sn=4; st_psi=20220720192804212-113200301202-3920776165',
'Host': 'push2his.eastmoney.com',
'Referer': 'http://quote.eastmoney.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 Edg/101.0.1210.39'
}
def get_timestamp():
return int(round(time.time() * 1000))
def get_cb():
return ('jQuery' + '1.12.4' + str(random.random())).replace('.', '') + '_' + str(get_timestamp())
def get_resposne_by_id(id):
url = "http://push2his.eastmoney.com/api/qt/stock/kline/get"
params = {
'cb': get_cb(),
'fields1': 'f1,f2,f3,f4,f5,f6',
'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
'ut': '7eea3edcaed734bea9cbfc24409ed989',
'klt': '101',
'fqt': '1',
'secid': '0.' + id,
'beg': '0',
'end': '20500000'
}
response = requests.get(url=url, headers=headers, params=params)
print(response.content)
if __name__ == "__main__":
get_resposne_by_id('002108')
获取的结果:
可以看到我们获取成功了,但是结果不是我们要的JSON数据,处理一下结果,直接将我们不要的结果截掉即可,当然前面开始的位数是固定的,所以不要怕,直接用索引去掉。
responseTxt = response.text[42:].lstrip("(").rstrip(');')
responseJson = json.loads(responseTxt)
这两句将结果从resposne对象转换为str类型并且去除多余的字符干扰,可以直接转换为JSON格式,方便调用。
由于在对源码分析完毕后,数据会由[“…”,”…”]变成每个时间点一个数组[[…],[…]]一数组中有11个字符,在取值会按索引取值,并且会对8-10的数据位置进行调换,修改后的代码如下。
dataReturn = []
for kline in data['klines']:
dataObj =[]
klines = str(kline).split(',')
for kline in klines:
dataObj.append(str(kline))
dataObj[7] = dataObj[7] + '%'
temp = dataObj[8]
dataObj[8] = dataObj[9]
dataObj[9] = dataObj[10]
dataObj[10] = temp
dataReturn.append(dataObj)
data['klines'] = dataReturn
在数据爬虫上解决完成后,就要求对JS来源代码进行抠取了。
05
JS数据生成
数据来源位置是如图所示生成的对象的属性或者方法。
可以看到此处,下个断点即可进入到方法中。
然后开始抠代码,代码要抠全,从function(){开始到}结束,要仔细查看是否抠全,然后带有this的属性我们本地没有,就需要去补环境,结果发现klinedata是我们处理后的数据,其他两个一个是150一个是120固定值。
这里直接提供源码,klinedata直接从python打印复制进去的。
这样补一下即可,然后将函数名定义之后,将this.xx属性改成我们上面自定义的名字即可,字数不能超,就不放测试源码了。
然后a(3701)调用查看结果。
有了结果对的上,就可以直接用python调用了。
使用的PyExecJS库,调用js代码执行,将结果输出,然后调用结果即可。
def getData(data):
result = ''
with open('./index.js', encoding='utf-8') as file:
jsData = execjs.compile(file.read())
result = jsData.call("a", 3701, data)
return result
先用complie编译,用call调用传参,这个3701是按序号改的即可。
最后获取输出结果即可:
def get_chengben(result):
return result['avgCost']
def get_huoli_billi(result):
return 100 * result['benefitPart']
打印结果看看是否成功:
print('获利比例' + str(get_huoli_billi(result)))
print('平均成本' + str(get_chengben(result))
03
var fator = 150
var range = 120
function a(t, klinedata){
var e = 0
, o = 0
, r = fator
, i = range ? Math.max(0, t - range + 1) : 0
, n = klinedata.slice(i, Math.max(1, t + 1))
if (0 === n.length)
throw "invaild index";
for (var a = 0; a < n.length; a++)
var s = n[a]
, e = e ? Math.max(e, +s[3]) : +s[3]
, o = o ? Math.min(o, +s[4]) : +s[4];
for (var l = Math.max(.01, (e - o) / (r - 1)), h = [], a = 0; a < r; a++)
h.push(+(o + l * a).toFixed(2));
for (var d = function(t) {
for (var e = [], i = 0; i < t; i++)
e.push(0);
return e
}(r), a = 0; a < n.length; a++) {
for (var c = n[a], p = +c[1], g = +c[2], A = +c[3], u = +c[4], f = (p + g + A + u) / 4, m = Math.min(1, c[8] / 100 || 0), C = Math.floor((A - o) / l), y = Math.ceil((u - o) / l), v = [A == u ? r - 1 : 2 / (A - u), Math.floor((f - o) / l)], x = 0; x < d.length; x++)
d[x] *= 1 - m;
if (A == u)
d[v[1]] += v[0] * m / 2;
else
for (var I = y; I <= C; I++) {
var b = o + l * I;
b <= f ? Math.abs(f - u) < 1e-8 ? d[I] += v[0] * m : d[I] += (b - u) / (f - u) * v[0] * m : Math.abs(A - f) < 1e-8 ? d[I] += v[0] * m : d[I] += (A - b) / (A - f) * v[0] * m
}
}
for (var w = +klinedata[t][2], k = 0, a = 0; a < r; a++) {
var M = +d[a].toPrecision(12);
k += M
}
var T = new function() {
this.x = arguments[0],
this.y = arguments[1],
this.benefitPart = arguments[2],
this.avgCost = arguments[3],
this.percentChips = arguments[4],
this.computePercentChips = function(t) {
if (1 < t || t < 0)
throw 'argument "percent" out of range';
var e = [(1 - t) / 2, (1 + t) / 2]
, i = [R(k * e[0]), R(k * e[1])];
return {
priceRange: [i[0].toFixed(2), i[1].toFixed(2)],
concentration: i[0] + i[1] === 0 ? 0 : (i[1] - i[0]) / (i[0] + i[1])
}
}
,
this.getBenefitPart = function(t) {
for (var e = 0, i = 0; i < r; i++) {
var n = +d[i].toPrecision(12);
o + i * l <= t && (e += n)
}
return 0 == k ? 0 : e / k
}
}
;
return T.x = d,
T.y = h,
T.benefitPart = T.getBenefitPart(w),
T.avgCost = R(.5 * k).toFixed(2),
T.percentChips = {
90: T.computePercentChips(.9),
70: T.computePercentChips(.7)
},
T;
function R(t) {
for (var e = 0, i = 0, n = 0; n < r; n++) {
var a = +d[n].toPrecision(12);
if (t < i + a) {
e = o + n * l;
break
}
i += a
}
return e
}
}
04
Python源码
import requests
import time
import random
import json
import execjs
headers = {
'Cookie': 'qgqp_b_id=96d6e6751a2f0e0ef83bde7b8768bab7; HAList=a-sz-002108-%u6CA7%u5DDE%u660E%u73E0; em-quote-version=topspeed; st_si=39126136647263; st_asi=delete; st_pvi=01981213275515; st_sp=2022-06-09%2011%3A36%3A06; st_inirUrl=https%3A%2F%2Fwww.baidu.com%2Flink; st_sn=4; st_psi=20220720192804212-113200301202-3920776165',
'Host': 'push2his.eastmoney.com',
'Referer': 'http://quote.eastmoney.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36 Edg/101.0.1210.39'
}
def get_timestamp():
return int(round(time.time() * 1000))
def get_cb():
return ('jQuery' + '1.12.4' + str(random.random())).replace('.', '') + '_' + str(get_timestamp())
def get_resposne_by_id(id):
url = "http://push2his.eastmoney.com/api/qt/stock/kline/get"
params = {
'cb': get_cb(),
'fields1': 'f1,f2,f3,f4,f5,f6',
'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
'ut': '7eea3edcaed734bea9cbfc24409ed989',
'klt': '101',
'fqt': '1',
'secid': '0.' + id,
'beg': '0',
'end': '20500000'
}
response = requests.get(url=url, headers=headers, params=params)
responseTxt = response.text[42:].lstrip("(").rstrip(');')
data = json.loads(responseTxt)['data']
dataReturn = []
for kline in data['klines']:
dataObj =[]
klines = str(kline).split(',')
a8 = ''
a9 = ''
a10 = ''
for i in range(len(klines)):
if i == 7:
dataObj.append(str(klines[i]) + '%')
elif i == 8:
a9 = str(klines[i])
elif i == 9:
a10 = str(klines[i])
elif i == 10:
a8 = str(klines[i])
else:
dataObj.append(str(klines[i]))
dataObj.append(a8)
dataObj.append(a9)
dataObj.append(a10)
dataReturn.append(dataObj)
data['klines'] = dataReturn
return data
def getData(data):
result = ''
with open('./index.js', encoding='utf-8') as file:
jsData = execjs.compile(file.read())
result = jsData.call("a", 3701, data)
return result
def get_chengben(result):
return result['avgCost']
def get_huoli_billi(result):
return 100 * result['benefitPart']
if __name__ == "__main__":
data = get_resposne_by_id('002108')
result = getData(data['klines'])
print('获利比例' + str(get_huoli_billi(result)))
print('平均成本' + str(get_chengben(result)))
喜欢就点个关注一起进步吧
此文章仅做学习之用,请勿做违法违纪之事。
声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/218398.html