utils.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import datetime
  2. import time
  3. import base64
  4. import urllib3
  5. import requests
  6. from Crypto.Hash import SHA256
  7. from Crypto.PublicKey import RSA
  8. from pyasn1_modules import rfc8017
  9. from Crypto.Signature import pkcs1_15
  10. from pyasn1.codec.der import decoder
  11. from urllib.parse import urlparse, parse_qs, urlencode
  12. urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  13. HEADERS = {
  14. "user-agent": "Kuril+/5.47.1 (Android 11)",
  15. "cache-control": "max-age=3600",
  16. "x-request-version": "5.93.1",
  17. "x-request-sign-type": "RSA2",
  18. "x-request-package-sign-version": "0.0.1",
  19. "x-request-utm_source": "yingyongbao",
  20. "content-type": "application/json",
  21. "x-request-id": "",
  22. "x-request-sign-version": "v1",
  23. "referer": "https://qiandao.cn",
  24. "x-request-channel": "yingyongbao",
  25. "x-device-id": "fbc87d023bf9ff1b",
  26. "host": "api.qiandao.com",
  27. "x-request-package-id": "1006",
  28. "x-request-device": "android",
  29. "x-request-version-code": "245",
  30. # "authorization": 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Ijg2NTIxNTgzOTI0MDk4MzQ1NyIsInR5cGUiOiJVU0VSIiwiZGV2aWNlSWQiOiIxMDA2IiwiZXhwIjoxNzUxMTc1NjczLCJpYXQiOjE3NTA5MTY0NzN9.aWao_5NvwpSKHw6-bbRVHtfY-_zw_SlHow4zI-A0qg3vZIvR2PbbXQ2XYIvaM0l7h9jbzGPA5veeW4wSWDVG9uqHQdX-zvghfvflxzvntBoYP1Un1e9TxZhYCrblqi256p49YFguFkTvuEdR15LvdtdlVadfq-GMX_UPKj_w2XnqTZHCryebnJcTQ5Y-uA9wnDEtHHMWUKJ4i_JuwaWR1DKzW_5-Kd4GcoJ3c75RVkfqlWlYLd9PKxVSLBEuiV2YCj459Ceo-hyGWz2x9gcBxg1FLbstsDiI5HgAEFl1upOImwVqj7cBS1B4MAwLUp0A-jdGFamji0PrfW-xH7ZcRA'
  31. }
  32. def get_proxys(log):
  33. """
  34. 获取代理
  35. :return: 代理
  36. """
  37. tunnel = "x371.kdltps.com:15818"
  38. kdl_username = "t13753103189895"
  39. kdl_password = "o0yefv6z"
  40. try:
  41. proxies = {
  42. "http": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": kdl_username, "pwd": kdl_password, "proxy": tunnel},
  43. "https": "http://%(user)s:%(pwd)s@%(proxy)s/" % {"user": kdl_username, "pwd": kdl_password, "proxy": tunnel}
  44. }
  45. return proxies
  46. except Exception as e:
  47. log.error(f"Error getting proxy: {e}")
  48. raise e
  49. def get_params_dict(url):
  50. """
  51. 因app对list值做过特殊处理
  52. 从URL里提取出params
  53. :param url:
  54. :return:
  55. """
  56. query = urlparse(url).query
  57. parsed = parse_qs(query, keep_blank_values=True)
  58. params = {}
  59. for key, value in parsed.items():
  60. clean_key = key.rstrip("[]")
  61. params[clean_key] = value[0] if len(value) == 1 else value
  62. return params
  63. def get_sign(s):
  64. """
  65. 获取签名
  66. :param s:
  67. :return:
  68. """
  69. modulus_b64_str = (
  70. "MIIEpQIBAAKCAQEA5d6YfNTsNo5qrD16Lu4TrfrWND5QhuXQH/1ysF1p9rmcJ0SebEAJvaw0rWbZdDFK"
  71. "YYQwKiw6wikZhX5tXPf0XvQ1aTzzPqc5d3ZYm0kCSaP5+Qs4Mv+xVT10f/xoFxrCXCqq/kEpOvS/GGJAP"
  72. "H7zQV3fTwYZWQFrj01sJ2wO/nckn6TCNLt56dBUgYEL6lmOaLPbYxQqDFBkCIjYYKJJ228wpBLyuoCYxa"
  73. "8tqUHz2WgRTpWmlL/1sco8p7bC2DTYM9n6LgEyndDmfGGlIX09Tu+wXZmPbny3IZ97BpvB5pa7x1X/9lr"
  74. "n5FIOxBdH9kh2/n/hb5UB/wR1/C9tJuuafwIDAQABAoIBAQDGIV79+ei//XEklLjDyqFbzGDlFvEB1QPX"
  75. "DvXT3jB/YOyfTB3g4DGFMvEUpRm5dOLPushpEUZ0JEjDL33ELFSNo6CF3OssjaaSuYcWEY/POW80od8G1"
  76. "i1bc2T/C+gMQhxUpNJN5IxNLLeppMYJXsL9DJR14KPoe7jiA7G9KP6jhRuHH4JC+b91swCqB1bNsh6kWr"
  77. "C6dxfhJtlFNkIkZXdenQ3KCoFnApBvsv7bMsLIFqYyv2QRH+KVioV0t+XrCfHoQ4qMzUgFgel6ITcT4JZ"
  78. "DGGH+CmPEeiP4TXFtSs32Ii0qJ0CDjXaBWjwRCGWK07VRRZL6FLNYf6el12JgWACBAoGBAOfVDK5jnD9i"
  79. "5YLY9OdDFtyO4LtojObGJUnYH3Y2IoEiaArKWYsKQtgGI76TwdJwj2F7blQNXm2C/3tlrqeZOhz1LZqVa"
  80. "Nflkhml8/ghYZENcrTUTGKWTLmZnWWUQQZHN5aqHb0DTQPbqD6YspqVvlrjF2yJs1/UBZc7GiGbYSc5Ao"
  81. "GBAP3VKrl+QpESRa7B/6+GezvPV0DNf/b9zuTEMO/EljWLEnnSghBFBAlk2qihL4Hoo/qmWk2POwMHLYm"
  82. "b9Q9zHYzvwbcUOiQIpraFrN0Wn9B+nwDff0Wl+6vyA0W6AylBQHU4GfCZuts6qAMX+fKTAF/h24Ml36/1"
  83. "Lci45NIN3ld3AoGBANSSKXqNo2sLh16fCJA0l/XMnIu6pdfEv9Qh81c09BZsMfIS8F/pHLlvh77rRMFsr"
  84. "Eu6HcO8LmVDxHalGaxbd0muFg60CNpNidUysa1HDmsuZYshTpjnL5rPG99UPPtAudvQSExThn6PHomnAb"
  85. "10qII1z/iZmnu3sRil/KPsEP0hAoGBAK2BETQ77sp0//almt1jAkduwciE74xoDwzmYkDyUm6FAnsM/mS"
  86. "amFjHfIM5slyNJdFF9oH/fqniNSlT1l3aJP/aPsKi698HntUyaGezeEgu1QbmvntgKrhss/nsXQ7NEH9P"
  87. "esOwgT4rSP7cW7iI7P+dRcvOjqka4VHLuHUwj6OfAoGACezeNNpkxd8PhgjYkAHjtSnrEHgzQB1YErvAr"
  88. "jssDWQtkne6jL669lyx0al/f2dIfUxaPp79aOCETle9n/2mpng7vjrM8sGZuvKcsgs1ZsEOLepIEUIaYv"
  89. "m0S4kmzwYKVmhxhJzPjWX9scCgORQkNctsOKujmzNvBLwSxbpuxTE="
  90. )
  91. # base64 解码 modulus_b64_str 得到 ASN.1 结构(modulus 和 privateExponent)
  92. key_der = base64.b64decode(modulus_b64_str)
  93. # 尝试解码为 RSAPrivateKey 格式(标准 PKCS#1)
  94. private_key_asn1, _ = decoder.decode(key_der, asn1Spec=rfc8017.RSAPrivateKey())
  95. n = int(private_key_asn1['modulus'])
  96. d = int(private_key_asn1['privateExponent'])
  97. # 构造 RSA 私钥
  98. key = RSA.construct((n, 65537, d)) # 公共指数默认是 65537(0x10001)
  99. # 进行 SHA256 with RSA 签名
  100. h = SHA256.new(s.encode('utf-8'))
  101. signature = pkcs1_15.new(key).sign(h)
  102. # 返回 base64 编码的签名结果
  103. return base64.b64encode(signature).decode()
  104. def get_query_string(params, reverse=False):
  105. """
  106. 对参数进行倒序排列
  107. :param reverse:
  108. :param params:
  109. :return:
  110. """
  111. if not params:
  112. return ''
  113. query_items = []
  114. for key, value in params.items():
  115. if isinstance(value, list):
  116. for v in value:
  117. query_items.append((f"{key}[]", v))
  118. else:
  119. query_items.append((key, value))
  120. if reverse:
  121. query_items = sorted(query_items, key=lambda x: x[0], reverse=reverse)
  122. return urlencode(query_items)
  123. def get_headers(url, params=None, token=None):
  124. """
  125. 设置请求头
  126. :param params:
  127. :param url:
  128. :param token:
  129. :return:
  130. """
  131. ts = str(int(time.time() * 1000))
  132. query_string = get_query_string(params, reverse=True)
  133. sign = get_sign(f'{urlparse(url).path}{query_string}{ts}')
  134. HEADERS["x-request-timestamp"] = ts
  135. HEADERS["x-request-sign"] = sign
  136. if token:
  137. HEADERS["authorization"] = token
  138. return HEADERS
  139. def request_post_data(log, url, data, proxy=True):
  140. """
  141. 发送POST请求
  142. :param log:
  143. :param url:
  144. :param data:
  145. :param proxy:
  146. :return:
  147. """
  148. headers = get_headers(url)
  149. headers = get_headers(url, params, token)
  150. if proxy:
  151. response = requests.post(url, headers=headers, json=data, verify=False, proxies=get_proxys(log))
  152. else:
  153. response = requests.post(url, headers=headers, json=data, verify=False)
  154. # response = requests.post(url, headers=headers, json=data, verify=False, proxies=get_proxys(log))
  155. # response = requests.post(url, headers=headers, json=data, verify=False)
  156. response.raise_for_status()
  157. return response.json()
  158. def request_get_data(log, url, params, token=None, proxy=True):
  159. """
  160. 发送GET请求
  161. :param log:
  162. :param url:
  163. :param params:
  164. :param token:
  165. :param proxy:
  166. :return:
  167. """
  168. headers = get_headers(url, params, token)
  169. # GET请求对url参数进行了特殊的处理
  170. query_string = get_query_string(params)
  171. url = f'{url}?{query_string}'
  172. if proxy:
  173. response = requests.get(url, headers=headers, verify=False, proxies=get_proxys(log), timeout=22)
  174. else:
  175. response = requests.get(url, headers=headers, verify=False, timeout=22)
  176. # response = requests.get(url, headers=headers, verify=False, proxies=get_proxys(log), timeout=22)
  177. # print(response.text)
  178. response.raise_for_status()
  179. return response.json()
  180. def transform_ms(log, tradingTime):
  181. """
  182. 将毫秒转换为时间
  183. :param log:
  184. :param tradingTime:
  185. :return:
  186. """
  187. try:
  188. timestamp_s = int(tradingTime) / 1000 # 转换为秒
  189. dt = datetime.datetime.fromtimestamp(timestamp_s)
  190. trading_time = dt.strftime('%Y-%m-%d %H:%M:%S')
  191. except Exception as e:
  192. log.error(e)
  193. trading_time = None
  194. return trading_time
  195. def translate_s(log, timestamp):
  196. """
  197. 解析 时间戳 秒
  198. :param log:
  199. :param timestamp:
  200. :return:
  201. """
  202. try:
  203. dt = datetime.datetime.fromtimestamp(int(timestamp))
  204. formatted_date = dt.strftime('%Y-%m-%d %H:%M:%S')
  205. except Exception as e:
  206. log.error(e)
  207. formatted_date = None
  208. return formatted_date