基礎知識學習
目標: 通過python程序實現自動登錄下單功能
知識點: Selenium + 雲打碼 + Python
學習鏈接:
需求分析
Chrome瀏覽器:71.0.3578.98_chrome_installer.exe
12306官網: https://www.12306.cn/index/index.html
# 選擇時間,點擊確定,查詢列表,獲取列表頁的請求URL
# 通過F12,查看當前頁面的請求URL:
https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E4%B8%8A%E6%B5%B7,SHH&ts=%E6%88%90%E9%83%BD,CDW&date=2019-02-03&flag=N,N,Y
# 查看請求參數
# 選擇一條車次數據,查看該列表(一條tr一條數據)
# 獲取時間元素
# 查看預定按鈕
# 當根據時間選擇好了列車后,點擊預定按鈕就會提示我們登錄賬號(注意是Ajax請求,所以需要until查找)
# 查找用戶名和密碼登錄框的ID
# 截取驗證碼
安裝插件 pip3 install Pillow
# 獲取全屏圖片 –> 計算截圖位置(根據元素的寬和高確定大小) –> 截取所需要圖片的大小
# 利用雲打碼進行驗證碼解析
# 定義打點(模擬選中圖片)
# 點擊登錄
# 選擇需要買票的人
# 點擊提交
完整代碼
12306.py
# coding: utf-8
import os
import time
import json
# 圖片操作對象
from PIL import Image
# 將二進制文件轉換為IO流輸出
from io import BytesIO
import yundama
from selenium import webdriver
# 1. 導入模塊
from selenium import webdriver
# 1> 等待對象模塊
from selenium.webdriver.support.wait import WebDriverWait
# 2> 導入等待條件模塊
from selenium.webdriver.support import expected_conditions as EC
# 3> 導入查詢元素模塊
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
browser = webdriver.Chrome("chromedriver")
# 12306是異步請求,所以使用selenium的顯性等待方案
wait = WebDriverWait(browser, 10, 0.5) # 創建等待對象
# 請求參數
linktypeid = 'dc'
fs = '上海,SHH'
ts = '成都,CDW'
date ='2019-02-03'
flag = 'N,N,Y'
# 請求URL:
# https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=上海&ts=北京&date=2019-02-03&flag=N,N,Y
base_url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid={}&fs={}&ts={}&date={}&flag={}"
url = base_url.format(linktypeid, fs, ts, date, flag)
# 訪問12306的列表頁面
browser.get(url)
# 通過時間判定,選擇點擊預訂
# 尋找tr標簽中,屬性id以ticket開頭的數據
tr_list = wait.until(EC.visibility_of_all_elements_located((By.XPATH, "//tr[starts-with(@id, 'ticket_')]"))) # 找到所有可見元素
for tr in tr_list:
count = 0
t_start = tr.find_element_by_class_name('start-t').text
with open('start.txt', 'a') as f:
f.write(t_start)
# 判斷時間是否在符合條件的范圍內
if count < 5 and t_start == "11:16": # 這里以06:33為例
tr.find_element_by_class_name('btn72').click()
break
else:
count += 1
continue
# 點擊賬號(注意因為是異步加載的所有需要顯性等待)
# browser.find_element_by_link_text("賬號登錄").click() # 因為還沒有加載出來,因為是Ajax請求,所以要用until查找
wait.until(EC.visibility_of_element_located((By.LINK_TEXT, "賬號登錄"))).click()
# 打開json文件,
with open('account.json', 'r', encoding='utf-8') as f:
account = json.load(f, encoding='utf-8')
# 輸入用戶名和密碼
j_username = browser.find_element_by_id('J-userName').send_keys(account['username'])
j_password = browser.find_element_by_id('J-password').send_keys(account['password'])
# 獲取全屏圖片
full_img_data = browser.get_screenshot_as_png()
# 計算截圖位置
login_img_element = browser.find_element_by_id("J-loginImg")
# 獲取截圖元素對象
scale = 1.0
x1 = login_img_element.location["x"] - 155
y1 = login_img_element.location["y"] - 100
x2 = x1 + login_img_element.size["width"]
y2 = y1 + login_img_element.size["height"]
cut_info = (x1, y1, x2, y2)
print('cut_info', cut_info)
# 把全屏圖片構建成全屏圖片操作對象
full_img = Image.open(BytesIO(full_img_data))
# 通過截圖信息對象截圖圖片
cut_img = full_img.crop(cut_info)
# 把圖片保存到本地
cut_img.save('cut_img.png')
time.sleep(5)
# 利用雲打碼進行圖片解析
result = yundama.decode('cut_img.png', '6701')
print('Image Decode:', result)
# 定義8個點擊坐標點
positions = [
(80, 140),
(230, 140),
(380, 140),
(530, 140),
(80, 280),
(230, 280),
(380, 280),
(530, 280)
]
# 模擬點擊坐標
for num in result:
position = positions[int(num) - 1]
# ActionChains 動作對象
ActionChains(browser).move_to_element_with_offset(login_img_element,position[0] / 2,position[1] / 2).click().perform()
print(position[0], position[1], "點擊圖片完成")
time.sleep(5)
# 點擊登錄
browser.find_element_by_id("J-login").click()
# 點擊選擇人物
wait.until(EC.visibility_of_element_located((By.ID, "normalPassenger_1"))).click()
# 點擊提交訂單
browser.find_element_by_id('submitOrder_id').click()
time.sleep(2)
# 點擊確認訂單
wait.until(EC.visibility_of_element_located((By.ID, 'qr_submit_id'))).click()
print("搶票成功,請支付")
time.sleep(5)
browser.quit()
account.json
{
"username":"18XXXXXXXXXX",
"password":"18XXXXXXXXXX"
}
yundama.json
import http.client, mimetypes, urllib, json, time, requests
######################################################################
class YDMHttp:
apiurl = 'http://api.yundama.com/api.php'
username = ''
password = ''
appid = ''
appkey = ''
def __init__(self, username, password, appid, appkey):
self.username = username
self.password = password
self.appid = str(appid)
self.appkey = appkey
def request(self, fields, files=[]):
response = self.post_url(self.apiurl, fields, files)
response = json.loads(response)
return response
def balance(self):
data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid,
'appkey': self.appkey}
response = self.request(data)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['balance']
else:
return -9001
def login(self):
data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid,
'appkey': self.appkey}
response = self.request(data)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['uid']
else:
return -9001
def upload(self, filename, codetype, timeout):
data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid,
'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
file = {'file': filename}
response = self.request(data, file)
if (response):
if (response['ret'] and response['ret'] < 0):
return response['ret']
else:
return response['cid']
else:
return -9001
def result(self, cid):
data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid,
'appkey': self.appkey, 'cid': str(cid)}
response = self.request(data)
return response and response['text'] or ''
def decode(self, filename, codetype, timeout):
cid = self.upload(filename, codetype, timeout)
if (cid > 0):
for i in range(0, timeout):
result = self.result(cid)
if (result != ''):
return cid, result
else:
time.sleep(1)
return -3003, ''
else:
return cid, ''
def report(self, cid):
data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid,
'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
response = self.request(data)
if (response):
return response['ret']
else:
return -9001
def post_url(self, url, fields, files=[]):
for key in files:
files[key] = open(files[key], 'rb');
res = requests.post(url, files=files, data=fields)
return res.text
def decode(filename, codetype):
######################################################################
# 用戶名
username = 'XXXXXXXXX'
# 密碼
password = 'XXXXXXXX'
# 軟件ID,開發者分成必要參數。登錄開發者后台【我的軟件】獲得!
appid = 6795
# 軟件密鑰,開發者分成必要參數。登錄開發者后台【我的軟件】獲得!
appkey = '62a6760f83a91f818be141d9a77463c5'
# 圖片文件
# filename = 'getimage.jpg'
# 驗證碼類型,# 例:1004表示4位字母數字,不同類型收費不同。請准確填寫,否則影響識別率。
# 在此查詢所有類型 http://www.yundama.com/price.html
# codetype = 3000
# 超時時間,秒
timeout = 60
# 檢查
if (username == 'username'):
print('請設置好相關參數再測試')
else:
# 初始化
yundama = YDMHttp(username, password, appid, appkey)
# 登陸雲打碼
uid = yundama.login();
print('uid: %s' % uid)
# 查詢余額
balance = yundama.balance();
print('balance: %s' % balance)
# 開始識別,圖片路徑,驗證碼類型ID,超時時間(秒),識別結果
cid, result = yundama.decode(filename, codetype, timeout);
print('cid: %s, result: %s' % (cid, result))
return result
######################################################################
















