总结一下用python+selenium抢12306票的小程序(最新)

前段时间买火车票老是需要让携程给我弄什么加速包来抢票,于是就想着自己弄一个抢票的小程序。刚开始本来也没打算用selenium来做,因为这个部署到服务器很麻烦,所以想着用requests+js2py来做,尝试了一下后发现从登陆到订票一系列的参数和跳转太多了,分析得我脑壳痛,于是不得已还是用了selenium+chromedriver,不得不说,嗯,真香!

我先说一下我的思路吧:
1、首先抛开登陆这个先不谈,先把简单的事儿做了。先尝试查询一下火车余票信息,这个其实可以用requests很简单的就能爬下来。当有余票信息后立马调用selenium。
2、对于selenium这部分来说主要是解决登录和订票问题,登录所需的验证码还是需要去找到地址爬取下来,然后人为去识别然后再在页面上获取其(x,y)值然后发送点击事件。这样是比较麻烦的一种做法,我想到一个简单的,就是用代码中实现在selenium上一次登录后记录下cookie值,写进文件里面,下次再需要的时候直接读取cookie值发送过去就行了。
3、订票的过程没什么好说的,按照页面点击就行。
4、最后由于订到票之后会有30分钟的付款时间,所以可以做一个邮件提醒功能,或是手机短信提醒,这样就能及时的付款了,还能调用微信的接口,给自己发条微信,也是可以的。

具体先看查询余票,12306有自己的一长串城市和城市代码的map,就是下面这个,我写了一个方法,方便返回城市名或城市代码。

station_map = '@bjb|北京北|VAP|beijingbei|bjb|0@bjd|北京东|BOP|beijingdong|bjd|1@bji|北京|BJP|beijing|bj|2@bjn|'
def get_city_or_citycode(city):
    for i in station_map.split( '@'):
        if i:
            tmp = i.split( '|')
            if city == tmp[1]:
                return tmp[2]
            elif city==tmp[2]:
                return tmp[1]

    return False

然后就可以发送请求了
 

