utils_others.py 9.3 KB

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