想去看演唱却总是抢不到票?教你用Python制作一个自动抢票脚本

Posted 魔王不会哭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了想去看演唱却总是抢不到票?教你用Python制作一个自动抢票脚本相关的知识,希望对你有一定的参考价值。

前言

嗨喽!大家好,这里是魔王!!

大麦网,是中国综合类现场娱乐票务营销平台,业务覆盖演唱会、 话剧、音乐剧、体育赛事等领域。

但是因为票数有限,还有黄牛们不能丢了饭碗,所以导致了,很多人都抢不到票

那么,今天带大家用Python来制作一个自动抢票的脚本小程序

此次知识点:

  • 面向对象编程
  • selenium 操作浏览器
  • pickle 保存和读取Cookie实现免登陆
  • time 做延时操作
  • os 创建文件,判断文件是否存在

开发环境:

  • 版 本:anaconda5.2.0(python3.6.5)
  • 编辑器:pycharm

python安装包 安装教程视频

pycharm 社区版 专业版 及 激活码

视频教程私我领取

第三方库:

  • selenium >>> pip install selenium==3.4.1

步骤

  1. 实现免登陆
    第一次登陆的时候 会帮助我记录我们的登陆信息
    set_cookie 登陆成功之后 cookie会发生变化
    后续抢票: 直接使用我们记录好的登陆信息
    get_cookie

  2. 抢票并且下单

首先导入本次所需的模块

from selenium import webdriver  # 操作谷歌浏览器 需要额外安装的 并且现在安装这个模块得指定版本 3.4
from time import sleep
import pickle  # 保存和读取cookie实现免登录的工具
import os   # 操作文件的模块

第一步,实现免登录

确定目标,设置全局变量

# 大麦网主页
damai_url = "https://www.damai.cn/"
# 登录页
login_url = "https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F"
# 抢票目标页
target_url = 'https://detail.damai.cn/item.htm?spm=a2oeg.search_category.0.0.77f24d15RWgT4o&id=654534889506&clicktitle=%E5%A4%A7%E4%BC%97%E7

初始化加载

class Concert:
    def __init__(self):
        self.status = 0         # 状态,表示如今进行到何种程度
        self.login_method = 1   # 0:模拟登录,1:Cookie登录自行选择登录方式
        self.driver = webdriver.Chrome(executable_path='chromedriver.exe')        # 默认Chrome浏览器

登录调用设置cookie

def set_cookie(self):
    self.driver.get(damai_url)
    print("###请点击登录###")
    while self.driver.title.find('大麦网-全球演出赛事官方购票平台') != -1:
        sleep(1)
    print('###请扫码登录###')

    while self.driver.title != '大麦网-全球演出赛事官方购票平台-100%正品、先付先抢、在线选座!':
       sleep(1)
    print("###扫码成功###")
    pickle.dump(self.driver.get_cookies(), open("cookies.pkl", "wb"))
    print("###Cookie保存成功###")
    self.driver.get(target_url)

获取cookie

def get_cookie(self):
    try:
        cookies = pickle.load(open("cookies.pkl", "rb"))  # 载入cookie
        for cookie in cookies:
            cookie_dict = 
                'domain':'.damai.cn',  # 必须有,不然就是假登录
                'name': cookie.get('name'),
                'value': cookie.get('value')
            
            self.driver.add_cookie(cookie_dict)
        print('###载入Cookie###')
    except Exception as e:
        print(e)

开始登录

    def login(self):
        if self.login_method==0:
            self.driver.get(login_url)                                
            # 载入登录界面
            print('###开始登录###')

        elif self.login_method==1:
            if not os.path.exists('cookies.pkl'):                     
            # 如果不存在cookie.pkl,就获取一下
                self.set_cookie()
            else:
                self.driver.get(target_url)
                self.get_cookie()

打开浏览器

