2025年3月22日 星期六 甲辰(龙)年 月廿一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

python3+selenium实现12306抢票脚本

时间:03-27来源:作者:点击数:46

一、 环境配置

  • python3
  • selenium模块,并配置使用环境
  • google浏览器或者其他浏览器

安装selenium模块

  • pip install selenium

淘宝镜像下chrome版本对应的chromedriver

windows环境

将chromedriver与selenium、requests等放在一起即可

或者将chromedriver放在某个路径,并配置环境变量

mac、linux环境

  • sudo mv /路径/chromedriver /usr/bin

如果配置好环境后运行依然报错,可以尝试以下代码

  • options = webdriver.ChromeOptions()
  • options.add_argument('--disable-extensions')
  • options.add_argument('--disable-gpu')
  • options.add_argument('--no-sandbox')
  • options.add_argument("window-size=1024,768")
  • browser = webdriver.Chrome(options=options)

二、 实现思路及代码

1. 查询票数

https://kyfw.12306.cn/otn/leftTicket/init直接在这个界面查询票数

  • 输入地点时间
    我看了网上的一些12306抢票代码,基本上都是用添加cookies的方式,来实现输入出发地、目的地、出发日,所有我也用这样方法。
    出发地和目的地的编码可以在浏览器里看到,我偷懒了,先查到直接复制。
    %u6F62%u5DDD%2CKCN 潢川
    %u90D1%u5DDE%2CZZF 郑州
  • 模拟点击“查询”
    直接找个这个标签,然后通过click()函数实现
  • 不断刷新
    把模拟点击代码放在循环里,通过time.sleep(second)可以设置程序暂停时间。必须要暂停,刷新太快可能被封ip或封账号。
    下面是主要代码
  • def refresh_order():
  • try:
  • browser.get('https://kyfw.12306.cn/otn/leftTicket/init')
  • # 通过添加cookie的方式实现起终站以及购票时间的输入
  • browser.add_cookie({'name': '_jc_save_fromStation', 'value': '%u6F62%u5DDD%2CKCN'})
  • browser.add_cookie({'name': '_jc_save_toStation', 'value': '%u90D1%u5DDE%2CZZF'})
  • browser.add_cookie({'name': '_jc_save_fromDate', 'value': buyTicketDay})
  • js = 'window.open("https://kyfw.12306.cn/otn/resources/login.html");'
  • browser.execute_script(js)
  • handles = browser.window_handles
  • # 暂停120秒去登录
  • time.sleep(120)
  • # 然后切换到抢票界面
  • browser.switch_to.window(handles[0])
  • time.sleep(3)
  • browser.refresh()
  • click_query = browser.find_element_by_css_selector('.content.content-lg .sear-box.quick-sear-box.sear-box-lg '
  • '.quick-s .btn-area a')
  • time.sleep(3)
  • # index用于统计抢票次数
  • index = 0
  • while True:
  • click_query.click()
  • time.sleep(10)
  • try:
  • global queryWZ, queryYZ
  • queryYZ = browser.find_element_by_css_selector('.content.content-lg .t-list #queryLeftTable '
  • '#ticket_580000K7440O #YZ_580000K7440O').text
  • queryWZ = browser.find_element_by_css_selector('.content.content-lg .t-list #queryLeftTable '
  • '#ticket_580000K7440O #WZ_580000K7440O').text
  • finally:
  • pass
  • if (queryYZ != '无' and queryYZ != '--') or (queryWZ != '无' and queryWZ != '--'):
  • browser.find_element_by_css_selector('.content.content-lg .t-list #queryLeftTable '
  • '#ticket_580000K7440O td').click()
  • noteStr = "有硬座或者无座,及时查看"
  • send_mail(noteStr)
  • send_message(noteStr)
  • try:
  • submit_order()
  • except Exception as e:
  • print(e)
  • print("提交订单失败")
  • send_message("有余票但是提交订单失败")
  • finally:
  • pass
  • index = index+1
  • print("第"+str(index)+"次抢票结果: 成功")
  • break
  • index = index+1
  • print("第"+str(index)+"次抢票结果: 失败")
  • finally:
  • print("Something is wrong")
  • send_message("程序运行出错")

