**思路:**1.selenium 定位登录12306;
2.登录后跳转至index.html页,自动输入行程+日期,选择"高铁/动车",点击"查询"按钮;
3.跳转至购票页https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=%E5%8C%97%E4%BA%AC%E5%8C%97,VAP&ts=%E6%AD%A6%E6%B1%89,WHN&date=2021-01-30&flag=N,Y,Y,用selenium定位到
车次列表详细信息,循环遍历车次列表信息,判断余票(本次实例仅判断了二等座余票),如果有则点击"预定"按钮;
4.跳转至乘客确认页,选择购票乘客;
5.乘客选择完弹出div浮层,选择"1D"、“1F” 座位,点击"确认"按钮后完成购票。
注:因12306购票,每天只能取消3次,所以最后一个点击"确认"按钮慎用。

**代码:**共2个py文件(buy_tickets_by_selenium.py、captcha.py)
buy_tickets_by_selenium.py文件为主要核心流程
captcha.py文件为验证码实现流程+滑动解锁功能

**总结:**本例仅实现购票流程,但是真正意义上要抢票,用selenium需认真考虑。做的过程当中发现selenium存在稳定性问题,同样代码今天运行ok,明天运行也许就挂了,这个跟网络、页面加载等等都会有关。还有些页面元素加载超级慢,需强制等待几秒,这又影响了抢票效率。所以建议用requests写这样速度上应该会有提升,在requests写时需考虑浏览器cookie及购票页的url拼接,因为购票页url做了编码及额外参数拼接(例如你输入北京北参数,url参数拼接时是北京北,VAP,VAP是额外加的),这时就需要把12306源码下载下来分析具体js。目前来说用requests实现,还需要再研究研究! buy_tickets_by_selenium.py代码:

from selenium import webdriver
from captcha import Code
import time
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class buy_ticket:def __init__(self,driver,login_url,user_name,password,fromStation,toStationText,train_date,passenger_list):self.driver=driverself.login_url=login_urlself.user_name=user_nameself.password=passwordself.fromStation=fromStationself.toStationText=toStationTextself.train_date=train_dateself.passenger_list=passenger_listdef login(self):#登录self.driver.get(login_url)time.sleep(0.5)#登录首页有2个div进行切换,一个是二维码登录div,一个是账号登录div。默认显示二维码登录div,隐藏账号登录div.#如果不在此等0.5s可能导致二维码登录div正在加载状态时,点击了账号登录div,此时账号登录div与二维码登录div会同时重叠展现self.driver.find_element_by_xpath("//div[@class='login-box']/ul/li[2]/a").click()self.driver.find_element_by_id("J-userName").send_keys(self.user_name)self.driver.find_element_by_id("J-password").send_keys(self.password)c=Code(self.driver)c.main()time.sleep(3)self.driver.find_element_by_xpath("//div[@class='dzp-confirm']/div[2]/div[3]/a").click()self.driver.find_element_by_xpath("//li[@id='J-index']/a").click()self.ticket_index()def ticket_index(self):#跳转到购票首页#输入起始地WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'fromStationText')))self.driver.find_element_by_id("fromStationText").click()self.driver.find_element_by_id("fromStationText").send_keys(self.fromStation)self.driver.find_element_by_id("fromStationText").send_keys(Keys.ENTER)#输入目的地WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'toStationText')))self.driver.find_element_by_id("toStationText").click()self.driver.find_element_by_id("toStationText").send_keys(self.toStationText)self.driver.find_element_by_id("toStationText").send_keys(Keys.ENTER)#输入日期js="document.getElementById('train_date').removeAttribute('readonly')"#将日期的只读属性去掉便于下面输入日期self.driver.execute_script(js)WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'train_date')))self.driver.find_element_by_id("train_date").clear()#清空默认日期值self.driver.find_element_by_id("train_date").send_keys(self.train_date)time.sleep(0.5)js1="document.querySelector('body > div.cal-wrap').style.display='none'"self.driver.execute_script(js1)#点击高铁/动车WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.ID,'isHighDan')))self.driver.find_element_by_id('isHighDan').click()#点击"查询"按钮WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'search_one')))self.driver.find_element_by_id("search_one").click()self.check_tickets()def check_tickets(self):#跳转至购票页检查二等座是否有票,有票开始购买time.sleep(5)all_handles=self.driver.window_handlesself.driver.switch_to.window(all_handles[-1])trs=self.driver.find_elements_by_xpath("//div[@id='t-list']/table/tbody[1]/tr")for tr in trs:left_ticket = tr.find_element_by_xpath('//td[4]').textif left_ticket == '有' or left_ticket.isdigit:orderBtn = tr.find_element_by_class_name('btn72')orderBtn.click()time.sleep(2)self.confirm_Passenger()breakdef confirm_Passenger(self):#乘客确认页,选择要购票乘客all_handles = self.driver.window_handlesself.driver.switch_to.window(all_handles[-1])li_list=self.driver.find_elements_by_xpath("//ul[@id='normal_passenger_id']/li")for li in li_list:passenger=li.find_element_by_xpath('./label').textfor pger in self.passenger_list:if passenger==pger:li.find_element_by_xpath("./label").click()self.driver.find_element_by_id("submitOrder_id").click()time.sleep(1)self.driver.find_element_by_xpath("//div[@id='erdeng1']/ul[2]/li[1]/a").click()#二等座D座位self.driver.find_element_by_xpath("//div[@id='erdeng1']/ul[2]/li[2]/a").click()#二等座F座位# 点击"确认"按钮WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, 'qr_submit_id')))self.driver.find_element_by_id("qr_submit_id").click()if __name__=="__main__":options = webdriver.ChromeOptions()  # 设置浏览器属性options.add_argument('user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 Edg/86.0.622.69"')  # 添加UA属性options.add_experimental_option("excludeSwitches", ["enable-automation"])  # 设置开发者模式启动,该模式下webdriver属性为正常值options.add_experimental_option('useAutomationExtension', False)driver = webdriver.Chrome(options=options)driver.maximize_window()driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""})  # 增加反爬机制,防止被12306识别出来用的是seleniumlogin_url="https://kyfw.12306.cn/otn/resources/login.html"bt=buy_ticket(driver,login_url,"username","password","北京","武汉","2021-01-30",["passenger1","passenger2"])bt.login()