def enter_concert(self):
    """打开浏览器"""
    print('###打开浏览器,进入大麦网###')
    # self.driver.maximize_window()           # 最大化窗口
    # 调用登陆
    self.login()                            # 先登录再说
    self.driver.refresh()                   # 刷新页面
    self.status = 2                         # 登录成功标识
    print("###登录成功###")
    # 后续德云社可以讲
    if self.isElementExist('/html/body/div[2]/div[2]/div/div/div[3]/div[2]'):
        self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div/div/div[3]/div[2]').click()

第二步,抢票并下单

判断元素是否存在

def isElementExist(self, element):
    flag = True
    browser = self.driver
    try:
        browser.find_element_by_xpath(element)
        return flag

    except:
        flag = False
        return flag

选票

def choose_ticket(self):
    if self.status == 2:                  #登录成功入口
        print("="*30)
        print("###开始进行日期及票价选择###")
        while self.driver.title.find('确认订单') == -1:           # 如果跳转到了订单结算界面就算这步成功了,否则继续执行此步
            try:
                buybutton = self.driver.find_element_by_class_name('buybtn').text
                if buybutton == "提交缺货登记":
                    # 改变现有状态
                    self.status=2
                    self.driver.get(target_url)
                    print('###抢票未开始,刷新等待开始###')
                    continue
                elif buybutton == "立即预定":
                    self.driver.find_element_by_class_name('buybtn').click()
                    # 改变现有状态
                    self.status = 3
                elif buybutton == "立即购买":
                    self.driver.find_element_by_class_name('buybtn').click()
                    # 改变现有状态
                    self.status = 4
                # 选座购买暂时无法完成自动化
                elif buybutton == "选座购买":
                    self.driver.find_element_by_class_name('buybtn').click()
                    self.status = 5
            except:
                print('###未跳转到订单结算界面###')
            title = self.driver.title
            if title == '选座购买':
                # 实现选座位购买的逻辑
                self.choice_seats()
            elif title == '确认订单':
                while True:
                    # 如果标题为确认订单
                    print('waiting ......')
                    if self.isElementExist('//*[@id="container"]/div/div[9]/button'):
                        self.check_order()
                        break

选择想要座位

    def choice_seats(self):
        while self.driver.title == '选座购买':
            while self.isElementExist('//*[@id="app"]/div[2]/div[2]/div[1]/div[2]/img'):
                # 座位手动选择 选中座位之后//*[@id="app"]/div[2]/div[2]/div[1]/div[2]/img 就会消失
                print('请快速的选择您的座位!!!')
            # 消失之后就会出现 //*[@id="app"]/div[2]/div[2]/div[2]/div
            while self.isElementExist('//*[@id="app"]/div[2]/div[2]/div[2]/div'):
                # 找到之后进行点击确认选座
                self.driver.find_element_by_xpath('//*[@id="app"]/div[2]/div[2]/div[2]/button').click()

下单

