登录页面:https://kyfw.12306.cn/otn/login/init
查票页面:https://kyfw.12306.cn/otn/leftTicket/init
这一篇是对上一篇Python基于selenium的12306模拟登陆的功能拓展,可以实现模拟登陆+自动抢票,考虑到模拟登陆耗时较久且意义不大,所以增加了手动登陆的选项,需要手动点击验证码。抢票时可以自定义的参数包括出发地、目的地、车次、乘坐人和座位类型,当没有余票时将不断刷新页面直到出现余票。该软件由两个.py文件构成,其中ops.py负责对验证码识别并返回识别结果,如果是手动登陆就无需该模块,另外一个main.py为主程序。
main.py:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import ActionChains
from ops import getVerifyResult
from PIL import Image
from time import sleep
class Main():
def __init__(self):
self.usrname = '你的12306账号'
self.password = '你的12306密码'
self.fromStation = "南京"
self.toStation = "沈阳"
self.train_date = "2020-05-04"
self.train = 'G1228'
self.seat_code = {'一等座': 'M', '二等座': 'O', '硬座': '1',
'硬卧': '3', '软卧': '4', '商务座': '9'}
self.choice = '二等座'
self.driver = webdriver.Chrome(executable_path='./chromedriver.exe')
def login(self):
self.driver.get('https://kyfw.12306.cn/otn/login/init')
sleep(2)
self.driver.save_screenshot('main.png')
img_tag = self.driver.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
location = img_tag.location
size = img_tag.size
rangle = (int(location['x']) * 1.25, int(location['y']) * 1.25, int(location['x'] + size['width']) * 1.25,
int(location['y'] + size['height']) * 1.25)
img = Image.open('main.png')
frame = img.crop(rangle)
frame.save('code.png')
result = getVerifyResult('code.png')
for xy in result:
x = xy[0] * 0.8
y = xy[1] * 0.8
ActionChains(self.driver).move_to_element_with_offset(img_tag, x, y).click().perform()
sleep(1)
self.driver.find_element_by_id('username').send_keys(self.usrname)
sleep(1)
self.driver.find_element_by_id('password').send_keys(self.password)
sleep(1)
self.driver.find_element_by_id('loginSub').click()
while self.driver.current_url == 'https://kyfw.12306.cn/otn/login/init' \
or self.driver.current_url == 'https://kyfw.12306.cn/otn/login/init#':
print('等待登陆中...')
sleep(3)
def manual_login(self):
self.driver.get('https://kyfw.12306.cn/otn/login/init')
self.driver.find_element_by_id('username').send_keys(self.usrname)
sleep(1)
self.driver.find_element_by_id('password').send_keys(self.password)
sleep(1)
print('请手动点击验证码登陆')
while self.driver.current_url == 'https://kyfw.12306.cn/otn/login/init'\
or self.driver.current_url == 'https://kyfw.12306.cn/otn/login/init#':
print('等待登陆中...')
sleep(3)
def queryTicket(self):
self.driver.get("https://kyfw.12306.cn/otn/leftTicket/init")
fromStation = self.driver.find_element_by_id('fromStationText')
fromStation.click()
fromStation.clear()
fromStation.send_keys(self.fromStation + '\n')
toStation = self.driver.find_element_by_id('toStationText')
toStation.click()
toStation.clear()
toStation.send_keys(self.toStation + '\n')
self.driver.execute_script("document.getElementById('train_date').removeAttribute('readonly')")
trainDate = self.driver.find_element_by_id("train_date")
trainDate.clear()
trainDate.send_keys(self.train_date)
self.driver.find_element_by_id("query_ticket").click()
def orderTicket(self):
WebDriverWait(self.driver, 100).until(
EC.presence_of_element_located((By.CLASS_NAME, 'number')))
trains = self.driver.find_elements_by_class_name("number")
for i, item in enumerate(trains):
name = item.text
if name == self.train:
self.driver.find_elements_by_class_name("btn72")[i].click()
break
else:
print('该日没有指定的车次')
return
ul = WebDriverWait(self.driver, 100).until(
EC.presence_of_element_located((By.ID, 'normal_passenger_id')))
sleep(1)
lis = ul.find_elements_by_tag_name('li')
for i, item in enumerate(lis):
print("【{}】{}".format(i, item.find_elements_by_tag_name("label")[0].text))
num = int(input('请选择乘车人:'))
ul.find_elements_by_tag_name('input')[num].click()
code = self.seat_code[self.choice]
seatSelect = self.driver.find_element_by_id("seatType_1")
print('------余票查询中------')
count = 1
flag = False
while True:
print('第{}次查询'.format(count))
count += 1
for i, item in enumerate(seatSelect.find_elements_by_tag_name("option")):
if item.get_attribute("value") == code:
flag = True
item.click()
break
if flag:
break
self.driver.back()
sleep(1)
self.driver.forward()
ul = WebDriverWait(self.driver, 100).until(
EC.presence_of_element_located((By.ID, 'normal_passenger_id')))
sleep(1)
ul.find_elements_by_tag_name('input')[num].click()
seatSelect = self.driver.find_element_by_id("seatType_1")
self.driver.find_element_by_id('submitOrder_id').click()
print('预定成功,请及时付款')
def __call__(self, *args, **kwargs):
print('请选择登陆方式:1、自动登陆 2、手动登陆')
option = input('请选择:')
if option == '1':
self.login()
else:
self.manual_login()
self.queryTicket()
self.orderTicket()
if __name__ == '__main__':
main = Main()
main()
ops.py:
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
def transformImgCode(imgPath, imgType):
chaojiying = Chaojiying_Client('你的超级鹰账号', '你的超级鹰密码', '96001') # 用户中心>>软件ID 生成一个替换 96001
im = open(imgPath, 'rb').read()
return chaojiying.PostPic(im, imgType)['pic_str']
def getVerifyResult(img_path):
result = transformImgCode(img_path, 9004)
all_list = []
if '|' in result:
list_1 = result.split('|')
for i in range(len(list_1)):
xy_list = []
x = int(list_1[i].split(',')[0])
y = int(list_1[i].split(',')[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
xy_list = []
x = int(result.split(',')[0])
y = int(result.split(',')[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
return all_list
上一篇Python基于selenium的12306模拟登陆:https://blog.csdn.net/Divine0/article/details/105829700