import requests,json
from city_map import get_city_or_citycode
headers={
'Accept':'*/*',
'Accept-Encoding':'gzip, deflate, br',
'Accept-Language':'zh-CN,zh;q=0.9',
'Cache-Control':'no-cache',
'Connection':'keep-alive',
'Host':'kyfw.12306.cn',
'If-Modified-Since':'0',
'Referer':'https://kyfw.12306.cn/otn/leftTicket/init',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
def query_ticker(riqi="2018-12-31",from_sta='北京',to_sta="上海"):
    from_sta=get_city_or_citycode(from_sta)
    to_sta=get_city_or_citycode(to_sta)
    url='https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT'%(riqi,from_sta,to_sta)
    resp=requests.get(url)
    data=json.loads(resp.text)
    #这一步就拿到数据了
    results=data.get('data').get('result')

    #做一下数据清洗
    index = 0
    trains = []
    for i in results:
        trains.append([])
        for n in i.split('|'):
            trains[index].append(n)
        index += 1

    bancis=[]
    for train in trains:
        per={}
        per['班次'] =train[3]
        per['出发地'] =get_city_or_citycode(train[6])
        per['目的地'] =get_city_or_citycode(train[7])
        per['发车时间'] =train[8]
        per['到达时间'] =train[9]
        per['历时时间'] =train[10]
        per['商务座'] = train[32]
        per['一等座'] =train[31]
        per['二等座'] =train[30]
        per['高级软卧'] =train[21]
        per['软卧'] =train[23]
        per['硬卧'] =train[28]
        per['硬座'] =train[29]
        per['无座'] =train[26]
        bancis.append(per)
    print(bancis)
    return bancis


if __name__ == '__main__':
    riqi=input('请输入时间(例如"2018-01-01"):')
    from_city=input('请输入出发站:')
    to_city=input('请输入终点站:')
    print('查询ing......')
    trains=query_ticker(riqi,from_city,to_city)

按照它的规则组装好url就行,headers那部分我也没测哪些有用哪些没用,反正都给弄上去了,如果不行的话就再加上cookie,就应该没问题。
再就是抢票部分,这部分用了selenium,判断了一下当前目录下有没有cookie文件,没有的话需要手动在浏览器内后写入cookie,然后无限循环查票抢票,抢到后就调用邮件发送类,随便发个什么邮件以示抢票成功就行了,也可以换用微信接口,但是由于这部分不是重点,就不做多说了。


class Qiangpiao(object):

    def __init__(self):
        self.login_url = 'https://kyfw.12306.cn/otn/login/init'
        self.initmy_url = 'https://kyfw.12306.cn/otn/view/index.html'
        self.search_url = 'https://kyfw.12306.cn/otn/leftTicket/init'
        self.confirmPassenger = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc'
        self.need_write_cookie = True
        self.success=False


    def wait_input(self):
        self.from_station = input('出发地:')
        self.to_station = input('目的地:')
        #"2018-12-01"
        self.depart_time = input('出发时间:')

        self.passengers = input('乘客姓名:(多个乘客使用英文逗号分割)').split(',')
        self.trains = input('车次:(多个车次使用英文逗号分割)').split(',')

    def _login(self,driver):
        #查找cookies文件,没有则需要写入
        path=os.getcwd()
        lists=os.listdir(path)
        for i in lists:
            if i == 'cookies.json':
                self.need_write_cookie = False
                break

        if self.need_write_cookie:
            driver.get(self.login_url)
            print(11)
            WebDriverWait(driver,1000).until(EC.url_to_be(self.initmy_url))

            dictCookies = driver.get_cookies()
            jsonCookies = json.dumps(dictCookies)
            # 登录完成后,将cookie保存到本地文件
            with open('cookies.json', 'w') as f:
                f.write(jsonCookies)
                print('恭喜您,您已登录成功了!正在写入cookie......')
            print('cookie写入成功!')
            self.need_write_cookie = False
        else:
            # 一定要先在这个域下建立连接,并删除初始cookie,然后读取cookie
            driver.get("https://kyfw.12306.cn/otn/leftTicket/init")
            driver.delete_all_cookies()
            with open('cookies.json', 'r', encoding='utf-8') as f:
                listCookies = json.loads(f.read())
            for cookie in listCookies:
                driver.add_cookie({
                    'domain': '.12306.cn',
                    'name': cookie['name'],
                    'value': cookie['value'],
                    'path': '/',
                    'expires': None
                })
            print('添加cookie成功')
    def _order_ticket(self,driver):
        #订票
        driver.get("https://kyfw.12306.cn/otn/leftTicket/init")
        if driver.find_element_by_xpath('//input[@id="fromStationText"]'):

            from_btn=driver.find_element_by_xpath('//input[@id="fromStationText"]')
            to_btn=driver.find_element_by_xpath('//input[@id="toStationText"]')
            from_btn.click()
            from_btn.send_keys(self.from_station)
            from_btn.send_keys(Keys.ENTER)
            to_btn.click()
            to_btn.send_keys(self.to_station)
            to_btn.send_keys(Keys.ENTER)

            go_input=driver.find_element_by_xpath('//input[@id="train_date"]')
            go_input.click()

            now_or_next_month=handle_time(self.depart_time)
            if now_or_next_month[0]=='NOW_MONTH':
                go_date=driver.find_element_by_xpath('//div[@class="cal"]//div[@class="cal-cm"]//div[@class="cell"][position()=%s]'%now_or_next_month[1])
            else:
                go_date=driver.find_element_by_xpath('//div[@class="cal cal-right"]//div[@class="cal-cm"]//div[@class="cell"][position()=%s]'%now_or_next_month[1])
            go_date.click()

            query_btn=driver.find_element_by_xpath('//a[@id="query_ticket"]')
            query_btn.click()

        WebDriverWait(driver, 1000).until(EC.presence_of_element_located((By.XPATH,".//tbody[@id = 'queryLeftTable']/tr")))

        tr_list = driver.find_elements_by_xpath(".//tbody[@id ='queryLeftTable']/tr[not(@datatran)]")

        for tr in tr_list:
            train_number = tr.find_element_by_class_name('number').text
            if train_number in self.trains:
                left_ticket = tr.find_element_by_xpath('.//td[3]').text #找到第四个td标签下的文本
                print(left_ticket,left_ticket.isdigit())
                if left_ticket == '有' or left_ticket.isdigit(): #判断输入的车次是否在列表中
                    orderBotton = tr.find_element_by_class_name('btn72')
                    orderBotton.click()
                    #获取所有的乘客信息
                    WebDriverWait(driver, 1000).until(EC.url_to_be(self.confirmPassenger))
                    WebDriverWait(driver, 1000).until(EC.presence_of_element_located((By.XPATH,".//ul[@id = 'normal_passenger_id']/li")))
                    passanger_labels = driver.find_elements_by_xpath(".//ul[@id = 'normal_passenger_id']/li/label")
                    for passanger_label in passanger_labels:
                        name = passanger_label.text
                        if name in self.passengers:#判重
                            passanger_label.click() 

                            #获取提交订单的按钮
                            submitBotton = driver.find_element_by_id('submitOrder_id')
                            submitBotton.click()
                            #等待确人订单对话框
                            WebDriverWait(driver, 1000).until(EC.presence_of_element_located((By.CLASS_NAME,'dhtmlx_wins_body_outer')))
                            #等待确认按钮
                            WebDriverWait(driver, 1000).until(EC.presence_of_element_located((By.ID,'qr_submit_id')))
                            ConBotton = driver.find_element_by_id('qr_submit_id')
                            ConBotton.click()
                            mail_sender=MailSender('1111111@qq.com','今天去香格里拉吃饭')
                            mail_sender.sendEmail()
                            while ConBotton:
                                ConBotton.click()
                                ConBotton = driver.find_element_by_id('qr_submit_id')
                            self.success=True

                            return


    def run(self,driver):
        self.wait_input()
        self._login(driver)
        while not self.success:
            self._order_ticket(driver)

if __name__ == '__main__':

    driver = webdriver.Chrome(chrome_options=chrome_options,executable_path='D:\chromedriver.exe')

    spider = Qiangpiao()
    spider.run(driver)

最后还有改进的地方就是改写成多任务,这个也不难,再就是付款流程,因为要在30分钟内完成支付才行,所以可以进入付款页面获取支付的二维码图片,截下来后通过微信发送给自己就行了,也是很方便的。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值