内容简介:有一个朋友拜托我开发一个抢票类的工具,刚好最近有看python3的书籍,顺便练下手便答应了她。题外话:是某公司CRM系统中的客户预约功能,购买额度200万以下的金融产品很不容易预约上(而500万的产品不需要抢),全国每个产品也就几个名额。由于我朋友去到公司不久(新人)200万以下的产品是她收入和业绩的主要来源了。 写下本文的目的也仅仅是把涉及到各方面的要点记录(踩过的坑)下来,希望能帮助到初学者。一开始觉得不就是个拼手速的工具嘛,于是使用了selenium来模拟人的操作,工具很快写完,刚好100行代码。遇到
有一个朋友拜托我开发一个抢票类的工具,刚好最近有看 python 3的书籍,顺便练下手便答应了她。题外话:是某公司CRM系统中的客户预约功能,购买额度200万以下的金融产品很不容易预约上(而500万的产品不需要抢),全国每个产品也就几个名额。由于我朋友去到公司不久(新人)200万以下的产品是她收入和业绩的主要来源了。 写下本文的目的也仅仅是把涉及到各方面的要点记录(踩过的坑)下来,希望能帮助到初学者。
- selenium中switch_to()的使用
- requests中如何模拟登录用户
- selenium + requests 实现无所不能操作
- pyqt与qml文件通讯
- UI卡死与多线程
开发迭代过程
第一版:龟速自动操作——selenium
一开始觉得不就是个拼手速的 工具 嘛,于是使用了selenium来模拟人的操作,工具很快写完,刚好100行代码。遇到过的坑:
- 由于网页中采用了frameset结构,采用switch_to()方法,需要注意相对位置。
iframes = self.driver.driver.find_elements_by_tag_name('iframe')
iframe1 = iframes[1]
print('获取预约页面地址:' + iframe1.get_attribute('src'))
self.driver.driver.switch_to.frame(iframe1) # 切换到产品预约页iframe
复制代码
- 解决xss引起的chrome报错
chrome_opt = Options()
chrome_opt.add_argument('--disable-xss-auditor')
self.driver_name = 'chrome'
self.driver = Browser(driver_name=self.driver_name,chrome_options=chrome_opt)
复制代码
在实际抢的过程中,却还是没有抢到,虽然是比人快了不少,看来需要加速,让我想到了requests。
第二版:极速手动操作——requests
不出所料,速度还是很快,不过由于此CRM系统的,对其他行业的人来讲根本操作不来(需通过审查元素获取cookies、产品搜索与预约产品的url等)
# 准备搜索
postdata = {
'start': '0',
'Search': '1',
'Key': product_name,
'loadStore': 'true',
'extResponse': 'true',
}
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:65.0) Gecko/20100101 Firefox/65.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': header_cookies
}
search_count = 0
while True:
rep = s.post(search_prod, data=postdata, headers=headers)
product_json = json.loads(rep.text)
search_count = search_count + 1
if search_count % 10 == 1:
print('正在进行第(%d)次搜索...' % search_count)
try:
results = product_json['results']
if results > 1:
print('错误:查出多条产品,请退出后重新输入产品名称')
break
elif results == 1: # 找到产品
proudct_id = product_json['records'][0]['id']
proudct_CPJC = product_json['records'][0]['CPJC']
print('>>>找到预约产品:id:'+proudct_id+'CPJC:'+proudct_CPJC)
break
elif results == 0:
# 循环读取
time.sleep(0.001)
except json.decoder.JSONDecodeError as e:
print('参数不正确')
exit(0);
复制代码
但由于此CRM系统的URL也是动态的,含有操作码oprateId(各个页面还不同,且动态改变,没找到规律),只能从审查元素中去找到对应的URL和模拟header等信息(很短时间才有效)。另外不可能每次我来帮她抢啊,于是就有了selenium+requests的想法
第三版:无所不能的组合——selenium+requests
在搜索产品和提交预约之前通过selenium获取cookies和页面地址上的operateId和token。
通过selenium的get_cookies()获取cookies
cookies = self.driver.driver.get_cookies()
for cookie in cookies:
if cookie['name'] == 'JSESSIONID':
self.jsessionid = cookie['value']
break
print('cookie信息:')
print('jsessionid:' + self.jsessionid)
复制代码
通过selenium的switch_to()获取页面地址上的operateId和token
iframe = self.driver.driver.find_element_by_tag_name('iframe')
self.driver.driver.switch_to.frame(iframe) # 切换到主页下半部iframe
self.driver.click_link_by_text("产品预约")
time.sleep(1)
self.driver.driver.switch_to.parent_frame()
iframes = self.driver.driver.find_elements_by_tag_name('iframe')
iframe1 = iframes[1]
# print('获取预约页面地址:' + iframe1.get_attribute('src'))
self.driver.driver.switch_to.frame(iframe1) # 切换到产品预约页iframe
self.driver.click_link_by_id('ext-gen32') # 点开搜索页
time.sleep(1)
self.driver.driver.switch_to.parent_frame()
iframes = self.driver.driver.find_elements_by_tag_name('iframe')
iframe2 = iframes[2]
search_url = iframe2.get_attribute('src')
# print('获取预约页面地址:' + search_url)
parsed_search_url = urllib.parse.urlparse(search_url)
# print(parsed_search_url)
query_str = parsed_search_url.query
query_parms = query_str.split('&')
dict_query = self._parseQuery(query_parms) # 处理url参数
token = dict_query['Token']
operateid = dict_query['OperateID']
self.Token = token
# self.SearchOperateID = operateid
self.YuyueOperateID = operateid
print('获取Token:' + self.Token)
print('获取预约页面操作码:' + self.YuyueOperateID)
复制代码
此版很接近完美的实现了自动化的登录(验收码还是需要手动收入)、自动搜索产品,当放出产品的时候自动预约。经测试4个产品全部预约到。不过登录用户名、密码、客户手机、预约金额、以及准备预约的产品都写在python文件中的。让她改几个字(居然说是让她写代码,我服了),本来想通过一个配置文件解决。但想到python的UI操作还没试过(很久很久以前用过C语言+GTK),于是想试下pyqt+QT creator(模版只想可视化操作的)。
第四版:把程序装进壳里——PYQT+Qt Creator
Qt Creator 的设计目标是使开发人员能够利用 Qt 这个应用程序框架更加快速及轻易的完成开发任务。Qt Creator 包括项目生成向导、高级的 C++ 代码编辑器、浏览文件及类的工具、集成了 Qt Designer、Qt Assistant、Qt Linguist、图形化的 GDB 调试前端,集成 qmake 构建工具等。(百度百科) Qt Creator 可以创建多种工程,我选择的是qml文件,很快做好了qml文件,但是如何与python通讯呢?百度了下都没找到多少系统的文章,官方教程也没讲到(英文) doc.qt.io/qtforpython…
- 在界面触发事件调用python, 需在python中用pyqtSlot()申明为槽函数 ,并设置上下文关联,这样qml文件中就可以直接调用。
@pyqtSlot() # qml中可调用
def begin(self):
#代码略
pass
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
qml = QQmlApplicationEngine('ui.qml')
rootObject = qml.rootObjects()[0]
instance = Reserve(qml.rootContext() ,rootObject) #预约实例
qml.rootContext().setContextProperty('con',instance ) #与qml文件建立关联
sys.exit(app.exec())
复制代码
qml文件中绑定事件,触发调用python的槽函数
Connections {
target: button_start
onClicked: con.begin()
}
复制代码
- 如何主动更改ui界面中的值呢?例如之前用的print打印日志,现在需要全部显示到界面上 在qml文件中自定义方法,类似javascript
function updatelog(log) {// 定义函数
textArea.append(log)
}
function clearlog() {
textArea.clear()
}
复制代码
然后可直接
def __init__(self,context,parent=None):
super(Reserve,self).__init__(parent)
self.win = parent
self.ctx = context
#可直接调用qml中方法
self.win.showlog("测试日志")
复制代码
好啦,界面也有了,与程序也封装好了,开始抢票吧,怎么回事?界面卡死了。之前学习时候知道需要将界面与程序使用线程分开。 首先创建一个线程类
class WorkThread(QThread):
signal = pyqtSignal(type(""))
clearsignal = pyqtSignal()
message=""
yuyue=""
def __int__(self,parent=None):
super(WorkThread,self).__init__(parent)
def __del__(self):
self.wait()
#设置
def setup(self, instance):
self.yuyue = instance
#内部/外部(线程)使用的输出信息
def log(self,message):
self.signal.emit(message)
def run(self):
self.yuyue.config()
self.yuyue.login()
self.yuyue.start()
# 执行完毕后发出信号
self.log("运行完毕")
复制代码
在主程序_Init__方法中,启动线程,这里两个信号(signal),一个用于打印日志,一个用于清空日志(日志达到某个阀值)
def __init__(self,context,parent=None):
super(Reserve,self).__init__(parent)
self.win = parent
self.ctx = context
chrome_opt = Options()
chrome_opt.add_argument('--disable-xss-auditor') # 解决xss引起的chrome报错。
self.driver_name = 'chrome'
self.driver = Browser(driver_name=self.driver_name,chrome_options=chrome_opt)
#启动一个线程,并设置连接通道
self.thread = WorkThread()
self.thread.signal.connect(self.callbacklog)
self.thread.clearsignal.connect(self.callbackclear)
#向通道发送信息
self.thread.log("初始化完成")
# 保存session
self.s = requests.session()
# 槽函数(通道末端)
def callbacklog(self, log):
self.win.updatelog(log) # 调用qml中的方法
pass
def callbackclear(self):
self.win.clearlog() # 调用qml中的方法
pass
复制代码
这样线程原来使用print()打印日志的方法全部替换成self.thread.log()即可。运行界面一点都不卡了。
结束语:对python新手来说涉及到面还不少,虽然不少坑收获还是不少。我在身边很多朋友眼中一直都是大神一样的存在(其实我知道这些都是小把戏),我朋友在完成第二版的时候说过一句话让我很欣慰:
你把我的梦境变成现实了,太厉害了。
当然还有可以改进的地方,比如验证码完全可以不用手动输入,可以利用机器学习,对验证码进行训练,然后自动识别。不过真的没必要了。就她一个人用确实没必要折腾了。
以上所述就是小编给大家介绍的《用PYTHON初次编写小工具心得》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Intersectional Internet
Safiya Umoja Noble、Brendesha M. Tynes / Peter Lang Publishing / 2016
From race, sex, class, and culture, the multidisciplinary field of Internet studies needs theoretical and methodological approaches that allow us to question the organization of social relations that ......一起来看看 《The Intersectional Internet》 这本书的介绍吧!