Selenium虽然模拟用户操作显得比较真实,但毕竟动用了浏览器,并按手动操作执行,效率上显然是极低的,而且耗用巨大系统资源(Chrome

唯品会监控 唯品会监控库存_唯品会监控

)。所以总需要更直接高效的方式,那就是抓包再发包了。



01

商品页面

要到达商品页面,就先在浏览器上登陆,通过下方的地址进入登录页面,接码即可。

https://m.laiyifen.com/my/login?loginFrom=%2Fmy%2Findex

唯品会监控 唯品会监控库存_bc_02

然后就来到了商品页面,如下图:

唯品会监控 唯品会监控库存_bc_03

02

抓包分析

浏览器按下F12,切换到“网络”选项卡,F5刷新页面,发现出现一大堆数据包。在下方图片这条发现了该商品的详细信息:

唯品会监控 唯品会监控库存_唯品会监控_04

与第一步中的商品页面一毛一样,其中的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去获取信息,用工具测试一下,哇嘎,非法

唯品会监控 唯品会监控库存_json_05


唯品会监控 唯品会监控库存_时间戳_06

带上协议头测试一下,哇靠,时间戳验证失败

唯品会监控 唯品会监控库存_bc_07

唯品会监控 唯品会监控库存_bc_08

后面把网址和协议头的时间戳都替换了,发现还是验证失败或非法

唯品会监控 唯品会监控库存_bc_09

多次发包后,发现协议头的x-co-sign会变化,所以重点就转移到分析这个参数的来源了。

03

逆向

虽然不情愿也不懂,那都到这一步了,也得去试试看,万一被我找出来x-co-sign怎么来的呢

唯品会监控 唯品会监控库存_json_10

果断搜索x-co-sign,定位到js文件里具体位置,如下图:

唯品会监控 唯品会监控库存_bc_11

切换到“来源”选项卡,在该行位置下断点:

唯品会监控 唯品会监控库存_唯品会监控_12

刷新页面,得到下面的结果:

唯品会监控 唯品会监控库存_时间戳_13

可以看出来核心代码在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有了就差秘钥了,直接打印看看,看起来是明文。

唯品会监控 唯品会监控库存_时间戳_14

为了验证,搜索secretKey,发现就是个常量,那就真是明文了,搞定了。

唯品会监控 唯品会监控库存_时间戳_15

04

效果及代码

最终效果就是这样了:

唯品会监控 唯品会监控库存_json_16

实际应用时,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('输入回车后继续:')


唯品会监控 唯品会监控库存_时间戳_17