| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- # -*- coding: utf-8 -*-
- # Author : Charley
- # Python : 3.10.8
- # Date : 2025/6/23 15:41
- import time
- import requests
- import base64
- from urllib.parse import urlparse, parse_qs, urlencode
- from Crypto.PublicKey import RSA
- from Crypto.Signature import pkcs1_15
- from Crypto.Hash import SHA256
- from pyasn1.codec.der import decoder
- from pyasn1_modules import rfc8017
- import urllib3
- urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
- HEADERS = {
- "user-agent": "Kuril+/5.47.1 (Android 11)",
- "cache-control": "max-age=3600",
- "x-request-version": "5.47.1",
- "x-request-sign-type": "RSA2",
- "x-request-package-sign-version": "0.0.1",
- "x-request-utm_source": "yingyongbao",
- "content-type": "application/json",
- "x-request-id": "",
- "x-request-sign-version": "v1",
- "referer": "https://qiandao.cn",
- "x-request-channel": "yingyongbao",
- "x-device-id": "fbc87d023bf9ff1b",
- "host": "api.qiandao.com",
- "x-request-package-id": "1006",
- "x-request-device": "android",
- "x-request-version-code": "245"
- }
- def get_params_dict(url):
- '''
- 因app对list值做过特殊处理
- 从URL里提取出params
- :param url:
- :return:
- '''
- query = urlparse(url).query
- parsed = parse_qs(query, keep_blank_values=True)
- params = {}
- for key, value in parsed.items():
- clean_key = key.rstrip("[]")
- params[clean_key] = value[0] if len(value) == 1 else value
- return params
- def get_sign(s):
- '''
- 获取签名
- :param s:
- :return:
- '''
- modulus_b64_str = (
- "MIIEpQIBAAKCAQEA5d6YfNTsNo5qrD16Lu4TrfrWND5QhuXQH/1ysF1p9rmcJ0SebEAJvaw0rWbZdDFK"
- "YYQwKiw6wikZhX5tXPf0XvQ1aTzzPqc5d3ZYm0kCSaP5+Qs4Mv+xVT10f/xoFxrCXCqq/kEpOvS/GGJAP"
- "H7zQV3fTwYZWQFrj01sJ2wO/nckn6TCNLt56dBUgYEL6lmOaLPbYxQqDFBkCIjYYKJJ228wpBLyuoCYxa"
- "8tqUHz2WgRTpWmlL/1sco8p7bC2DTYM9n6LgEyndDmfGGlIX09Tu+wXZmPbny3IZ97BpvB5pa7x1X/9lr"
- "n5FIOxBdH9kh2/n/hb5UB/wR1/C9tJuuafwIDAQABAoIBAQDGIV79+ei//XEklLjDyqFbzGDlFvEB1QPX"
- "DvXT3jB/YOyfTB3g4DGFMvEUpRm5dOLPushpEUZ0JEjDL33ELFSNo6CF3OssjaaSuYcWEY/POW80od8G1"
- "i1bc2T/C+gMQhxUpNJN5IxNLLeppMYJXsL9DJR14KPoe7jiA7G9KP6jhRuHH4JC+b91swCqB1bNsh6kWr"
- "C6dxfhJtlFNkIkZXdenQ3KCoFnApBvsv7bMsLIFqYyv2QRH+KVioV0t+XrCfHoQ4qMzUgFgel6ITcT4JZ"
- "DGGH+CmPEeiP4TXFtSs32Ii0qJ0CDjXaBWjwRCGWK07VRRZL6FLNYf6el12JgWACBAoGBAOfVDK5jnD9i"
- "5YLY9OdDFtyO4LtojObGJUnYH3Y2IoEiaArKWYsKQtgGI76TwdJwj2F7blQNXm2C/3tlrqeZOhz1LZqVa"
- "Nflkhml8/ghYZENcrTUTGKWTLmZnWWUQQZHN5aqHb0DTQPbqD6YspqVvlrjF2yJs1/UBZc7GiGbYSc5Ao"
- "GBAP3VKrl+QpESRa7B/6+GezvPV0DNf/b9zuTEMO/EljWLEnnSghBFBAlk2qihL4Hoo/qmWk2POwMHLYm"
- "b9Q9zHYzvwbcUOiQIpraFrN0Wn9B+nwDff0Wl+6vyA0W6AylBQHU4GfCZuts6qAMX+fKTAF/h24Ml36/1"
- "Lci45NIN3ld3AoGBANSSKXqNo2sLh16fCJA0l/XMnIu6pdfEv9Qh81c09BZsMfIS8F/pHLlvh77rRMFsr"
- "Eu6HcO8LmVDxHalGaxbd0muFg60CNpNidUysa1HDmsuZYshTpjnL5rPG99UPPtAudvQSExThn6PHomnAb"
- "10qII1z/iZmnu3sRil/KPsEP0hAoGBAK2BETQ77sp0//almt1jAkduwciE74xoDwzmYkDyUm6FAnsM/mS"
- "amFjHfIM5slyNJdFF9oH/fqniNSlT1l3aJP/aPsKi698HntUyaGezeEgu1QbmvntgKrhss/nsXQ7NEH9P"
- "esOwgT4rSP7cW7iI7P+dRcvOjqka4VHLuHUwj6OfAoGACezeNNpkxd8PhgjYkAHjtSnrEHgzQB1YErvAr"
- "jssDWQtkne6jL669lyx0al/f2dIfUxaPp79aOCETle9n/2mpng7vjrM8sGZuvKcsgs1ZsEOLepIEUIaYv"
- "m0S4kmzwYKVmhxhJzPjWX9scCgORQkNctsOKujmzNvBLwSxbpuxTE="
- )
- # base64 解码 modulus_b64_str 得到 ASN.1 结构(modulus 和 privateExponent)
- key_der = base64.b64decode(modulus_b64_str)
- # 尝试解码为 RSAPrivateKey 格式(标准 PKCS#1)
- private_key_asn1, _ = decoder.decode(key_der, asn1Spec=rfc8017.RSAPrivateKey())
- n = int(private_key_asn1['modulus'])
- d = int(private_key_asn1['privateExponent'])
- # 构造 RSA 私钥
- key = RSA.construct((n, 65537, d)) # 公共指数默认是 65537(0x10001)
- # 进行 SHA256 with RSA 签名
- h = SHA256.new(s.encode('utf-8'))
- signature = pkcs1_15.new(key).sign(h)
- # 返回 base64 编码的签名结果
- return base64.b64encode(signature).decode()
- def get_query_string(params, reverse=False):
- '''
- 对参数进行倒序排列
- :param params:
- :return:
- '''
- if not params:
- return ''
- query_items = []
- for key, value in params.items():
- if isinstance(value, list):
- for v in value:
- query_items.append((f"{key}[]", v))
- else:
- query_items.append((key, value))
- if reverse:
- query_items = sorted(query_items, key=lambda x: x[0], reverse=reverse)
- return urlencode(query_items)
- def get_headers(url, params=None):
- '''
- 设置请求头
- :param url:
- :return:
- '''
- ts = str(int(time.time() * 1000))
- query_string = get_query_string(params, reverse=True)
- sign = get_sign(f'{urlparse(url).path}{query_string}{ts}')
- HEADERS["x-request-timestamp"] = ts
- HEADERS["x-request-sign"] = sign
- return HEADERS
- def reuqest_post_data(url, data):
- '''
- 发送POST请求
- :param url:
- :param data:
- :return:
- '''
- headers = get_headers(url)
- response = requests.post(url, headers=headers, json=data, verify=False)
- response.raise_for_status()
- return response.json()
- def reuqest_get_data(url, params):
- '''
- 发送GET请求
- :param url:
- :param params:
- :return:
- '''
- headers = get_headers(url, params)
- # GET请求对url参数进行了特殊的处理
- query_string = get_query_string(params)
- url = f'{url}?{query_string}'
- response = requests.get(url, headers=headers, verify=False)
- # print(response.url)
- response.raise_for_status()
- return response.json()
|