def check_order(self):
![请添加图片描述](https://img-blog.csdnimg.cn/4a0b661782e24b8ebf46390fb2333452.gif)
    if self.status in [3,4,5]:
        print('###开始确认订单###')
        try:
            # 默认选第一个购票人信息
            self.driver.find_element_by_xpath('//*[@id="container"]/div/div[2]/div[2]/div[1]/div/label').click()
        except Exception as e:
            print("###购票人信息选中失败,自行查看元素位置###")
            print(e)
        # 最后一步提交订单
        time.sleep(0.5)  # 太快会影响加载,导致按钮点击无效
        self.driver.find_element_by_xpath('//div[@class = "w1200"]//div[2]//div//div[9]//button[1]').click()

抢票成功, 退出当前程序

def finish(self):
    self.driver.quit()

测试代码

if __name__ == '__main__':
    try:
        con = Concert()             # 具体如果填写请查看类中的初始化函数
        con.enter_concert()         # 打开浏览器
        con.choose_ticket()         # 开始抢票

    except Exception as e:
        print(e)
        con.finish()

效果


好了,我的这篇文章写到这里就结束啦!

希望你在python这条路上依心而行,别回头,别四顾。一如既往不改初见的模样,未来的路很长,不管怎样,一定要相信自己一直走下去。

有更多建议或问题可以评论区或私信我哦!一起加油努力叭(ง •_•)ง

喜欢就关注一下博主,或点赞收藏评论一下我的文章叭!!!

春运到了,带你用python来抢票回家!

不知不觉,一年一度的春运抢票大幕已经拉开,想快速抢到回家的车票吗?作为程序员,这些技术手段,你一定要知道。

技术图片

为了让大家更快捷更便利的抢火车票,各种各样的抢票软件应需而生,这类软件大部分都是付费抢票的机制。

作为程序员,如何用技术手段抢到回家的票?来看看用 Python 写的抢票脚本。

手把手教你用 Python 抢票回家过年

环境介绍

windows 8.1
python3.6.1
firefox插件 geckodriver.exe

操作步骤

引入要的模块

        from selenium import webdriver      #控制浏览器
        from selenium.webdriver.common.keys import Keys  #用于给元素赋值
        import time   #时间模块
        from selenium.webdriver.support.select import Select  #控制下拉框模块
        from selenium.webdriver.common.by import By   #寻找元素模块
        from selenium.webdriver.support.ui import WebDriverWait  #“显示等待”模块
        from selenium.webdriver.support import expected_conditions as EC  #等待条件模块

 

登陆模块

首先需要选择使用的浏览器,此处以 firefox 为例,下载:geckodriver.exe 。

提到的 stations.txt 可以直接看这个:

车站信息:

https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9042

将 geckodriver.exe 放到 python.exe 同级目录下即可(如果有报错的情况下,放一个该文件到与 firefox.exe 同级目录下,并添加环境变量)

        #可以用input,也可以直接放入到后面的用户名、密码输入框中
        #可以利用标准输入进行批量的操作,此处以个人抢票操作为例
        # username = str(input(‘请输入你的用户名:‘))
        # password = str(input(‘请输入你的密码:‘))  #这两行可以暂时忽略

        browser = webdriver.Firefox()      #驱动firefox浏览器
        browser.get("https://kyfw.12306.cn/otn/login/init")    #启动浏览器后进入该链接下
        browser.find_element_by_id(username).clear()
        browser.find_element_by_id(username).send_keys(‘xxxxx’)    #xxxxx更换为用户名  
        browser.find_element_by_id(password).send_keys(‘xxxxx’)    #xxxxx更换为密码   
        time.sleep(10)    #此时验证码自行点击,该处设置10秒延迟,可以自己设置
        try:
                browser.find_element_by_id(loginSub).click()     #点击登陆操作,该id为登陆按钮
                #或者 browser.find_element_by_link_text(‘登陆‘).click()  #标签显示的名称
        except:
                browser.find_element_by_class_name(touclick-bgimg touclick-reload touclick-reload-normal).click()     #try中验证码输入点错了会在此处刷新一次
                time.sleep(20)                     #第二次输入验证码前等待20秒,可以自己设置,第一次输入无误直接跳过
                browser.find_element_by_id(loginSub).click()    #重新输入验证码后的点击登陆

 

跳转模块

#python学习群592539176
#默认跳转到首页
        time.sleep(2)    #此处一般无需设置时间等待,调试代码时使用
        clickReserve = browser.find_element_by_link_text(车票预订).click()  #跳转到车票预定页面,该页面可以查询票
        time.sleep(2)        #出发地点和到达地点设置
        #此处value值为出发时刻的地点,BJP表示北京,更改value值在页面上不加载,基本不耗时间,从页面中也看不到出发地和目的地
        #此处内容以爬取,保存在stations.txt中,每行表示一个地址,打开文档ctrl + F查找即可
        jsf = var a = document.getElementById("fromStation");a.value = "BJP"    #此处将BJP更换为你需要的出发地址,value值在以爬取到stations.txt中,自行查看
        browser.execute_script(jsf)
        jst = var a = document.getElementById("toStation");a.value = "LZJ"   #终点,同上方法
        browser.execute_script(jst)
        js = "document.getElementById(‘train_date‘).removeAttribute(‘readonly‘)"    #时间选择时默认为只读,通过JS移除只读属性
        browser.execute_script(js)    #执行JS语句
        browser.find_element_by_id(train_date).clear()    #时间元素中默认有提示字,需要先清空
        browser.find_element_by_id(train_date).send_keys(2018-02-01)   #按照改格式输入需要查询的时间
        search = browser.find_element_by_id(query_ticket).click()    #输入好信息时点击查询,该处存在成人票和学生票,默认是成人票,如果购买,对学生票处执行以下语句即可:
        #browser.find_element_by_id(‘xxxx‘).click()    #对于id还是class或其它自行选择,[可以查看此处](http://blog.51cto.com/12376665/2052278)

 

开始购票

此处,就是点击预定的操作,我在这里只是举一个方法例子,也可以通过不断点击直到成功(这样可以避免网站倒计时和实际时间的时间差影响,但是不知道 12306 在抢票时对不断快速访问有没有限制)。

  start_time = "Thu Jan 04 08:00:00 2018"    #首先设置需要抢票的时间
        b = time.mktime(time.strptime(start_time,"%a %b %d %H:%M:%S %Y"))        print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(b)) )  #此处是为了调试代码使用,可忽略,不影响使用
        a = float(b)-time.time()    #利用自己设置的时间减去当前时间的时间戳
        time.sleep(a)    #上一步骤得出的秒数就是需要等待抢票的时间
