OfpayGrab.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import csv
  4. import requests
  5. import json
  6. from datetime import datetime
  7. from cachetools import TTLCache
  8. import pickle
  9. import multiprocessing
  10. cache = None;
  11. cache_file_path = 'elife.cache.pickle';
  12. def init_cache():
  13. global cache;
  14. if os.path.exists(cache_file_path):
  15. # 从文件中加载缓存对象
  16. with open(cache_file_path, 'rb') as file:
  17. cache = pickle.load(file);
  18. else:
  19. cache = TTLCache(maxsize=100, ttl=60*60*24);
  20. # 将缓存对象持久化到文件
  21. with open(cache_file_path, 'wb') as file:
  22. pickle.dump(cache, file);
  23. return cache;
  24. def save_cache():
  25. global cache;
  26. with open(cache_file_path, 'wb') as file:
  27. pickle.dump(cache, file);
  28. def set_cache(key, value):
  29. global cache;
  30. cache[key] = value;
  31. def get_cache(key):
  32. global cache;
  33. return cache.get(key);
  34. def read_csv(filename='results.csv'):
  35. data = []
  36. try:
  37. with open(filename, mode='r', encoding='utf-8') as csvfile:
  38. reader = csv.DictReader(csvfile);
  39. for row in reader:
  40. data.append(row);
  41. except IOError as e:
  42. print(f"read_csv error occurred: {e}");
  43. # print(data);
  44. return data;
  45. def read_json(filename='results.json'):
  46. data = None;
  47. try:
  48. with open(filename, 'r', encoding='utf-8') as file:
  49. data = json.load(file);
  50. except IOError as e:
  51. print(f"read_json error occurred: {e}");
  52. # print(data);
  53. return data;
  54. class OfpayGrabber:
  55. CheckBuyRepeatEnable = True;
  56. FastModeEnable = True;
  57. CheckStockEnable = True;
  58. def __init__(self, accout_data, activities_data):
  59. self.award_want_discount_dict = None;
  60. self.activities_data = activities_data;
  61. self.enable = True if accout_data['enable'] == 1 else False;
  62. self.accout_data = accout_data;
  63. self.host = 'market-web.ofpay.com';
  64. self.market_id = accout_data['market_id'];
  65. self.event_visitor_id = accout_data['event_visitor_id'];
  66. self.accout = accout_data['account'];
  67. uuid = accout_data['uuid'];
  68. user_agent = accout_data['user_agent'];
  69. authorization = accout_data['authorization'];
  70. self.cookies = eval(accout_data['cookies']);
  71. cookies_str = '; '.join([f'{key}={value}' for key, value in self.cookies.items()]);
  72. self.headers = {
  73. 'Host': self.host,
  74. 'UUID': uuid,
  75. 'Accept': '*/*',
  76. 'Sec-Fetch-Site': 'same-origin',
  77. 'Origin': 'https://market-web.ofpay.com',
  78. 'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
  79. 'Accept-Encoding': 'gzip, deflate, br',
  80. 'Sec-Fetch-Mode': 'cors',
  81. 'Content-Type': 'application/json; charset=utf-8',
  82. 'Connection': 'keep-alive',
  83. 'User-Agent': user_agent,
  84. 'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
  85. 'Authorization': authorization,
  86. 'Sec-Fetch-Dest': 'empty',
  87. 'Referer': 'https://market-web.ofpay.com/h5/union/standard/interactiveIGoChoose/index',
  88. 'Cookie': cookies_str,
  89. };
  90. # print(self.headers);
  91. def start(self):
  92. cate_items = self.get_market_items_from_cache();
  93. if not cate_items:
  94. cate_items = self.get_market_items_from_svr(self.market_id, self.event_visitor_id);
  95. if not cate_items:
  96. return;
  97. all_buy_list = self.get_will_market_buy_list_all();
  98. will_cate_count = len(all_buy_list);
  99. all_ret_list = [];
  100. sort_num = 0;
  101. for activity_data in cate_items:
  102. if 'sortNum' in activity_data:
  103. sort_num = activity_data['sortNum'];
  104. if sort_num >= will_cate_count:
  105. continue;
  106. buy_list = all_buy_list[sort_num];
  107. print(sort_num, buy_list);
  108. buy_ret_list = self.check_to_buy_all(buy_list, activity_data);
  109. all_ret_list.extend(buy_ret_list);
  110. sort_num += 1;
  111. if len(all_ret_list):
  112. save_cache();
  113. return all_ret_list;
  114. def get_will_market_buy_list_all(self):
  115. def_val = '星巴克|霸王茶姬|百果园|京东E卡|滴滴快车';
  116. name_arr = [];
  117. name_str = self.accout_data['will_buy_list'] if self.accout_data['will_buy_list'] else def_val;
  118. if len(name_str):
  119. segments = name_str.strip().split('|');
  120. name_arr = [];
  121. for vstr in segments:
  122. if not len(vstr):
  123. name_arr.append(None);
  124. else:
  125. vlist = vstr.strip().split(',');
  126. name_arr.append(vlist);
  127. else:
  128. name_arr = [];
  129. return name_arr;
  130. def get_award_expected_discount(self, price, prize_name):
  131. if not self.award_want_discount_dict:
  132. self.award_want_discount_dict = {};
  133. def_val = '星巴克#20.80|霸王茶姬#10.80|百果园#10.80|京东E卡#10.80|滴滴快车#10.80';
  134. discount_str = self.accout_data['will_discount'] if self.accout_data['will_discount'] else def_val;
  135. if len(discount_str):
  136. segments = discount_str.strip().split('|');
  137. for vstr in segments:
  138. if len(vstr):
  139. vlist = vstr.strip().split('#');
  140. key = vlist[0];
  141. if vlist[1]:
  142. price_val = float(vlist[1].strip());
  143. self.award_want_discount_dict[key] = price_val;
  144. if self.award_want_discount_dict.get(prize_name) != None:
  145. return self.award_want_discount_dict[prize_name];
  146. return price - 8.8;
  147. def get_market_items_from_cache(self):
  148. if not self.activities_data:
  149. self.activities_data = read_json('elife_activities_data.json');
  150. return self.activities_data;
  151. def save_market_items_cache(self, market_id, event_visitor_id, data):
  152. with open('elife_activities_data.json', 'w', newline='', encoding='utf-8') as f:
  153. json_str = json.dumps(data, cls=DecimalEncoder, ensure_ascii=False);
  154. f.write(json_str);
  155. def get_market_items_from_svr(self, market_id, event_visitor_id):
  156. params = {
  157. 'marketId': market_id,
  158. 'eventVisitorId': event_visitor_id
  159. };
  160. url = ('https://%s/h5/union/api/interactiveIGoChoose/indexConfigRebuild' % (self.host));
  161. print("请求市场商品列表数据");
  162. response = self.get_request(url, params);
  163. if response.status_code == 200:
  164. try:
  165. json_str = response.content;
  166. json_str = json_str.decode('utf-8');
  167. params = json.loads(json_str);
  168. if params.get('code') == 'success':
  169. print("请求市场商品列表数据成功");
  170. # self.save_market_items_cache(params['data']);
  171. return params['data'];
  172. else:
  173. print(f"请求市场商品列表数据成功,响应:{json_str}");
  174. except Exception as e:
  175. print("请求市场商品列表发生错误");
  176. print(e);
  177. finally:
  178. pass
  179. else:
  180. print("请求市场商品列表发生错误");
  181. return None;
  182. def check_to_buy_all(self, buy_list, activity_data):
  183. activity_id = activity_data['activityId'];
  184. sub_activity_id = activity_data['subActivityId'] if 'subActivityId' in activity_data else None;
  185. sub_login_type = activity_data['subLoginType'];
  186. award_list = activity_data['awardList'];
  187. ret_list = [];
  188. for i in range(len(buy_list)):
  189. one_ret = self.check_to_buy_one(activity_id, sub_activity_id, sub_login_type, buy_list[i], award_list);
  190. if one_ret:
  191. ret_list.append(one_ret);
  192. return ret_list;
  193. def check_to_buy_one(self, activity_id, sub_activity_id, sub_login_type, item_name, award_list):
  194. one_ret = None;
  195. check_buy_repeat_key = f"lkOfPayBuyItemKey#{item_name}#{self.accout}";
  196. now_string = datetime.now().strftime('%Y-%m-%d %H:%M:%S');
  197. if OfpayGrabber.CheckBuyRepeatEnable:
  198. last_buy_succ_date = get_cache(check_buy_repeat_key);
  199. if now_string == last_buy_succ_date:
  200. print(f"商品[{item_name}]今日已抢购成功过,跳过~~");
  201. return one_ret;
  202. if OfpayGrabber.FastModeEnable:
  203. for award_data in award_list:
  204. if item_name in award_data['prizeName']:
  205. print(f"开始尝试抢购-{award_data['prizeName']}{award_data['prizeDesc']},价格:{award_data['price']},库存:{award_data['remainStock']}");
  206. if OfpayGrabber.CheckStockEnable:
  207. if award_data['remainStock'] > 0:
  208. one_ret = self.item_buy_fast(activity_id, sub_activity_id, sub_login_type, award_data);
  209. if one_ret:
  210. # 抢购成功
  211. set_cache(check_buy_repeat_key, now_string);
  212. else:
  213. print("库存不足,跳过~");
  214. else:
  215. one_ret = self.item_buy_fast(activity_id, sub_activity_id, sub_login_type, award_data);
  216. if one_ret:
  217. # 抢购成功
  218. set_cache(check_buy_repeat_key, now_string);
  219. break;
  220. else:
  221. for award_data in award_list:
  222. if item_name in award_data['prizeName']:
  223. print(f"开始尝试抢购-{award_data['prizeName']}{award_data['prizeDesc']},价格:{award_data['price']},库存:{award_data['remainStock']}");
  224. if award_data['remainStock'] > 0:
  225. one_ret = self.item_buy_normal(activity_id, sub_activity_id, sub_login_type, award_data);
  226. if one_ret:
  227. # 抢购成功
  228. set_cache(check_buy_repeat_key, now_string);
  229. else:
  230. print("库存不足,跳过~");
  231. break;
  232. return one_ret;
  233. def item_buy_fast(self, activity_id, sub_activity_id, sub_login_type, award_data):
  234. print("###item_buy_fast");
  235. activity_id = award_data['activityId']
  236. # 假设 ofpay_account_phone 和 event_visitor_id 在这段代码之外被定义
  237. # 或者作为参数传递给这个函数
  238. event_visitor_id = self.event_visitor_id;
  239. game_account = self.accout;
  240. third_info = json.loads(award_data['thirdInfo']);
  241. award_id = award_data['awardId'];
  242. pay_info = self.get_pay_info(activity_id, award_id, '', '', game_account, event_visitor_id);
  243. if pay_info:
  244. if 'detailId' in pay_info:
  245. pay_ret = self.pay(activity_id, event_visitor_id, pay_info['detailId']);
  246. if pay_ret:
  247. return award_data;
  248. return None;
  249. def get_pay_info(self, activity_id, award_id, goods_id, invitation_code, game_account, event_visitor_id):
  250. url = f'https://{self.host}/h5/union/api/draw/interactiveIGoChoose/{activity_id}?awardId={award_id}&goodsId={goods_id}&invitationCode={invitation_code}&gameAccount={game_account}&eventVisitorId={event_visitor_id}';
  251. print("请求商品预支付数据");
  252. response = self.get_request(url);
  253. if response.status_code == 200:
  254. try:
  255. json_str = response.content;
  256. json_str = json_str.decode('utf-8');
  257. params = json.loads(json_str);
  258. if params.get('code') == '0':
  259. print("请求商品预支付数据成功");
  260. print(json_str);
  261. return params;
  262. else:
  263. # 19=存在待支付订单
  264. print(f"请求商品预支付数据失败,响应:{json_str}")
  265. except Exception as e:
  266. print("请求商品预支付数据发生错误");
  267. print(e);
  268. finally:
  269. pass
  270. else:
  271. print("请求商品预支付数据发生错误");
  272. return None;
  273. def pay(self, activity_id, event_visitor_id, detail_id):
  274. account_phone = self.accout;
  275. post_data = {
  276. "detailId": detail_id,
  277. "rechargeAccount": account_phone,
  278. "account": account_phone,
  279. "appVersion": appVersion,
  280. };
  281. url = f"https://{self.host}/h5/api/mobile/activity/pay/{activity_id}?eventVisitorId={event_visitor_id}";
  282. body = json.dumps(post_data);
  283. response = self.post_request(url, body);
  284. if response.status_code == 200:
  285. try:
  286. json_str = response.content;
  287. json_str = json_str.decode('utf-8');
  288. params = json.loads(json_str);
  289. if params.get('code') == 'success':
  290. print("请求下单成功");
  291. print(json_str);
  292. return params;
  293. else:
  294. print(f"请求下单失败,响应:{json_str}");
  295. except Exception as e:
  296. print("请求下单发生错误");
  297. print(e);
  298. finally:
  299. pass
  300. else:
  301. print("请求下单发生错误");
  302. return None;
  303. def check_pick_item(self, prize_name, award_list):
  304. for award_data in award_list:
  305. one_prize_name = award_data['prizeName'];
  306. if '忽略' not in one_prize_name and prize_name in one_prize_name:
  307. return award_data;
  308. return None;
  309. def item_buy_normal(self, activity_id, sub_activity_id, sub_login_type, award_data):
  310. print("###item_buy_normal");
  311. activity_id = award_data["activityId"];
  312. prize_name = award_data["prizeName"];
  313. market_id = self.market_id;
  314. event_visitor_id = self.event_visitor_id;
  315. act_data_list = self.get_activity_items(market_id, activity_id, event_visitor_id);
  316. the_act_data = self.get_activity_data(activity_id, event_visitor_id);
  317. des_info = self.get_des_decode_info(activity_id, event_visitor_id);
  318. cate_act_type = sub_login_type;
  319. if des_info:
  320. if des_info["code"] != '0':
  321. # 5=已享这周首单优惠 7=人数过多稍后重试
  322. cate_act_type = 'subChoose'
  323. for act_data in act_data_list:
  324. act_type = act_data["type"]
  325. if act_type == cate_act_type:
  326. award_list = act_data["awardList"]
  327. new_award_item = self.check_pick_item(prize_name, award_list);
  328. if new_award_item:
  329. award_data = new_award_item;
  330. break;
  331. game_account = self.accout;
  332. third_info = json.loads(award_data["thirdInfo"]);
  333. award_id = award_data["awardId"];
  334. award_price = float(award_data["price"]);
  335. award_face_value = float(third_info["faceValue"]);
  336. award_data["faceValue"] = award_face_value;
  337. discount_price = self.get_award_expected_discount(award_face_value, prize_name);
  338. print(f"商品{prize_name}(面值:{award_face_value})\n匹配的最终价格:{award_price}\n预设折扣价格:{discount_price}#{award_id}");
  339. if award_price <= discount_price:
  340. activity_id = award_data["activityId"];
  341. pay_info = self.get_pay_info(activity_id, award_id, '', '', game_account, event_visitor_id);
  342. if pay_info:
  343. if "detailId" in pay_info:
  344. pay_ret = self.pay(activity_id, event_visitor_id, pay_info["detailId"]);
  345. if pay_ret:
  346. return award_data;
  347. else:
  348. print(f"未到预设折扣价格{discount_price},跳过~");
  349. return None;
  350. def get_activity_items(self, market_id, activity_id, event_visitor_id):
  351. url = f"https://{self.host}/h5/union/interactiveIGoChoose/marketIndexRebuild?marketId={market_id}&activityId={activity_id}&eventVisitorId={event_visitor_id}";
  352. print("请求活动商品列表数据");
  353. response = self.get_request(url);
  354. if response.status_code == 200:
  355. try:
  356. json_str = response.content;
  357. json_str = json_str.decode('utf-8');
  358. params = json.loads(json_str);
  359. if params.get('code') == 'success':
  360. print("请求活动商品列表数据成功");
  361. return params['data'];
  362. else:
  363. print(f"请求活动商品列表数据失败,响应:{json_str}");
  364. except Exception as e:
  365. print("请求活动商品列表发生错误");
  366. print(e);
  367. finally:
  368. pass
  369. else:
  370. print("请求活动商品列表发生错误");
  371. return None;
  372. def get_activity_data(self, activity_id, event_visitor_id):
  373. url = f"https://{self.host}/h5/api/mobile/activity/data?activityNo={activity_id}&eventVisitorId={event_visitor_id}";
  374. print("请求活动状态数据");
  375. response = self.get_request(url);
  376. if response.status_code == 200:
  377. try:
  378. json_str = response.content;
  379. json_str = json_str.decode('utf-8');
  380. params = json.loads(json_str);
  381. if params.get('code') == 'success':
  382. print("请求活动状态数据成功");
  383. return params['data'];
  384. else:
  385. print(f"请求活动状态数据失败,响应:{json_str}");
  386. except Exception as e:
  387. print("请求活动状态发生错误");
  388. print(e);
  389. finally:
  390. pass
  391. else:
  392. print("请求活动状态发生错误");
  393. return None;
  394. def get_des_decode_info(self, activity_id, event_visitor_id):
  395. url = f"https://{self.host}/h5/union/api/interactiveIGoChoose/getDesDecodeInfo?activityNo={activity_id}&eventVisitorId={event_visitor_id}";
  396. print("请求活动描述数据");
  397. response = self.get_request(url);
  398. if response.status_code == 200:
  399. try:
  400. json_str = response.content;
  401. json_str = json_str.decode('utf-8');
  402. params = json.loads(json_str);
  403. print("请求活动描述数据成功");
  404. print(json_str);
  405. return params;
  406. except Exception as e:
  407. print("请求活动描述发生错误");
  408. print(e);
  409. finally:
  410. pass
  411. else:
  412. print("请求活动描述发生错误");
  413. return None;
  414. def get_request(self, url, params=None):
  415. response = requests.get(url, headers=self.headers, params=params, cookies=self.cookies);
  416. return response;
  417. def post_request(self, url, data):
  418. response = requests.post(url, data=data, headers=self.headers, cookies=self.cookies);
  419. return response;
  420. def award_grab_worker(account_info, activities_data):
  421. global cache;
  422. init_cache();
  423. print('########账号[%s]开始抢券工作########' % account_info['account']);
  424. grabber = OfpayGrabber(account_info, activities_data);
  425. results = grabber.start();
  426. print('########################################');
  427. return f"账号[{account_info['account']}]任务完成";
  428. def main():
  429. cpu_threads = multiprocessing.cpu_count();
  430. print(f"CPU支持 {cpu_threads} 个线程/核心");
  431. activities_data = read_json('elife_activities_data.json');
  432. accout_data = read_csv('elife_accout_data.csv');
  433. if cpu_threads > 1:
  434. # 创建进程池
  435. pool = multiprocessing.Pool(cpu_threads);
  436. results = [];
  437. for item in accout_data:
  438. result = pool.apply_async(award_grab_worker,(item, activities_data,));
  439. results.append(result);
  440. # 关闭进程池,不再接受新的任务
  441. pool.close();
  442. # 等待所有任务完成
  443. pool.join();
  444. # for result in results:
  445. # print(result.get());
  446. else:
  447. global cache;
  448. init_cache();
  449. for item in accout_data:
  450. print('########账号[%s]开始抢券工作########' % item['account']);
  451. grabber = OfpayGrabber(item, activities_data);
  452. grabber.start();
  453. print('########################################');
  454. if __name__ == "__main__":
  455. main();