Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能


Posted in Python onNovember 23, 2018

测试结果: 

Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能

整个买票流程可以再快一点,不过为了稳定起见,有些地方等待了一些时间

完整程序,拿去可用

整个程序分了三个模块:购票模块(主体)、验证码识别模块、余票查询模块

购票模块:

from selenium import webdriver
from selenium.webdriver.common.by import By 
from selenium.webdriver.support import expected_conditions as EC 
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, ElementNotVisibleException
import time
import requests
from urllib.parse import urlencode
from pyquery import PyQuery as pq
from check_ticket import Check
from verify import Code
import json
class Buy_Ticket():
  def __init__(self, start_station, end_station, date, username, password, purpose):
    self.num = 1
    self.start = start_station
    self.end = end_station
    self.date = date
    self.username = username
    self.password = password
    self.purpose = purpose
    self.login_url = 'https://kyfw.12306.cn/otn/login/init'
    self.ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init'
  def login(self):
    browser.get(self.login_url)
    try:
      input_name = browser.find_element_by_id('username')
      input_pd = browser.find_element_by_id('password')
      button = browser.find_element_by_id('loginSub')
      time.sleep(1)
      input_name.send_keys(self.username)
      input_pd.send_keys(self.password)
      c = Code(browser)    #调用验证码识别模块
      c.main()
      button.click()
      time.sleep(2)
      #等待页面跳转,如果验证码识别错误,就执行下面的while语句
      while browser.current_url == self.login_url + '#':
        c = Code(browser)
        c.main()
        button.click()
        time.sleep(2)
      #self.get_passenger()
      self.check()
    except NoSuchElementException:
      self.login()
  def check(self):
    #调用余票查询模块
    check = Check(self.date, self.start, self.end, self.purpose)
    start_end = check.look_up_station()
    self.num = check.get_info()
    #cookie的添加,json.dumps把以汉字形式呈现的起始、终点站转化成unicode编码,可在审查元素里查看cookie
    browser.add_cookie({'name':'_jc_save_fromStation', 'value':json.dumps(self.start).strip('"').replace('\\', '%') + '%2C' + start_end[0]})
    browser.add_cookie({'name':'_jc_save_toStation', 'value':json.dumps(self.end).strip('"').replace('\\', '%') + '%2C' + start_end[1]})
    browser.add_cookie({'name':'_jc_save_fromDate', 'value':self.date})
    browser.get(self.ticket_url)
    if self.purpose == '学生':
      btn = browser.find_element_by_id('sf2')
      time.sleep(1)
      btn.click()
    button = browser.find_element_by_id('query_ticket')
    time.sleep(1)
    button.click()
  def book_ticket(self):
    print('开始预订车票...')
    #先查找出所有车次对应的预订按钮,再根据余票查询模块返回的车次序号,点击相应的预订按钮
    button = browser.find_elements_by_class_name('btn72')
    button[self.num-1].click()
    time.sleep(3)
    button2 = browser.find_element_by_id('normalPassenger_0') #按实际情况,可自行修改,这里就选择的第一个常用联系人,
                                  #第二个是normalPassenger_1,依此类推
    button2.click()
    button3 = browser.find_element_by_id('submitOrder_id')
    time.sleep(1)
    button3.click()
    time.sleep(3) #等待页面加载完毕,不然后面可能会报错,等待时间自行决定
    try:
      button4 = browser.find_element_by_id('qr_submit_id')
      button4.click()
    except ElementNotVisibleException:
      button4 = browser.find_element_by_id('qr_submit_id')
      button4.click()
    print('车票预定成功!请在30分钟内完成付款!')
  def main(self):
    self.login()
    self.book_ticket()
if __name__ == '__main__':
  begin = time.time()
  browser = webdriver.Chrome()
  b = Buy_Ticket('上海', '重庆', '2018-09-18', '账号', '密码', 'ADULT') #账号、密码自行修改
  b.main()
  end = time.time()
  print('总耗时:%d秒' % int(end-begin))
  #browser.close()