try:     #此处本来有try中的部分就够了,WebDriverWait已有相应等待重复访问机制,默认为0.5秒试验一次,except中添加是为了以防万一
    WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))   #查找需要预定的车次的id,直到出现,10表示共等待10秒
    ticket = browser.find_element_by_xpath(//tr[@id="ticket_2400000Z550L"]/td[13]/a).click()    #点击预定按钮except:
    browser.find_element_by_id(query_ticket).click()
    WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))
    ticket = browser.find_element_by_xpath(//tr[@id="ticket_2400000Z550L"]/td[13]/a).click()
"""
normalPassenger_8 数字表示该账号下的第几位,默认从0开始如果是第一个则为normalPassenger_0
"""WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "normalPassenger_8")))
browser.find_element_by_id(normalPassenger_8).click()   #id中的8表示账号下第九位s = Select(browser.find_element_by_id(‘seatType_1‘))
s.select_by_value(6)    #此处value值看下方各个种类,6表示高级软卧browser.find_element_by_id(‘submitOrder_id‘).click()
WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "qr_submit_id")))
browser.find_element_by_link_text(提交订单)
browser.find_element_by_id(qr_submit_id).click()#-------------------------------------------------结束#硬座 1#硬卧 3#软卧 4#高级软卧 6#二等座 O(大写字母)#一等座 M#商务座 9

 

总结

需要替换的地方:

  • 用户名,密码。

  • 起始地点和目的地的 value 值,查 stations.txt 修改即可。

  • 出发时间。

  • 自己选择车次的 xpath 路径,路径不用变,变对应 id 即可。

  • 勾选用户的位置(如果只要一个用户,默认用:normalPassenger_0)。

  • 所选座位类别,默认为有票的类别里最便宜的种类。

其余的在测试中都相同,没有发现有变化,在使用前,可以测试一下代码,测试是注意注释掉提交订单的代码(下单有取消限制,每天好像只能取消三次),测试时网速正常。

有人说用浏览器执行速度会慢,确实对于可以直接识别验证码的脚本而言,没有界面的会更快一些,但是实际上所用时间为预定开始到结束,相同网络下,代码执行时间是要快于人工操作的,

另外,时间可以研究一下,之前研究过某宝的时间,秒杀时间是要比北京时间提前一点几秒的,感觉全国各地有微小时间差的。

完整脚本示例

