selenium实现12306全自动购票

news/2024/11/19 6:20:41/

**思路:**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()

http://www.ppmy.cn/news/803720.html

相关文章

从12306.cn谈大网站架构与性能优化

PS&#xff1a;关于12306.cn网站&#xff0c;前些时间&#xff0c;骂的人很多&#xff0c;但是这网站的压力和架构不是一般非专业人生想得这么简单。下文是资深架构师陈皓写的关于12306.cn购票网站的架构和性能系列分析&#xff0c;个人认为很有参考价值&#xff0c;转载如下&a…

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

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

由12306.cn谈谈网站性能技术

12306.cn网站挂了&#xff0c;被全国人民骂了。我这两天也在思考这个事&#xff0c;我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促&#xff0c;而且完全基于本人有限的经验和了解&#xff0c;所以&#xff0c;如果有什么问题还请大家一起讨论和指正。&#xff0…

由12306谈谈网站性能技术

12306.cn网站挂了&#xff0c;被全国人民骂了。我这两天也在思考这个事&#xff0c;我想以这个事来粗略地和大 家讨论一下网站性能的问题。因为仓促&#xff0c;而且完全基于本人有限的经验和了解&#xff0c;所以&#xff0c;如果有什么问题还请大家一起讨论和指正。&#xff…

说说12306,呆在深圳就只能一直抢票

修改一下文章&#xff0c;标题也修改一下&#xff0c;之前叫《决定软件不是技术》&#xff0c;其实主要是写决定软件并不是技术本身&#xff0c;而是你对软件本身的定义或者说是对软件解决问题的定义&#xff0c;而技术只是实现的一种手段&#xff0c;感觉自己表达能力太烂&…

学习笔记——12306 手动登录解析(1)

#-*-coding:utf-8 -*- import urllib2 import urllib # 验证码登录同步 import cookielib import ssl #证书验证 ssl._create_default_https_context ssl._create_unverified_context # 请求验证码图片 ccookielib.LWPCookieJar()#生成一个储存cookie的对象 cookieurllib2.HTT…

python3 requests 实现12306购票登录模块

12306登录模块分析 第一次写文章&#xff0c;记录一下学习的内容。今天先记录登录模块的分析和实现。 在博客上看见一些大佬用的是splinter webdriver写的12306购票过程。 由于我之前学习了一阵子requests库&#xff0c;所以以下使用python3requests实现12306购票。&#xf…

java实现12306查票_GitHub - HendSame/J12306: 12306抢票程序JAVA版

J12306抢票助手 12306抢票程序JAVA版,自动登录-验证-查票-购票/自动候补。只需简单的配置即可运行进行快捷抢票。 使用说明 引入jar依赖 手动添加项目lib文件夹中的依赖包 配置文件config.yml # 请修改相关配置 # 12306账号密码配置(暂时没用到) j12306: user: 182xxxx passwo…