登陆车队管家网站sign参数生成解析(Python用Requests库登录)

登陆车队管家网站sign参数生成解析

:用Python中requests库自动登陆“车队管家”并且下载指定日期区间的排班明细,再导出指定字段数据生成Excel表格并自动打开(存放于系统临时目录中,名称为1,688.xlsx)

第一步:打开登陆页面

使用谷歌浏览器打开车管(https://tms.cheoa.cn/#/login)登陆界面,并按F12打开调试面板,定位到“网络”选项卡,输入用户名和密码点击登陆。

第二步:查看api登陆网址及参数

登陆后点击左侧请求列表

查看“标头”可以看到登陆网址:https://api.cheoa.cn/interface?store=pcpc&version=PC-29915

及登陆方式:POST

可以看到表单参数除“timestamp”时间戳、“nonce”随机数和“sign”签名参数会变化,其他参数皆为固定值。

表单参数:

countryNumber: 86

countryCode: CHN

isMd5Login:

userName: 你的用户名

password: 你的密码

timestamp: 1702949745108

nonce: 1002901267

appId: CHEOA

sign: f72b6d83a58bd3ebc3e5c22f3b9f1fab6701c69a123f634249f85e6827864d9d

source: point

method: user.pcLogin

第三步:查找“timestamp”时间戳、“nonce”随机数和“sign”签名的生成方式。

通过打开“源代码/来源”面板,搜索“sign”字符串,查找到sign的生成公式:

r.user.sign = CryptoJS.SHA256("appId=CHEOA×tamp=" + o + "&nonce=" + t + "&appSecret=CHEOAb10a26fae7f8826641f7a58084d")

通过公式可知,它使用了JavaScript中的一个加密库“CryptoJS.SHA256”加密后面的字符串。其中字符串生成的参数除了“o”和“t”参数,其他参数都是固定值,并且可以看到:

var o = e.data.timestamp

  , t = e.data.nonce

可以看到o是一个时间戳,t是一个随机数,并且是e.data里面返回的数据。

这说明在真正登陆之前要先请求服务器返回一个时间戳和随机数,然后计算sign值再进行登陆。

第四步:查找e.data数据中的timestamp和nonce值

{

重新登陆一次,查看“网络”面板,发现第一个网址,点击左侧请求列表》打开“响应”面板后可以看到服务器返回了nonce和timestamp参数。

   

响应数据:

"msg": "数据正确",

    "code": 100,

    "data": {

        "nonce": "1002901267",

        "timestamp": "1702949745108"

    }

}

再打开“标头”,可以看到

请求网址:

https://api.cheoa.cn/interface?store=pcpc&version=PC-29915&method=operation.getNowTimestamp&_time=1702949745894

请求方式:GET

请求网址中除了“operation.getNowTimestamp&_time”(系统当前时间戳)参数是变化的,其他也是固定的。

此时,我们已经取得了计算sign名称中“o”和“t”参数的获取方法。

第五步:按前面公式代入'o'和't'参数值计算sign签名值

第六步:再将'o'和't'及'sign'签名值代入登陆参数中即可实现登陆

附:Python用requests库登录“车队管家”并下载“排班明细”实现代码

# 运行前替换你的用户名和密码,并确保有下载排班明细权限

import time

import datetime

from datetime import date

import requests

from urllib.parse import urlencode

import hashlib

import tkinter as tk

from tkinter import simpledialog

from tkinter import messagebox

import json

from json.decoder import JSONDecodeError

import os

import tempfile

from openpyxl import Workbook

from openpyxl.cell import _writer

def get_nonce_timestamp():

    # 获取服务器返回nonce随机数和timestamp时间戳

    # print('获取当前时间戳')

    timestamp = int(time.time()*1000)

    # print('构造请求网址')

    t_url = f'https://api.cheoa.cn/interface?store=pcpc&version=PC-29915&method=operation.getNowTimestamp&_time={timestamp}'

    # print(f't_url:{t_url}')

    # print('请求api网址')

    res = requests.get(t_url)

    # ot签名参数

    try:

        ot_dic = {

            "o": res.json()["data"]["timestamp"],    # o = e.data.timestamp

            "t": res.json()["data"]["nonce"],    # t = e.data.nonce

        }

    except JSONDecodeError as e:

        print(f"获取时间戳和随机数失败:{e}")

    # print(f"res.json():{res.json()},\not_dic:{ot_dic}")

    return ot_dic

def get_sign(o, t):

    # print('生成sign签名参数')

    data = "appId=CHEOA×tamp=" + o + "&nonce=" + t + "&appSecret=CHEOAb10a26fae7f8826641f7a58084d"

    # 返回sign值

    return hashlib.sha256(data.encode()).hexdigest()

def login_site(ot, sign, s, headers):

    # POST登录

    login_url = "https://api.cheoa.cn/interface?store=pcpc&version=PC-29915"

    login_pramas = {

        "countryNumber": "86",

        "countryCode": "CHN",

        "isMd5Login": "",

        "userName": "你的用户名",

        "password": "你的密码",

        "timestamp": str(ot.get("o")), # str(time.time() * 1000),   # 此处参数为“字符串类型”

        "nonce": ot.get("t"),   # 使用服务器返回的timestamp

        "appId": "CHEOA",

        "sign": sign, #

        "source": "point",

        "method": "user.pcLogin"

    }

    # print(f"login_params:{login_pramas}")

    response = s.post(login_url, data=login_pramas, headers=headers)

    return response

def input_date():

    # 让用户输入起止日期

    root = tk.Tk()  

    root.withdraw()  # 隐藏主窗口  

    # 当前日期第一天

    first_date = date.today().replace(day=1).strftime("%Y-%m-%d")

    # 明天日期

    tomorrow_date = date.today() + datetime.timedelta(days=1)

    tomorrow_date = tomorrow_date.strftime("%Y-%m-%d")

    # 弹出输入对话框,设置默认值为当前日期

    from_date = tk.simpledialog.askstring("^", "起始日期:", initialvalue=first_date)

    to_date = tk.simpledialog.askstring("^", "截止日期:", initialvalue=tomorrow_date)

    # 处理用户输入

    if not from_date:

        from_date = first_date

    if not to_date:

        to_date = tomorrow_date

    return from_date, to_date

def get_all_data(from_date, to_date, token, headers, s):

    # 分页获取排班数据

    all_data = {"data":{"items":[]}}

    offset = 0      # 偏移量,从第1页开始获取

    url = 'https://api.cheoa.cn/interface?store=pcpc&version=PC-29915'

    while True:

        params = {

            "sortKey": "START_DATE",

            "sortType": "ASC",

            "offset": offset,           # 偏移量(即下一页)

            "length": "300",            # 每页条数

            "fromDate": from_date,      # 2023-12-14

            "toDate": to_date,          # 2023-12-14

            "schedulingStatus": "127",

            "statusName": "全部排班",

            "keyword": "",

            "stopName": "",

            "isDeleted": "1",

            "plateNo": "全部车辆",

            "isNewList": "true",

            "vehicleId": "",

            "method": "scheduling.listScheduling",

            "token": token,             # 口令

            "isPage": "true"

        }

        res = s.post(url, data=params, headers=headers)

        # 下100条

        offset += 300

        # 合并数据

        try:

            all_data["data"]["items"].extend(res.json()["data"]["items"])

        except JSONDecodeError as e:

            print(f"获取排班数据失败:{e}")

        # 获取总数

        total = int(res.json()["data"]["total"])

        # print(f"total:{total}")

        #

        if offset >= total:

            print(f"获取中{total}/{total}...")

            # 返回所有数据

            return all_data

            # 退出循环

            break

        print(f"获取中{offset}/{total}...")

def export_excel(json_data):

    # 将服务器返回的排班源代码导出到Excel格式

    # 新建工作表,并在第一行写入标题

    wb = Workbook()

    ws = wb.active

    ws.append(["订单号", "分组", "星期几", "用车人姓名", "用车人手机号码", "起点站", "出发日期", "出发时间", "终点站", "结束日期", "结束时间", "发送给司机的信息", "司机姓名", "司机手机号码", "车牌号码", "订单收入", "订单支出", "行程公里数(自动)", "备注"])

    # 遍历JSON数据

    for item in json_data["data"]["items"]:

        order_number = item["orderNumber"]

        group_name = item["groupName"]

        week = item["week"]

        contactsName = item["contactsName"]

        contactsPhone = item["contactsPhone"]

        startStop = item["startStop"]

        startDate = item["startDate"]

        startTime = item["startTime"]

        endStop = item["endStop"]

        endDate = item["endDate"]

        endTime = item["endTime"]

        driverMessage = item["driverMessage"]

        driver_names = ";".join([son["driverName"] for son in item["sons"]])

        driverPhones = ";".join([son["driverPhone"] for son in item["sons"]])

        plateNos = ";".join([son["plateNo"] for son in item["sons"]])

        jobFee = item["jobFee"]

        driverFee = item["driverFee"]

        extend_data = json.loads(item["extend"])    # 将extend数组对象转化为JSON对象

        for entry in extend_data.get("customize", []):

            if entry.get("name") == "行程公里数(自动)":

                tripKilometers = entry.get('value')

        ws.append([order_number, group_name, week, contactsName, contactsPhone, startStop, startDate, startTime, endStop, endDate, endTime, driverMessage, driver_names, driverPhones, plateNos, jobFee, driverFee, tripKilometers, driverMessage])

    # 将工作簿保存一个Excel文件

    try:

        wb.save(tempfile.gettempdir() + "\\1688.xlsx")

    except PermissionError:

        # print("请将1688.xlsx工作表关闭后重试")

        messagebox.showerror("错误", "1688.xlsx表格已打开,请关闭后重试")

def main():

    print("请求并获取服务器o——时间戳timestamp,t——随机数nonce值")

    # 获取o和t值

    ot = get_nonce_timestamp()  

    print('计算生成sign值')

    # 获取sign值

    sign = get_sign(ot.get("o"), ot.get("t"))   

    # 设置请求头

    headers = {

        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',

        'X-Requested-With': 'XMLHttpRequest',

    }

    # 创建会话对象保持登录状态

    s = requests.Session()

    print('发送登陆请求')

    response = login_site(ot, sign, s, headers)

    # 检查登录是否成功

    if response.status_code == 200:

        # 解析返回的JSON数据,获取token

        json_data = response.json()

        # print(json_data)

        

        # 提取“company”和“token”字段的值

        company = json_data['data']['company']

        token = json_data['data']['token']

        if company == "你的公司名":

            print("登陆成功")

            from_date, to_date = input_date()

            print(f"输入起止日期:{from_date}——{to_date}")

            # 开始获取指定日期区间排班数据

            all_data = get_all_data(from_date, to_date, token, headers, s)

            # print(all_data)

            print("导出到临时表格文件", tempfile.gettempdir() + "\\1688.xlsx")

            export_excel(all_data)

            print("打开生成的表格")

            temp_dir = tempfile.gettempdir()    # 获取系统临时文件路径

            os.system(f'start et {temp_dir}\\1688.xlsx')

    else:

        print("登录失败,错误信息:", response.text)

if __name__ == "__main__":

    main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值