验证码识别模块:

import requests
from PIL import Image
from selenium.webdriver import ActionChains
import time
from io import BytesIO
class Code():
  def __init__(self, browser):
    self.browser = browser
    self.verify_url = 'http://littlebigluo.qicp.net:47720/'   #验证码识别网址,返回识别结果
    #确定验证码的位置
  def get_position(self):
    time.sleep(3)
    element = self.browser.find_element_by_class_name('touclick-img-par')
    time.sleep(2)
    location = element.location
    size = element.size
    position= (location['x'], location['y'], location['x'] + size['width'], location['y'] + size['height'])
    return position
    #截取整个网页页面
  def get_screenshot(self):
    screenshot = self.browser.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))
    return screenshot
    #从截取的网页,裁剪出验证码图片,并保存到本地
  def get_touclick_img(self, name = 'captcha.png'):
    position = self.get_position()
    print('验证码的位置:', position)
    screenshot = self.get_screenshot()
    captcha = screenshot.crop(position)
    captcha.save('captcha.png')
    #验证码解析
  def parse_img(self):
    files = {'file': open('captcha.png', 'rb')}       #打开保存到本地的验证码图片
    response = requests.post(self.verify_url, files=files)
    num = response.text.split('<B>')[1].split('<')[0]
    print('验证码识别成功!图片位置:%s' % num)
    try:
      if int(num):
        return [int(num)]
    except ValueError:
      num = list(map(int,num.split()))
      return num
    #识别结果num都以列表形式返回,方便后续验证码的点击
    #实现验证码自动点击
  def move(self):
    num = self.parse_img()
    try:
      element = self.browser.find_element_by_class_name('touclick-img-par')
      for i in num:
        if i <= 4:
          ActionChains(self.browser).move_to_element_with_offset(element,40+72*(i-1),73).click().perform()
        else :
          i -= 4
          ActionChains(self.browser).move_to_element_with_offset(element,40+72*(i-1),145).click().perform()
    except:
      print('元素不可选!')
  def main(self):
    self.get_touclick_img()
    self.move()

余票查询模块:

 

import requests
from urllib.parse import urlencode
class Check():
  def __init__(self, date, start, end, purpose):
    self.base_url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?'
    self.url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9018'
    self.date = date
    self.start_station = start
    self.end_station = end
    if purpose == '学生':
      self.purpose = '0X00'
    else:
      self.purpose = purpose
    #查找出车站的英文简称,用于构造cookie、完整的余票查询链接
  def look_up_station(self):
    response1 = requests.get(self.url)
    a = response1.text.split('@')
    a.pop(0)
    for each in a:
      i = each.split('|')
      if self.start_station == i[1]:
        self.start_station = i[2]
      elif self.end_station == i[1]:
        self.end_station = i[2]
    return [self.start_station, self.end_station]
  def get_info(self):
    start_end = self.look_up_station()
    #构造请求参数
    data = {
    'leftTicketDTO.train_date':self.date,
    'leftTicketDTO.from_station':start_end[0],
    'leftTicketDTO.to_station':start_end[1],
    'purpose_codes':self.purpose
    }
    url = self.base_url + urlencode(data)
    response = requests.get(url)
    json = response.json()
    maps = json['data']['map']
    count = 0    #用于对车次编号       
    for each in json['data']['result']:
      count += 1
      s = each.split('|')[3:]
      info = {
      'train':s[0],
      'start_end':maps[s[3]] + '-' + maps[s[4]],
      'time':s[5] + '-' + s[6],
      '历时':s[7],
      '一等座':s[-5],
      '二等座':s[-6]
      }
      try:
        #余票的结果有3种:有、一个具体的数字(如:18、6等)、无,判断如果余票是有或者一个具体的数字就直接输出对应的车次信息,然后返回
        if info['二等座'] == '有' or int(info['二等座']):   
          print('[%d]' % count, info)
          return count
      except ValueError:
        continue

