朋友让帮忙做一个自动抢某投资公司的理财产品预购

主要学到两点:

  1. 使用的目标地址是移动端的,没有cookies。通过在HTTPS头中添加 Authorization 字段,值为:'Bearer ’ + token,来判断登录用户。大概过程如下:

    1. 对登录地址发出post,带上用户名和密码(json),会返回一个token。
    2. 预约产品时需要用到用户信息,所以要先获取用户信息:在HTTPS头中加上Authorization 字段,值为’Bearer ’ + token(token即为第1步登录后服务器返回的token值),对用获取用户信息的地址发出get。
    3. 预约产品:同样需要在HTTPS头中带上token,并将用户信息和理财产品的id构造一个json data,并发出post。
    
    headers = {'Authorization': 'Bearer ' + token, #token添加在这里'Host': 'api.***.com','Accept': 'application/json, text/plain, */*','Content-Type': 'application/json;charset=UTF-8','Referer': 'https://m.***.com','Origin': 'https://m.***.com','User-Agent': self._get_user_agent()
    }async with aiohttp.ClientSession() as session:async with session.post(self.urls['order'], json=data, headers=self.headers, verify_ssl=False, timeout=self.time_out) as response:if response.status == 200:return await response.json()
    
  2. 虽然个小公司,产品抢购人不会太多,每次需要帮忙抢的人也不多(每期最多也就10来人),其实不用并发也是可以的,估算了一下,10人也就1-2秒的事。但最终还是开发了并发的版本,正好拿来练手python asyncio aiohttp。
    1. 大概流程是使用通过读取用户信息文件(json格式),然后所有用户登录、获取信息、预约,这三个先后进程,就使用共发。
    2. 因为服务器使用https,aiohttp会提示SSL错误,只要在get、post中使用verify_ssl=False就可以了。
    3. 并发使用的是asyncio.gather(),把所有的任务都封装成task并发运行了。但是在处理超时重发的情况下突然懵了,不知在并发的情况下如何再次发起。起初就想着怎么再把超时的请求重新放到asyncio.loop中,但是怎么都没有一个比较好的法子去获得结果。后来直接就在处理超时异常(aiohttp.ServerTimeoutError)时,再一次await 本身这个异步函数。

完整代码如下:
user.py

# -*- coding: utf-8 -*-
import random
import aiohttp
import asyncioclass User:"""用户number:注册的手机号码pwd:密码order_amt:预约金额urls: url列表:登录url,获取用户信息url,提交预约urlsemaphore: 并发数time_out: 超时retry_times: 重试次数"""def __init__(self, number, pwd, order_amt, urls, semaphore, time_out, retry_times):self.number = numberself.pwd = pwdself.order_amt = order_amtself.urls = urlsself.semaphore = semaphoreself.time_out = time_outself.RETRY_TIMES = retry_timesself.retry = retry_timesself.token = Noneself.login_msg = ''self.headers = {}self.msg = {}async def login(self):"""用户登录"""self.retry -= 1async with self.semaphore:try:async with aiohttp.ClientSession() as session:async with session.post(self.urls['login'], json={'phoneNo': self.number, 'password': self.pwd},verify_ssl=False, timeout=self.time_out) as response:if response.status == 200:return await response.json()else:return {'msg': 'response_status {}'.format(response.status)}except aiohttp.ServerTimeoutError as e:retry_msg = '{} 重试登录'.format(self.number)false_msg = '{} 登录失败'.format(self.number)await self._handle_timeout(self.login, self.parse_login, retry_msg, false_msg, e)except Exception as e:raise edef parse_login(self, task):"""解析登录结果,获取token,并构造headers"""self.retry = self.RETRY_TIMESdata = task.result()self.login_msg = data.get('msg', '')if self.login_msg == '登录成功':self.token = data['data']['token']self.make_headers()def _get_user_agent(self):"""返回随机User-Agent"""USER_AGENT_LIST = ["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60","Opera/8.0 (Windows NT 5.1; U; en)","Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50","Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0","Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11","Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36","Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER","Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER) ","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)","Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)"]return random.choice(USER_AGENT_LIST)def make_headers(self):"""构造requests的HTML头部信息,主要在于添加token"""if self.token:self.headers = {'Authorization': 'Bearer ' + self.token,'Host': 'api.***.com','Accept': 'application/json, text/plain, */*','Content-Type': 'application/json;charset=UTF-8','Referer': 'https://m.***.com','Origin': 'https://m.***.com','User-Agent': self._get_user_agent()}async def get_msg(self):"""通过服务器获取用户信息"""if self.headers:self.retry -= 1async with self.semaphore:try:async with aiohttp.ClientSession() as session:async with session.get(self.urls['getuser'], headers=self.headers,verify_ssl=False, timeout=self.time_out) as response:if response.status == 200:return await response.json()else:return {'msg': 'response_status {}'.format(response.status)}except aiohttp.ServerTimeoutError as e:retry_msg = '{} 重试读取用户信息'.format(self.number)false_msg = '{} 读取用户信息失败'.format(self.number)await self._handle_timeout(self.get_msg, self.parse_msg, retry_msg, false_msg, e)except Exception as e:raise edef parse_msg(self, task):"""解析用户信息"""self.retry = self.RETRY_TIMESdata = task.result()msg = data['msg']if msg == 'success':self.msg = data['data']else:print('get_user_msg server back_error: ', self.number, msg)raise Exception('get_user_msg server back_error: ', self.number, msg)async def order(self, product_id):"""认购"""if self.headers and self.msg:self.retry -= 1data = {"productId": product_id,"phoneNo": self.msg.get('phoneNo', ''),"certNo": self.msg.get('certNo', ''),"preOrderAmt": self.order_amt,"bankInfo": self.msg.get('bankInfo', ''),"bankCode": self.msg.get('bankCode', ''),"introducer": self.msg.get('introducer', ''),"custType": self.msg.get('custType', '')}async with self.semaphore:try:async with aiohttp.ClientSession() as session:async with session.post(self.urls['order'], json=data, headers=self.headers,verify_ssl=False, timeout=self.time_out) as response:if response.status == 200:return await response.json()else:return {'msg': 'response_status {}'.format(response.status)}except aiohttp.ServerTimeoutError as e:retry_msg = '{} 重试预约'.format(self.number)false_msg = '{} 预约失败'.format(self.number)await self._handle_timeout(self.order, self.parse_order, retry_msg, false_msg, e, product_id)except Exception as e:raise eelse:print('{} 认购失败:未登录或未获取用户信息'.format(self.number))raise Exception('{} 认购失败:未登录或未获取用户信息'.format(self.number))def parse_order(self, task):"""解析并打印预约结果"""self.retry = self.RETRY_TIMESdata = task.result()print('{} 结果:{}'.format(self.msg['custName'], data['msg']))async def _handle_timeout(self, func, callback, retry_msg, false_msg, e, para=None):if self.retry:print(retry_msg)if para:task = asyncio.create_task(func(para))else:task = asyncio.create_task(func())task.add_done_callback(callback)await taskelse:print(false_msg)self.retry = self.RETRY_TIMESraise e

