有一個朋友拜託我開發一個搶票類的工具,恰好最近有看python3的書籍,順便練下手便答應了她。題外話:是某公司CRM系統中的客戶預定功能,購買額度200萬如下的金融產品很不容易預定上(而500萬的產品不須要搶),全國每一個產品也就幾個名額。因爲我朋友去到公司不久(新人)200萬如下的產品是她收入和業績的主要來源了。 寫下本文的目的也僅僅是把涉及到各方面的要點記錄(踩過的坑)下來,但願能幫助到初學者。javascript
一開始以爲不就是個拼手速的工具嘛,因而使用了selenium來模擬人的操做,工具很快寫完,恰好100行代碼。遇到過的坑:html
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
複製代碼
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。前端
不出所料,速度仍是很快,不過因爲此CRM系統的,對其餘行業的人來說根本操做不來(需經過審查元素獲取cookies、產品搜索與預定產品的url等)java
# 準備搜索
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的想法python
在搜索產品和提交預定以前經過selenium獲取cookies和頁面地址上的operateId和token。web
經過selenium的get_cookies()獲取cookieschrome
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和tokenjson
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(模版只想可視化操做的)。cookie
Qt Creator 的設計目標是使開發人員可以利用 Qt 這個應用程序框架更加快速及輕易的完成開發任務。Qt Creator 包括項目生成嚮導、高級的 C++ 代碼編輯器、瀏覽文件及類的工具、集成了 Qt Designer、Qt Assistant、Qt Linguist、圖形化的 GDB 調試前端,集成 qmake 構建工具等。(百度百科) Qt Creator 能夠建立多種工程,我選擇的是qml文件,很快作好了qml文件,可是如何與python通信呢?百度了下都沒找到多少系統的文章,官方教程也沒講到(英文)doc.qt.io/qtforpython…session
@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()
}
複製代碼
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新手來講涉及到面還很多,雖然很多坑收穫仍是很多。我在身邊不少朋友眼中一直都是大神同樣的存在(其實我知道這些都是小把戲),我朋友在完成第二版的時候說過一句話讓我很欣慰:
你把個人夢境變成現實了,太厲害了。
固然還有能夠改進的地方,好比驗證碼徹底能夠不用手動輸入,能夠利用機器學習,對驗證碼進行訓練,而後自動識別。不過真的不必了。就她一我的用確實不必折騰了。