captcha.py代码:

import time
import base64
import requests
import numpy as np
from selenium.webdriver import ActionChains
from selenium.webdriver.support import wait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ECclass Code():def __init__(self, browser):self.browser = browserself.verify_url = 'http://littlebigluo.qicp.net:47720/'#验证码识别网址,返回识别结果#获取验证码图片def get_captcha(self):element = self.browser.find_element_by_class_name('imgCode')#time.sleep(0.5)img = base64.b64decode(element.get_attribute('src')[len('data:image/jpg;base64,'):])with open('captcha.png', 'wb') as f:f.write(img)#验证码解析def parse_img(self):pic_name = 'captcha.png'# 打开保存到本地的验证码图片files={'pic_xxfile':(pic_name,open(pic_name,'rb'),'image/png')}response = requests.post(self.verify_url, files=files)try:num = response.text.split('<B>')[1].split('<')[0]except IndexError:  #验证码没识别出来的情况print('验证码未能识别!重新识别验证码...')returntry:if int(num):print('验证码识别成功!图片位置:%s' % num)return [int(num)]except ValueError:try:num = list(map(int,num.split()))print('验证码识别成功!图片位置:%s' % num)return numexcept ValueError:print('验证码未能识别')return#识别结果num都以列表形式返回,方便后续验证码的点击#还有可能验证码没能识别出来#实现验证码自动点击def move(self):num = self.parse_img()if num:try:element = self.browser.find_element_by_class_name('loginImg')for i in num:if i <= 4:ActionChains(self.browser).move_to_element_with_offset(element,40+72*(i-1),73).click().perform()#鼠标移动到图片位置点击,40+72*(i-1)代表x坐标,73代表Y坐标#ActionChains 处理鼠标相关的操作,行为事件存储在ActionChains对象队列,当使用perform()时,对象事件按顺序依次执行else :i -= 4ActionChains(self.browser).move_to_element_with_offset(element,40+72*(i-1),145).click().perform()self.browser.find_element_by_class_name('login-btn').click()self.slider()except Exception as e:print(e)#print('元素不可选!')else:self.browser.find_element_by_class_name('lgcode-refresh').click()  #刷新验证码time.sleep(1.5)self.main()def ease_out_quart(self, x):return 1 - pow(1 - x, 4)#生成滑动轨迹def get_tracks(self, distance, seconds, ease_func):tracks = [0]offsets = [0]for t in np.arange(0.0, seconds, 0.1):ease = ease_funcoffset = round(ease(t / seconds) * distance)tracks.append(offset - offsets[-1])offsets.append(offset)return tracks#处理滑动验证码def slider(self):print('开始处理滑动验证码...')#track = self.get_tracks(305, 1, self.ease_out_quart)track = [30, 50, 90, 140]  #滑动轨迹可随意,只要距离大于300try:slider = wait.WebDriverWait(self.browser, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'nc_iconfont.btn_slide')))ActionChains(self.browser).click_and_hold(slider).perform()for i in track:ActionChains(self.browser).move_by_offset(xoffset=i, yoffset=0).perform()except:print('验证码识别错误!等待验证码刷新,重新识别验证码...')time.sleep(2.1)  #验证码刷新需要2秒self.main()def main(self):self.get_captcha()self.move()