2. 登录

我这里用selenium模拟浏览器,可以直接扫码登录,或者用密码账号登录。登录时间设置为2分钟,2分钟一过会自动切换窗口开始抢票。

关于自动登录

网上很多人打码平台打码,还有人自己训练模型实现自动识别。我没有时间,所以选择一种比较的方式。(主要是穷学生一个,深度学习又不会)

3. 提交订单

有余票时就开始买了,主要代码如下

  • def submit_order(): # 完成提交订单一系列功能
  • time.sleep(5)
  • browser.find_element_by_xpath('//div[@class="content"]//div[@class="per-sel"]//ul[@id="normal_passenger_id"]'
  • '/li/label[text()=person]/../input').click()
  • # 购买学生票,如果选择否,将.dialog_xsertcj_ok改为dialog_xsertcj_cancel
  • # 针对乘客信息里包含"(学生)"的乘客,其他乘客则直接注释掉下面一行代码
  • time.sleep(1)
  • browser.find_element_by_css_selector('#body_id #dialog_xsertcj .up-box-bd .lay-btn .dialog_xsertcj_ok').click()
  • # 提交订单
  • time.sleep(2)
  • browser.find_element_by_xpath('//div[@class="content"]//div[@class="lay-btn"]/a[@id="submitOrder_id"]').click()
  • # 核对信息
  • time.sleep(2)
  • browser.find_element_by_css_selector('.dhtmlx_window_active .dhtmlx_wins_body_inner .dhtmlx_wins_no_header'
  • '#checkticketinfo_id .up-box-bd.ticket-check #confirmDiv #qr_submit_id'
  • ).click()

这个函数我并没有测试过,累了,以后再测试,有错会再修改。

4. 邮件提醒

有余票或者购票成功通过发邮件提醒一下

  • def send_mail(notestr): # 邮箱通知
  • msg = MIMEText(notestr, 'plain', 'utf-8')
  • subject = '抢票结果通知'
  • msg['Subject'] = Header(subject, 'utf-8')
  • msg['From'] = 'Tomm<发件人邮箱>'
  • msg['To'] = "收件人邮箱"
  • # 输入Email地址和口令:
  • from_addr = '发件人邮箱'
  • password = '***' # 不是登录密码,而是客户端授权码
  • # 输入SMTP服务器地址:
  • smtp_server = 'smtp.163.com'
  • # 输入收件人地址:
  • to_addr = '收件人邮箱'
  • server = smtplib.SMTP() # SMTP协议默认端口是25
  • server.connect(smtp_server, 25)
  • server.set_debuglevel(1)
  • server.login(from_addr, password)
  • server.sendmail(from_addr, to_addr, msg.as_string())
  • server.quit()

5. 短信提醒

twilio注册一个账号,网上有教程,不再累述。免费的

  • def send_message(noteStr): # 短信通知
  • account_sid = 'ACc8dddd69ae80c69f816f5cb24c6a0cca'
  • auth_token = '***'
  • client = Client(account_sid, auth_token)
  • message = client.messages.create(
  • from_='+发短信人手机号',
  • body=noteStr,#短信内容
  • to='+86收短信手机号'
  • )

三、总结与分析

一些问题

  • 没有实现自动登录
  • 没有实现出发地和目的地编码的自动获取
  • 提交订单没有测试
  • 每次查询票数时,用显示等待更好,这样减少查找不到报错的几率
  • 一次只能抢一天的票
  • 我设置的查询硬座和无座,其他的没有考虑内
  • 还有很多功能没有实现
    总的来说,这个脚本够我自己用,量身定做的。因为我现在没有那么多时间,以后会不断完善代码。

一些想法

如果只想查询有没有余票,完全不用登陆,直接请求类似于这种的链接https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2019-02-21&leftTicketDTO.from_station=KCN&leftTicketDTO.to_station=ZZF&purpose_codes=ADULT,会获得所有车票信息,用“|”分割。然后通过短信提醒自己,再用手机或者电脑购票。

完整源码下载

放在github上的源码

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门