Python 使用 selenium 实现半自动购买12306火车票

实现原理:

1. 使用 selenium 模拟自动打开 chrome浏览器;

2. (浏览器打开后)会自动填写账号密码,手动选择验证码并提交;

3. 自动跳转选择“预定”及其单程购票设置;

4. 循环查询余票:先按顺序查询所有车次二等座,没有再按顺序查询无座,如此循环;

5. 发现余票后自动预定,并选择第一个乘客提交订单。

注意:提交订单后若没有余票,不会自动返回(没写完整)。此外,如果失败,又得重头再来,尝试多次频繁登录会导致账号短时间内不能登录。


验证码功能虽然没有实现,不过也大概了解了其验证原理:

验证码图片实为一张图(如下图),图片是动态的,地址为:

https://kyfw.12306.cn/passport/captcha/captcha-image

示例:https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.547952507783088

该图片分上下两块区域:上部分为文字,下部分为各图片组合。(可判断这图片是固定的,可爬取所有验证码下来做自动识别)


当我们点选下面的答案中的小图时,页面脚本会识别选择点的像素位置,生成一个 div 标签行,添加到表单中(即添加在验证码图片div内),如下图。


点击登录,首先进行验证码校验,后台有相应的接口来返回验证结果,校验成功才进行登录表单提交。

https://kyfw.12306.cn/passport/captcha/captcha-check


暂时只能人工识别验证码,主要是速度快,方便自己而已。如果需要特定的其他查询选择,则需要更完善些。这里没有这么完善。

#-*- coding: utf-8 -*-
# python 3.5.0

import re
import time
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