selenium实现12306全自动购票相关推荐

  1. java + Selenium实现12306自动购票

    为什么搞这个东西?[java + Selenium实现12306自动购票, 余票监测] 1.主要是12306是爬虫界的一个分水岭,所以我一直想玩12306[本次的实现并非真正意义上的破解12306实现 ...

  2. 28. 实战:基于selenium实现12306自动购票

    目录 前言 目的 思路 代码实现 1. 进入登录界面,输入账号密码 2. 点击登录按钮,完成滑块验证 3. 在个人中心点击购票,跳转 4. 输入出发地.目的地,从控制台输入得到 5. 文本框输入出发日 ...

  3. python + selenium实现12306全自动买票

    整个程序分了三个模块:购票模块(主体).验证码处理模块.余票查询模块 使用方法:三个模块分别保存为三个python文件,名字分别为:book_ticket,captcha,check_ticket. ...

  4. selenium实现12306火车购票网站滑块自动验证登录

    解决滑块验证登录问题和网站禁止selenium操作无法通过验证问题,问题过程如下,亲测有效: 当输入账号密码,点击登录后出现如下滑动解锁框: 此时,完成滑块自动滑动至右边解锁,写个拖动滑块的函数 mo ...

  5. Python + selenium + requests实现12306全自动买票

    Python + selenium + requests实现12306全自动买票 2020.05.03更新: 下面是新的测试结果: 2021.03.28更新:谷歌浏览器升级导致之前的隐藏方法失效,更新 ...

  6. 爬虫日常-selenium登录12306,绕过验证

    文章目录 前言 代码设计 前言 hello兄弟们,这里是无聊的网友.愉快的周末过去了,欢迎回到学习频道.书接上文,我们说到了再用selenium登录12306时遇到了滑块验证的问题.当前的网站几乎每家 ...

  7. 爬虫(12,13)selenium练习 12306案例

    文章目录 第十二.十三章 selenium练习12306案例 0. 前言 1. 登录的实现 2. 车次及余票查询 2.1 车站信息读取 2.2 车站信息添加方法 2.3 车站信息导入输入框 2.4 出 ...

  8. 12306订票候补是个坑_重磅!12306候补购票正式上线,实测你可能连排队的机会都没有!...

    原标题:重磅!12306候补购票正式上线,实测你可能连排队的机会都没有! 今天,备受期待的12306候补购票功能正式上线了. 当你以为今年不需要抢票的时候,候补购票功能立马给你打脸-- 首先这次候补购 ...

  9. 去哪儿12306候补购票怎么用?

    今天,一朋友用去哪儿购票,问我怎么用去哪儿旅行APP里面的12306候补购票?我试着找了一下,确实有点不好找,不过还是被我找到了. 如图: 去哪儿旅行APP首页界面 首先在首页界面里点击火车票,这界面 ...

  10. selenium处理12306登录

    selenium处理12306登录 使用edge浏览器驱动 from selenium.webdriver import Edge from selenium.webdriver.common.act ...

最新文章

  1. 计算机组装安全常识,计算机组装与维护宝典
  2. HDLBits 系列(4)如何设计一定不会产生Latch的组合逻辑?
  3. oracle 存储过程和函数例子
  4. php 修改文件访问时间,php获取文件的创建、修改时间及访问时间
  5. odoo 中多币种处理(外币处理)
  6. LeetCode 1332. 删除回文子序列
  7. Spring Data JPA 从入门到精通~方法的查询策略的属性表达式
  8. 老上网本不能上无线网
  9. 服务器硬盘 二手,分析:二手服务器配件 哪个最不该买?
  10. HTML 中获取现在时间,实时时间获取
  11. Java项目《谷粒商城》高级篇 个人错误总结
  12. 中国石油大学(北京)-《 修井工程》第二阶段在线作业
  13. 计算机语言写信祝福语,写信祝福语
  14. MySQL 第十三章 约束(★★★★★)
  15. 编码的奥秘:两种典型的微处理器
  16. websocket即时通讯
  17. 简述新图像文件格式——SVG
  18. Mac下的winscp替代者 FileZilla
  19. spring data mongodb 大数据量查询性能差的原因(20s 优化到2s)
  20. dll转换成.a /dll转换lib

热门文章

  1. 蓝屏总结(二)——系统蓝屏及转储方法
  2. 罗振宇2021跨年演讲3:谁能跳出数字化系统困境?
  3. python got an unexpected keyword argument
  4. 谁在维护linux内核,故意向Linux内核中提及漏洞? Linux 内核维护者封杀明尼苏达大学...
  5. 【git commit --amend 修改提交记录】
  6. python excel表格去重_EXCEL数据如何去重? Python:这事我比你熟
  7. 课堂作业之首尾相连子数组值
  8. 服务器冗余电源维修图纸,冗余热备份电源的电路图设计
  9. c#语言求两个数最大公约数,C#趣味程序---求两个数的最大公约数和最小公倍数...
  10. SNAP 4. 使用snap进行地物光谱分析