#python学习群592539176
#python3.6.1#data:2018-01-03#author:LGC247CG"""
说明:
1.该脚本主要是提供一个实现思路,实现方法有很多,可以优化的地方也有很多,触发机制也可以自己设置,代码以压缩到最短,只是为了让大家都可以看明白
2.正常网络状况下,不设置指定时间时,从点击确认验证码到下单基本上1秒左右,所以速度上还是没问题的
3.由于同时勾选多人和单人使用所需时间基本相同,希望该方法只用于技术交流,请勿作为黄牛使用
4.在作为技术交流的情况下,如果验证码可以实现将可以完全实现自动抢票:
--1>验证码有一定规律和数量,可以利用脚本获取所有图片,并加上相应标签
--2>将页面的文字和标签相匹配,再将图片进行相似度计算,对对应图片进行点击操作
--3>或是训练深度学习的图片识别模型,通过算法识别
"""from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport timefrom selenium.webdriver.support.select import Selectfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC

browser = webdriver.Firefox()
browser.get("https://kyfw.12306.cn/otn/login/init")
browser.find_element_by_id(‘username‘).clear()
browser.find_element_by_id(‘username‘).send_keys(‘xxxxxxx‘)
browser.find_element_by_id(‘password‘).send_keys(‘xxxxxxx‘)
time.sleep(10)try:
    browser.find_element_by_id(‘loginSub‘).click()except:
    browser.find_element_by_class_name(‘touclick-bgimg touclick-reload touclick-reload-normal‘).click()
    time.sleep(15)
    browser.find_element_by_id(‘loginSub‘).click()#跳转到车票预定页面time.sleep(2)
clickReserve = browser.find_element_by_link_text(‘车票预订‘).click()#出发地点和到达地点设置WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "fromStation")))
jsf = ‘var a = document.getElementById("fromStation");a.value = "BJP"‘browser.execute_script(jsf)
jst = ‘var a = document.getElementById("toStation");a.value = "LZJ"‘browser.execute_script(jst)
js = "document.getElementById(‘train_date‘).removeAttribute(‘readonly‘)"browser.execute_script(js)
browser.find_element_by_id(‘train_date‘).clear()
browser.find_element_by_id(‘train_date‘).send_keys(‘2018-02-02‘)
search = browser.find_element_by_id(‘query_ticket‘).click()#对于时间,我一直觉得网站计算的时间和自己获取的时间差一秒左右,这个根据不同环境自己测试start_time = "Thu Jan 04 10:00:00 2018"    #首先设置需要抢票的时间b = time.mktime(time.strptime(start_time,"%a %b %d %H:%M:%S %Y"))
print(time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(b)) )  #此处是为了调试代码使用,可忽略,不影响使用a = float(b)-time.time()    #利用自己设置的时间减去当前时间的时间戳time.sleep(a)    #上一步骤得出的秒数就是需要等待抢票的时间browser.find_element_by_id(‘query_ticket‘).click()    #时间到了先点击查询刷新一下,以防找不到元素try:
    WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "ticket_2400000Z550L")))
    ticket = browser.find_element_by_xpath(‘//tr[@id="ticket_2400000Z550L"]/td[13]/a‘).click()except:
    browser.find_element_by_id(‘query_ticket‘).click()
    WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.ID, "ticket_250000K8880L")))
    ticket = browser.find_element_by_xpath(‘//tr[@id="ticket_250000K8880L"]/td[13]/a‘).click()"""
normalPassenger_8 数字表示该账号下的第几位,默认从0开始如果是第一个则为normalPassenger_0
"""WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "normalPassenger_8")))
browser.find_element_by_id(‘normalPassenger_8‘).click()
s = Select(browser.find_element_by_id(‘seatType_1‘))
s.select_by_value(‘6‘)
browser.find_element_by_id(‘submitOrder_id‘).click()
WebDriverWait(browser,10).until(EC.presence_of_element_located((By.ID, "qr_submit_id")))
browser.find_element_by_link_text(‘提交订单‘)#browser.find_element_by_id(‘qr_submit_id‘).click()

以上是关于想去看演唱却总是抢不到票?教你用Python制作一个自动抢票脚本的主要内容,如果未能解决你的问题,请参考以下文章

过年回家抢不到火车票?教你用 Python 开发 12306 查票神器

春运到了,带你用python来抢票回家!

演唱会抢票

抢红包神器上线,再也不怕抢不到红包了!

Python操作12306抢票脚本

春节还怕抢不到票?Github上11k star开源神器助你一臂之力