总结

以上所述是小编给大家介绍的Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
Django集成百度富文本编辑器uEditor攻略
Jul 04 Python
python并发编程之线程实例解析
Dec 27 Python
selenium+python 对输入框的输入处理方法
Oct 11 Python
在python中使用requests 模拟浏览器发送请求数据的方法
Dec 26 Python
Python简单基础小程序的实例代码
Apr 28 Python
Python 变量的创建过程详解
Sep 02 Python
python实现图片上添加图片
Nov 26 Python
Python面向对象程序设计之私有变量,私有方法原理与用法分析
Mar 23 Python
Jupyter notebook设置背景主题,字体大小及自动补全代码的操作
Apr 13 Python
解决Jupyter NoteBook输出的图表太小看不清问题
Apr 16 Python
python 如何实现遗传算法
Sep 22 Python
Python djanjo之csrf防跨站攻击实验过程
May 14 Python
python+selenium实现自动抢票功能实例代码
Nov 23 #Python
3分钟学会一个Python小技巧
Nov 23 #Python
值得收藏,Python 开发中的高级技巧
Nov 23 #Python
python 常见字符串与函数的用法详解
Nov 23 #Python
django+mysql的使用示例
Nov 23 #Python
Linux下Python安装完成后使用pip命令的详细教程
Nov 22 #Python
Windows下Python3.6安装第三方模块的方法
Nov 22 #Python
You might like
关于Zend Studio 配色方案插件的介绍
2013/06/24 PHP
一个严格的PHP Session会话超时时间设置方法
2014/06/10 PHP
PHP面向对象程序设计方法实例详解
2016/12/24 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
利用javascript中的call实现继承
2007/01/22 Javascript
filters.revealTrans.Transition使用方法小结
2010/08/19 Javascript
javascript中创建对象的三种常用方法
2010/12/30 Javascript
jquery 获取dom固定元素 添加样式的简单实例
2014/02/04 Javascript
js校验表单后提交表单的三种方法总结
2014/02/28 Javascript
JavaScript调试技巧之console.log()详解
2014/03/19 Javascript
node.js中的console.assert方法使用说明
2014/12/10 Javascript
AngularJS中的Directive实现延迟加载
2016/01/25 Javascript
JavaScript Array对象详解
2016/03/01 Javascript
JS中Select下拉列表类(支持输入模糊查询)功能
2017/01/17 Javascript
深入学习 JavaScript中的函数调用
2017/03/23 Javascript
Nodejs回调加超时限制两种实现方法
2017/06/09 NodeJs
Mobile Web开发基础之四--处理手机设备的横竖屏问题
2017/08/11 Javascript
JS实现盒子拖拽效果
2020/02/06 Javascript
Bootstrap FileInput实现图片上传功能
2021/01/28 Javascript
python网络编程学习笔记(六):Web客户端访问
2014/06/09 Python
浅谈python import引入不同路径下的模块
2017/07/11 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
python实现自动网页截图并裁剪图片
2018/07/30 Python
解决Pandas的DataFrame输出截断和省略的问题
2019/02/08 Python
Python爬虫:Request Payload和Form Data的简单区别说明
2020/04/30 Python
新手常见Python错误及异常解决处理方案
2020/06/18 Python
如何表示python中的相对路径
2020/07/08 Python
幼师岗位求职简历的自荐信格式
2013/09/21 职场文书
水电工岗位职责
2014/02/12 职场文书
职业规划实施方案
2014/06/10 职场文书
行政执法队伍作风整顿剖析材料
2014/10/11 职场文书
2015年建筑工作总结报告
2015/05/04 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
教你怎么用Python操作MySql数据库
2021/05/31 Python
Redis集群的关闭与重启操作
2021/07/07 Redis
MySQL 外连接语法之 OUTER JOIN
2022/04/09 MySQL