users.py

# -*- coding: utf-8 -*-
import asyncio
from pprint import ppfrom .user import Userclass Users:"""用户集合类config: 配置信息:超时、重试次数、并发数、用户信息等"""def __init__(self, config):self.users = []self.logined, self.not_logined_msg = [], []time_out = config['time_out']retry_times = config['retry_times']semaphore = asyncio.Semaphore(config['concurrent_num'])for u in config['users']:user = User(u['number'], u['pwd'], u['order_amt'], config['urls'], semaphore, time_out, retry_times)self.users.append(user)async def login(self):"""登录所有用户,并分离登录成功或未成功的用户"""tasks = []for user in self.users:task = asyncio.create_task(user.login())task.add_done_callback(user.parse_login)tasks.append(task)await asyncio.gather(*tasks)self.logined = [user for user in self.users if user.token]self.not_logined_msg = ['{}: {}'.format(user.number, user.login_msg) for user in self.users ifuser.token is None]if self.not_logined_msg:print('\n登录失败的用户:\n\t{}'.format('\n\t'.join(self.not_logined_msg)))async def get_logined_users_msg(self):"""获取所有已登录用户在服务器上的信息"""tasks = []for user in self.logined:task = asyncio.create_task(user.get_msg())task.add_done_callback(user.parse_msg)tasks.append(task)await asyncio.gather(*tasks)def show_logined_users(self):"""显示已登录用户的信息"""print('已登录用户:{} 位'.format(len(self.logined)))for user in self.logined:pp(user.msg)print('\n')async def order(self, product_id):"""已登录的用户预约产品"""tasks = []for user in self.logined:task = asyncio.create_task(user.order(product_id))task.add_done_callback(user.parse_order)tasks.append(task)await asyncio.gather(*tasks)

orderer.py

# -*- coding: utf-8 -*-
from .users import Usersclass Orderer:"""预约抢购器类"""def __init__(self, config):self.product_id = ''self.users = Users(config)async def go(self):"""初始化所有用户:登录、获取用户服务器上的信息"""await self.users.login()await self.users.get_logined_users_msg()await self.run()async def run(self):"""预约抢购器主程序"""while True:print('\n操作选项:\n\t[1]-显示已登录用户信息\n\t[2]-输入产品ID\n\t[3]-开始预约 ({})\n\t[4]-退出'.format(self.product_id))choice = input('请选择操作:')if choice == '1':self.users.show_logined_users()elif choice == '2':self.product_id = input('请输入产品ID: ')elif choice == '3':if not self.product_id:print('\n*** 请先输入产品ID ***')else:await self.users.order(self.product_id)elif choice == '4':breakelse:print('\n*** 无效操作 ***')

main.py

