继上一篇。
Selenium虽然模拟用户操作显得比较真实,但毕竟动用了浏览器,并按手动操作执行,效率上显然是极低的,而且耗用巨大系统资源(Chrome
01
—
商品页面
要到达商品页面,就先在浏览器上登陆,通过下方的地址进入登录页面,接码即可。
https://m.laiyifen.com/my/login?loginFrom=%2Fmy%2Findex
然后就来到了商品页面,如下图:
02
—
抓包分析
浏览器按下F12,切换到“网络”选项卡,F5刷新页面,发现出现一大堆数据包。在下方图片这条发现了该商品的详细信息:
与第一步中的商品页面一毛一样,其中的stockStatus就是我们想要的库存状态,获取该值就可以判断有没有库存。
再看这个数据包的具体链接:
https://openapi.laiyifen.com/community-app-api/v1/community/product/secret?times=1677133674158&platform=2&companyId=30&platformId=3&deviceId=d02e61f8-4563-4def-a4c2-e26256314e56&channelSkuUnitId=115812******
看起来就是调用了官方的api去获取信息,用工具测试一下,哇嘎,非法。
带上协议头测试一下,哇靠,时间戳验证失败
后面把网址和协议头的时间戳都替换了,发现还是验证失败或非法
多次发包后,发现协议头的x-co-sign会变化,所以重点就转移到分析这个参数的来源了。
03
—
逆向
虽然不情愿也不懂,那都到这一步了,也得去试试看,万一被我找出来x-co-sign怎么来的呢
果断搜索x-co-sign,定位到js文件里具体位置,如下图:
切换到“来源”选项卡,在该行位置下断点:
刷新页面,得到下面的结果:
可以看出来核心代码在4062-4069行,反推回去,就是:
变量f的值赋给x-co-sign;
变量f是由变量h转换的字符串;
变量h是对变量d进行HmacSHA1加密,秘钥等下找;
变量d就在4062行了。
我们将变量d输出:
GET\n/community-app-api/v1/community/product/secret\nchannelSkuUnitId=115812121*****&companyId=30&deviceId=d02e61f8-4563-4def-a4c2-e26256314e56&platform=2&platformId=3×=1677135596412\nx-co-client:70923143D9C04D1390617B676BF4ACA7\nx-co-timestamp:1677135596663
分析发现它就是把x-co-client,x-co-timestamp,SkuUnitId等参数组成,都很简单了,时间戳就按发包的时间,SkuUnitId就是商品id了,所以构造这个变量d很简单了。
变量d有了就差秘钥了,直接打印看看,看起来是明文。
为了验证,搜索secretKey,发现就是个常量,那就真是明文了,搞定了。
04
—
效果及代码
最终效果就是这样了:
实际应用时,x-co-client、deviceId和clientInfo 等这些跟设备有关的信息需要相应更改,代码供参考。
import requests,json,time
from urllib import parse
from hashlib import sha1
import base64
import hmac
p_token= '' #push+
skuid = ["11581212********","11581212*********","1158121********"]#DD,YH,HM
def hash_hmac(key, code, sha1):
hmac_code = hmac.new(key.encode(), code.encode(), sha1).digest()
return base64.b64encode(hmac_code).decode()
n = 1 #循环次数
DDok = 0 #有货次数
HMok = 0 #有货次数
YHok = 0 #有货次数
a = input('输入 延时时间(s) 回车后继续:')
if a == "":
延时时间 = 3 # 延时时间
print("未设置,按默认延时时间(s):"+str(延时时间))
else:
延时时间 = int(a) # 延时时间
print("延时时间(s):" + str(延时时间))
print('输入欲监控的商品[DD,YH,HM]对应位置为1,不监控为0,以.隔开。如1.0.1 表示监控DD,HM')
a = input('输入数字回车后继续:')
if a == "":
执行 = [1, 1, 1]
print("未设置,按默认监控所有")
else:
# 将输入每个数以.隔开做成数组
执行 = [int(k) for k in a.split(".")]
while True:
t = int(time.time()*1000)#13时间戳
t = str(t)
#print(t)
i = 0
for i in range(3):
#x-co-sign明文
k = f'GET\n/community-app-api/v1/community/product/secret\nchannelSkuUnitId={skuid[i]}&companyId=30&deviceId=9f05fde7-7a72-4614-b6cc-bb1ad6f39edd&platform=2&platformId=3×={t}\nx-co-client:70923143D9C04D1390617B676BF4ACA7\nx-co-timestamp:{t}'
#print(k)
x_co_sign = hash_hmac('SECRETKEY-9385D4D1F8864252AD2D19', k, sha1)#HMACSHA1加密
#print(x_co_sign)
url = f"https://openapi.laiyifen.com/community-app-api/v1/community/product/secret?times={t}&platform=2&companyId=30&platformId=3&deviceId=9f05fde7-7a72-4614-b6cc-bb1ad6f39edd&channelSkuUnitId={skuid[i]}"
headers = {
"Host": "openapi.laiyifen.com",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"X-Requested-With": "XMLHttpRequest",
"x-co-client": "70923143D9C04D1390617B676BF4ACA7",
"User-Agent": '''Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36''',
"clientInfo": "%7B%22platformId%22:2,%22clientSystem%22:%22H5%22%7D",
"Accept": "application/json",
"x-co-sign": x_co_sign,
"x-co-timestamp": t,
"ut": "4bc91c70bcc9f84bceb2e95f7970bcc451",
"Origin": "http://m.laiyifen.com",
"Sec-Fetch-Site": "cross-site",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Referer": "https://m.laiyifen.com/",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9"}
try:
if 执行[i] == 1:
response = requests.get(url=url, headers=headers)
#print(response.text)
if response.text.find("stockStatus") != -1:
aaa = json.loads(response.text)
#print(aaa['data']['productName'],aaa['data']['promotionPrice'],aaa['data']['stockStatus'])
if aaa['data']['stockStatus'] != "0":
#print(aaa['data']['productName']+"————有货")
if i == 0:
DDok = DDok + 1
print("DD 有货")
if DDok <= 3:
t = str(time.time())
content = parse.quote_plus(t)
title = parse.quote_plus('DD 有货')
requests.get(f'http://www.pushplus.plus/send?token={p_token}&title={title}&content={content}')
elif i == 1:
YHok = YHok + 1
print("YH 有货")
if YHok <= 3:
t = str(time.time())
content = parse.quote_plus(t)
title = parse.quote_plus('YH 有货')
requests.get(f'http://www.pushplus.plus/send?token={p_token}&title={title}&content={content}')
else:
HMok = HMok + 1
print("HM 有货")
if HMok <= 3:
t = str(time.time())
content = parse.quote_plus(t)
title = parse.quote_plus('HM 有货')
requests.get(f'http://www.pushplus.plus/send?token={p_token}&title={title}&content={content}')
else:
#print(aaa['data']['productName'])
if i == 0:
print("DD 已售罄")
elif i == 1:
print("YH 已售罄")
else:
print("HM 已售罄")
else:
print(response.text,11111)
except Exception:
print('异常,未获取到数据')
i = i + 1
time.sleep(0.3)#每次运行3个商品间隔0.3秒
print(f"已运行{n}次")
if DDok+HMok+YHok >= 6: # 总共提醒了6次就结束循环
print("已监控到6次,结束运行")
break
n = n + 1
time.sleep(延时时间)
input('输入回车后继续:')
以上代码及思路仅供学习参考,请勿用于非法用途。
如果喜欢,欢迎打赏支持,让我更有动力。
- End -
更多精彩文章
点击下方名片关注【偶尔敲代码】
点亮小花 让更多人了解