class Main(object):
	def __init__(self):
		self.username = "账号"
		self.password = "密码"
		self.fromStation = "深圳"
		self.toStation = "南宁"
		self.train_date = "2018-02-14"
		self.train = ['G2912','G2914','G2916'] #优先顺序
		
		self.loginurl = "https://kyfw.12306.cn/otn/login/init"
		#_chromedriver = "D:/Python35/selenium/phantomjs/bin/phantomjs.exe"
		#driver = webdriver.PhantomJS(_chromedriver)
		self.chromedriver = 'D:/Python35/selenium/webdriver/chromedriver/chromedriver.exe'
		self.driver = webdriver.Chrome(self.chromedriver)
	
	def setUrl(self,url):
		self.loginurl  = url
	
	def getUrl(self):
		print("[0]打开浏览器")
		self.driver.get(self.loginurl)
		time.sleep(2)

	def setUserPwd(self):
		print("[1]设置登录账号")
		self.driver.find_element_by_id("username").send_keys(self.username)
		self.driver.find_element_by_id("password").send_keys(self.password)

	#此处在浏览器界面手动点击验证码并登录!
	def waitingNewPage(self):
		print("[2]请手动点击登录")
		while self.loginurl == self.driver.current_url:
			print("[*]login waiting……")
			time.sleep(3)

	def selectYuding(self):
		print("[3]点击车票预订")
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'selectYuding'))).click()
		time.sleep(1)
	
	def selectOptions(self):
		print("[4]点击单程")
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'dc_label'))).click()
		print("[5]点击普通票")
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'query_ticket'))).click()
		
	def setQueryLeftForm(self):
		print("[6]设置信息")
		#出发地
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'fromStation_icon_image'))).click()
		self.driver.find_element_by_xpath("//li[@title='%s'][@data='SZQ']" % self.fromStation).click()
		#目的地
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'toStation_icon_image'))).click()
		self.driver.find_element_by_xpath("//li[@title='%s'][@data='NNZ']" % self.toStation).click()
		#出发日
		year = self.train_date.split('-')[0]
		month = self.train_date.split('-')[1]
		day = self.train_date.split('-')[2]
		y = 4 if year == "2017" else 5 if year == "2018" else 6
		#m = int(month) - 1
		#d = int(day)-1
		m = int(month)
		d = int(day)
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'date_icon_1'))).click()
		self.driver.find_element_by_xpath("//div[@class='year']/input").click()
		self.driver.find_element_by_xpath("//div[@class='year']/div/ul/li[%s]" % y).click()
		self.driver.find_element_by_xpath("//div[@class='month']/input").click()
		self.driver.find_element_by_xpath("//div[@class='month']/ul/li[%s]" % m).click()
		self.driver.find_element_by_xpath("//div[@class='cal']/div[@class='cal-cm']/div[%s]/div" % d).click()

	def queryTicket(self):
		print("[7]点击查询")
		WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID,'query_ticket'))).click()
		time.sleep(1)
	
		
	#总车次数
	def getTraiNum(self):
		#self.driver.find_element_by_xpath("//div[@class='sear-result']/p[1]/strong[2]").text.strip()  
		return self.driver.find_element_by_id('trainum').text.strip()  
	
	def click(self,xpath):
		self.driver.find_element_by_xpath(xpath).click()
	
	def checkTicket(self,tx,zuo,name):
		#是否是自己选择的车次,是则检查是否有二等座,有则预订
		for t in self.train:
			if re.match(t, tx):
				if zuo == "--" or zuo == "无":
					pass
				else:
					print("【可预订车次】%s [%s]" % (tx,name))
					return 1
			else:
				pass
		return 0
	
	
	#遍历车次
	def checkTicketAll(self):
		click_xpath = ""
		mark = 0
		trainum = self.getTraiNum()
		print("【共计 %s 个车次】" % trainum)
		id = 1
		while id <= 2*int(trainum): #第一次遍历所有车次的二等座
			xpath = "//tbody[@id='queryLeftTable']/tr[%s]" % id
			tx = self.driver.find_element_by_xpath(xpath + "/td[1]").text.strip().replace('\n', ',') #车次信息
			ze = self.driver.find_element_by_xpath(xpath + "/td[4]").text.strip() #二等座
			mk = self.checkTicket(tx,ze,"二等座")
			click_xpath = xpath + "/td[13]/a"
			id = id + 2
			if mk == 1:
				mark = 1
				return mark,click_xpath
			
		id = 1
		while id <= int(trainum): #第二次遍历所有车次的无座
			xpath = "//tbody[@id='queryLeftTable']/tr[%s]" % id
			tx = self.driver.find_element_by_xpath(xpath + "/td[1]").text.strip().replace('\n', ',') #车次信息
			wz = self.driver.find_element_by_xpath(xpath + "/td[11]").text.strip() #无座
			mk = self.checkTicket(tx,wz,"无座")
			click_xpath = xpath + "/td[13]/a"
			id = id + 2
			if mk == 1:
				mark = 1
				return mark,click_xpath
		
		return mark,click_xpath

		
	def checkTicketLoop(self):
		click_xpath = ""
		mark = 0
		mark,click_xpath = self.checkTicketAll()
		while mark == 0:
			self.queryTicket()
			time.sleep(2) #几秒刷新一次??
			mark,click_xpath = self.checkTicketAll()
			print("--------------------------------------------------")
		
		#点击预订
		if 	mark == 1:
			print("[8]点击预定")
			self.click(click_xpath)
	
	#选择乘客,提交订单
	def submitOrder(self):
		try:
			print("[9]选择乘客")
			passenger = WebDriverWait(self.driver, 5).until(EC.presence_of_element_located((By.ID, 'normalPassenger_0')))
			passenger.click()
			print("[10]提交订单")
			submit = WebDriverWait(self.driver, 5).until(EC.presence_of_element_located((By.ID, 'submitOrder_id')))
			submit.click()
		except TimeoutException:
			print("Loading took too much time!")
		time.sleep(1)
	
	#检查余票
	def checkTicketNum(self):
		ze = self.driver.find_element_by_xpath("//p[@id='sy_ticket_num_id']/strong[1]").text.strip()  
		wz = self.driver.find_element_by_xpath("//p[@id='sy_ticket_num_id']/strong[1]").text.strip() 
		if int(ze) == 0 and int(wz) == 0 :
			return 0
		else:
			return 1
		
	def submitOrderConfirm(self):
		print("[11]确认提交订单!")
		self.driver.find_element_by_id("qr_submit_id").click()
		
		
if __name__ == "__main__":
	main = Main()
	sub = 0
	#main.setUrl("https://kyfw.12306.cn/otn/leftTicket/init")
	main.getUrl()
	main.setUserPwd()
	main.waitingNewPage()
	main.selectYuding()
	main.selectOptions()
	main.setQueryLeftForm()
	main.queryTicket()
	main.checkTicketLoop()
	main.submitOrder() #订单提交
	
	sub = main.checkTicketNum()
	while sub == 0:
		print("[11]返回修改~")
		main.setUrl("https://kyfw.12306.cn/otn/leftTicket/init")
		main.getUrl()
		main.selectYuding()
		main.selectOptions()
		main.setQueryLeftForm()
		main.queryTicket()
		main.checkTicketLoop()
		main.submitOrder() #订单提交
		sub = main.checkTicketNum()
		if sub == 1:
			main.submitOrderConfirm() #确认订单提交
"""
	while True:
		time.sleep(5)
"""



  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值