# -*- coding: utf-8 -*-
"""
投资产品预定抢购 V3
2020-05-27
增加: 请求超时(5秒)重试
2020-05-22
修改: 并发抢购
修改: 通过配置文件读取用户信息、urls、并发数
"""
import os
import asyncio
from orderer import Orderer
from tools import init_logger, get_configlogger = init_logger('Production')
config_file = os.path.join(os.path.dirname(__file__), 'config.json')if __name__ == '__main__':try:config = get_config(config_file)order = Orderer(config)asyncio.run(order.go())except Exception as e:logger.error(e, exc_info=True)print(e)input('按任意键退出')

自动抢某投资公司的理财产品预购相关推荐

  1. 2021最新 python爬取12306列车信息自动抢票并自动识别验证码(三)购票篇

    项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否则后果自行 ...

  2. 2021最新python爬取12306列车信息自动抢票并自动识别验证码

    项目描述 项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否 ...

  3. python爬取12306列车信息自动抢票并自动识别验证码(一)列车数据获取篇

    项目前言 自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目--[12306-tiebanggg-master].注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一 ...

  4. python爬取12306列车信息自动抢票并自动识别验证码(二)selenium登录验证篇

    项目前言 自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时 ...

  5. 【python教程入门学习】自动抢票之 12306 登录篇

    逢年过节 12306 的票总是要靠抢,前几天小编就在抢周一去上海的票,实在是抢不到呀,就撸了一个自动抢票的脚本. 抢票的思路就是使用 selenium 模拟用户登录 12306 网站购票行为,登录后抓 ...

  6. 100个必会的python脚本-100行Python代码实现自动抢火车票(附源码)

    前言 又要过年了,今年你不妨自己写一段代码来抢回家的火车票,是不是很Cool.下面话不多说了,来一起看看详细的介绍吧. 先准备好: 12306网站用户名和密码 chrome浏览器及下载chromedr ...

  7. eclipse+adt下开发android微信红包自动抢(AccessibilityService类)

    纯粹是兴趣,google提供了android操作系统的钩子AccessibilityService类,用于监听我们手机的焦点.窗口变化.按钮点击.通知栏变化等.微信红包自动抢通过Accessibili ...

  8. python抢货程序_Python自动化xpath实现自动抢票抢货代码示例

    本篇文章小编给大家分享一下Python自动化xpath实现自动抢票抢货代码示例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 总代码: for i i ...

  9. python初学火车座位判断_初学Python实现学校图书馆座位自动抢座预约

    初学Python实现学校图书馆座位自动抢座预约 初学Python实现学校图书馆座位自动抢座预约 最近突然有个想利用python爬取学校图书馆预约的想法(因为图书馆单人的座位很难抢)可是没学过pytho ...

  10. 微信抢红包神器自动抢软件

    微信抢红包神器自动抢软件,支持企业红包,微信红包,还有各种红包雨,让你轻松秒变抢红包达人,微信抢红包神器自动抢最佳软件里面包括了各种形式,不需要root就能够轻松使用,轻轻松松获得红包,喜欢的玩家来试 ...

最新文章

  1. 侠客X官方网站成立,第一个内测版本即将放出,敬请期待.
  2. adnroid 打包问题 :compileReleaseJavaWithJavac
  3. 关于ASP.NET 中站点地图sitemap 的使用
  4. 基于管道模式的容器设计
  5. dnd 辅助 android,Easy DND
  6. 物联网Wi-Fi配网方式,你知道几种?
  7. (译文)在Ubuntu 14.04上成功运行ModelSim-Altera Starter Edition 10.1d
  8. 《Python快速入门》基础知识扫盲课
  9. 程序员如何应对中年危机?让编程变得不再重要
  10. 人生:沉得住气,方成得了器!
  11. 商城系统学习总结(1)——订单与库存在高并发场景下案例解析
  12. 不可描述⁄( ⁄•⁄ω⁄•⁄ )⁄!亚马逊用机器学习找有声书中情色片段
  13. upc组队赛5 Election of Evil【搜索】
  14. php禁止上传,PHP禁止某些类型的上传文件_PHP教程
  15. Oracle中Minus的使用
  16. android快牙原理,快牙是什么 快牙使用原理及使用教程
  17. 怎么登录163vip邮箱?163vip邮箱登录方式有哪些?
  18. Prescan 8.5.0 许可证过期(Could not checkout a valid license)
  19. Mysql错误代码1045
  20. c语言12cr,计算机二级C语言讲义12.doc

热门文章

  1. 2011新版车主宝典改进亲体验(Android版)
  2. MTK6577 编译报错
  3. 超级符号就是超级创意_超级食物
  4. 中科大计算机辅助图形实验室,Prof. Ligang Liu at USTC (中科大刘利刚教授)
  5. 根据拼音首字母进行过滤的combobox
  6. 戴尔电脑能升级鸿蒙系统嘛,戴尔台式机预装win10家庭版升级win10专业版方法教程...
  7. 程序员的终极幻想(三):做一只小小的蜗牛
  8. 050_Scrapy 爬虫框架 案例四大名著爬取
  9. 算法入门篇:排序算法(一)
  10. 《非常网管:网络管理从入门到精通(修订版)》——第1章 网络基础知识回顾1.1 计算机网络基础...