您当前的位置:首页 > 计算机 > 编程开发 > Python

python3+selenium实现12306抢票脚本

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

一、 环境配置

  • 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上的源码

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