Python爬蟲之:12306查票

#!/usr/bin/env python2.7
12306查票腳本
有基本的餘票查詢,監控功能,暫無訂票功能

# -*- coding: utf-8 -*-
"""
author='Mr RaoJL',
author_email='[email protected]',
description=' Train ticket inquiry Script'
aothor Date='Wed Jan 11 17:42:14 2017'
"""
import urllib2
import ssl
import json
import sys
import re
import time
import smtplib
# from City_code import City_Code
from email.mime.text import MIMEText
email_FROM_city=''
email_TO_city=''
email_DATE_city=''
DESC_title = """
 __  __        ____                 _ _
|  \/  |_ __  |  _ \ __ _  ___     | | |
| |\/| | '__| | |_) / _` |/ _ \ _  | | |
| |  | | |    |  _ < (_| | (_) | |_| | |___
|_|  |_|_|    |_| \_\__,_|\___/ \___/|_____|

"""
HELPR="""
 1 查詢
 2 將查詢的信息發送郵件
 Q 查詢可用站點 用法: 1> Q [站點名] 2> Q ALL 列出全部(較長)
 F 監控餘票 使用: F [出發地] [目的地] [日期,標準格式:xxxx-xx-xx] [座位類型,如:硬座] [列車編號,如:K935] [查詢次數,默認爲1次]
 exit 退出程序
"""
send_head = {
                "Host": "kyfw.12306.cn",
                "User-Agent": 'Mozilla / 5.0(X11;Fedora;Linux x86_64;rv:50.0) Gecko / 20100101Firefox / 50.0',
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                'Connection': 'keep-alive'
            }
City_Code = {}
reload(sys)
sys.setdefaultencoding('utf-8')
ssl._create_default_https_context = ssl._create_unverified_context
Faile = 0

def HTTP_query(trainDate, From_city, To_city):
    try:
        try:
            bagful = "https://kyfw.12306.cn/otn/leftTicket/queryA?" \
                     "leftTicketDTO.train_date={0}&" \
                     "leftTicketDTO.from_station={1}&" \
                     "leftTicketDTO.to_station={2}" \
                     "&purpose_codes=ADULT" \
                     "".format(trainDate, City_Code[From_city], City_Code[To_city])
            req = urllib2.Request(bagful, headers=send_head)
        except KeyError:
            print "無法找到的站點"
            return False
        except NameError:
            print "無法找到輸入的站點"
            return False

        global GET_INFO
        GET_INFO = urllib2.urlopen(req).read()
        return True
    except urllib2.HTTPError as alter:
        print "錯誤: [%s]!"% alter
        pass
def Get_City_code():
    print "讀取站點數據庫..."
    try:
        Ci_url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8994'
        Ci_text = urllib2.urlopen(Ci_url).read()
        Ci_text = Ci_text.replace('\'', '').replace('var station_names =\'', '')
        city = re.findall('\|\W+\|[A-Z]{3}', Ci_text)
        for i in city:
            City_Code[re.split('\|', i)[1]] = re.split('\|', i)[2]
        print "讀取成功!\n輸入h或help獲取幫助"
    except urllib2.URLError as erron:
        print "錯誤:%s無法讀取站點數據,請檢查網絡後重試!"%erron
        sys.exit()
def Get_Data(trainDate, From_city, To_city):
    global info
    json._default_encoder = 'UTF-8'
    try:
        data = json.loads(GET_INFO, encoding='UTF-8')
    except NameError:
        print "無法找到輸入的站點"
        return False
    tables = []
    info = []
    try:
        for i in data['data']:
            tables.append(i)
        if True:
            for j in tables:
                info.append(j['queryLeftNewDTO'])

    except KeyError:
        print "\033[31m未查詢到數據\033[0m"
        pass
        return Faile
    List_Data(From_city, To_city, trainDate)
def Filter_Data(From, To, Date, zw_type, tr_Num):
    HTTP_query(Date, From, To)
    F_info = []
    F_table = []
    json._default_encoder = 'uft-8'
    try:
        assert isinstance(GET_INFO, object)
        data = json.loads(GET_INFO, encoding='UTF-8')
    except NameError:
        print "無法找到輸入的站點"
        return False
    try:
        for i in data['data']:
            F_table.append(i)
        if True:
            for j in F_table:
                F_info.append(j['queryLeftNewDTO'])

    except KeyError:
        print "\033[31m未查詢到數據\033[0m"
    try:
        exchange_name(zw_type)
        for i in F_info:
            if i['station_train_code'] == tr_Num and (i[zw_type] == '有' or re.search('\d+', i[zw_type])):
                print '從{0}到{1},日期爲{2},列車 {3} {4}:{7}{5}{8} 出發時間:{7}{6}{8} '.format(From, To, Date, i['station_train_code'], ZW_NAME, i[zw_type], i['start_time'], StartColor, EndColor)

    except KeyError:
        print "輸入錯誤"
        return False

def exchange_name(self):
    global ZW_NAME
    ZW_NAME=''
    if self == 'yz_num':
        ZW_NAME = '硬座'
    if self == 'rw_num':
        ZW_NAME = '軟我'
    if self == 'yw_num':
        ZW_NAME = '硬臥'
    if self == 'wz_num':
        ZW_NAME = '無座'
    else:
        print ""
        return False
def List_Data(From_city, To_city, trainDate):
    if True:
        seq = 0
        print "查詢結果:"
        global StartColor
        global EndColor
        StartColor = '\033[32m'
        EndColor = '\033[0m'
        try:
            from prettytable import PrettyTable
            List_train = PrettyTable(['序列', '列車類型', '出發時間', '到達時間', '歷時', '硬座', '硬臥', '軟臥', '無座', '起始站','終點站','列車狀態'])
            for z in info:
                seq += 1
                if re.search('K', z['station_train_code']):
                    liche_code = "快速 "
                elif re.search('G', z['station_train_code']):
                    liche_code = "高鐵 "
                elif re.search('Z', z['station_train_code']):
                    liche_code = "直達 "
                elif re.search('T', z['station_train_code']):
                    liche_code = "特快 "
                elif re.search('D', z['station_train_code']):
                    liche_code = "動車組 "
                elif re.search('Y', z['station_train_code']):
                    liche_code = "旅行專列 "
                elif re.search('C', z['station_train_code']):
                    liche_code = "城際 "
                elif re.search('\d+', z['station_train_code']):
                    liche_code = '普通 '
                else:
                    liche_code = ""
                List_train.add_row(
                    [StartColor + str(seq) + EndColor, StartColor + liche_code + z['station_train_code'] + EndColor,
                     StartColor + z['start_time'] + EndColor,
                     StartColor + z['arrive_time'] + EndColor, StartColor + z['lishi'] + EndColor,
                     StartColor + z['yz_num'] + EndColor, StartColor + z['yw_num'] + EndColor,
                     StartColor + z['rw_num'] + EndColor, StartColor + z['wz_num'] + EndColor,
                     StartColor + z['start_station_name'] + EndColor, StartColor+z['end_station_name']+EndColor,
                     StartColor + z['controlled_train_message'] + EndColor])
            print List_train
            print "查詢完成,從 {2} 到 {3},日期爲{1}的列車,共{0}條記錄.".format(len(info), trainDate, From_city, To_city, trainDate)
            return True
        except ImportError:
            for z in info:
                seq += 1
                if re.search('K', z['station_train_code']):
                    liche_code = "快速 "
                elif re.search('G', z['station_train_code']):
                    liche_code = "高鐵 "
                elif re.search('Z', z['station_train_code']):
                    liche_code = "直達 "
                elif re.search('T', z['station_train_code']):
                    liche_code = "特快 "
                elif re.search('D', z['station_train_code']):
                    liche_code = "動車組 "
                elif re.search('Y', z['station_train_code']):
                    liche_code = "旅行專列 "
                elif re.search('C', z['station_train_code']):
                    liche_code = "城際 "
                elif re.search('\d+', z['station_train_code']):
                    liche_code = '普通 '
                else:
                    liche_code = ""
                print "-" * 140
                print "{5}\t{4}:\033[32m{0}\033[0m\t出發時間:\033[32m{1}\033[0m\t到達時間:\033[32m{2}\033[0m\t歷時:\033[32m{3}\033[0m\t" \
                    .format(z['station_train_code'], z['start_time'], z['arrive_time'], z['lishi'], liche_code, seq),
                print '硬座:\033[32m{0}\033[0m\t硬臥:\033[32m{2}\033[0m\t軟臥:\033[32m{3}\033[0m\t\033[0m無座:\033[32m{4}\033[0m\t列車狀態:\033[32m{1}\033[0m' \
                    .format(z['yz_num'], z['controlled_train_message'], z['yw_num'], z['rw_num'], z['wz_num'])
            print "查詢完成,從 {2} 到 {3},日期爲{1}的列車,共{0}條記錄.".format(len(info), trainDate, From_city, To_city, trainDate)


def Get_Input():
    """global From_city
    global To_city
    global trainDate
    """
    print "\033[32m-->>\033[0m進入12306查票控制檯"
    From_city = raw_input("請輸入出發城市(中文): ")
    To_city = raw_input("請輸入目的城市(中文): ")
    trainDate = raw_input("請輸入乘車日期(如:{0})默認即回車爲今日: ".format(time.strftime('%Y-%m-%d')))
    if trainDate == '':
        trainDate = time.strftime('%Y-%m-%d')
        print "請稍等,正在查詢從{0}到{1}日期:{2}的列車......".format(From_city, To_city, trainDate)
    else:
        CheckDate = re.search('[0-9]{4}.[0-9]{2}.[0-9]{2}', trainDate)
        if CheckDate:
            print "請稍等,正在查詢從{0}到{1},日期:{2}的列車......".format(From_city, To_city, trainDate)
        else:
            print "日期格式錯誤!請重試!"
            return False
    email_DATE_city = trainDate
    email_FROM_city  = From_city
    email_TO_city = To_city
    HTTP_query(trainDate, From_city, To_city)
    Get_Data(trainDate, From_city, To_city)


def Check_Input():
    if True:
        print "12306查票腳本(收錄全國{0}個站點,作者Mr RaoJL".format(City_Code.__len__())
        print DESC_title
        if True:
            while True:
                snedmail = raw_input("控制檯 >>> ")
                if snedmail == '2':
                    print "發送郵件..."
                    Get_Msg(email_FROM_city, email_TO_city, email_DATE_city)
                    if True:
                        print "發送成功"
                        continue
                    else:
                        print "發送失敗"
                        continue
                elif snedmail == '1':
                    Get_Input()
                    if False:
                        print "查詢失敗"
                        continue
                elif re.findall('Q \S+', snedmail):
                    search_wd = re.findall('Q \S+', snedmail)[0]
                    search_wd = search_wd.split(' ')[1]
                    for key in City_Code:
                        if search_wd in key:
                            print '\t' + key
                        elif search_wd == 'ALL':
                            print '\t' + key
                        else:
                            pass
                    if False:
                        print "無法找到你輸入的站點"

                elif re.findall('F \S+', snedmail):
                    if len(snedmail.split(' ')) < 6:
                        print "請輸入完整信息!"
                    else:
                        In_info = snedmail.split(' ')
                        s_city = In_info[1]
                        e_city = In_info[2]
                        s_date = In_info[3]
                        zw_type = ''
                        if In_info[4] == '硬座':
                            zw_type = 'yz_num'
                        elif In_info[4] == '硬臥':
                            zw_type = 'rw_num'
                        elif In_info[4] == '無座':
                            zw_type = 'wz_num'
                        elif In_info[4] == '軟臥':
                            zw_type = 'rw_num'
                        else:
                            print "參數錯誤"
                            continue
                        train_num = In_info[5]
                        i=0
                        cish = 1
                        try:
                            if In_info.__len__() == 7 and re.search('\d+',In_info[6]):
                                cish = int(In_info[6])
                                try:
                                    while i < cish:
                                        i += 1
                                        print "第%s次查詢" % i
                                        Filter_Data(s_city, e_city, s_date, zw_type, train_num)
                                except NameError:
                                    print "請先查詢"
                                    continue
                            else:
                                Filter_Data(s_city, e_city, s_date, zw_type, train_num)
                        except IndexError as esa:
                            Filter_Data(s_city, e_city, s_date, zw_type, train_num)
                elif snedmail == '':
                    continue
                elif snedmail == 'h' or snedmail == 'help':
                   print HELPR
                elif snedmail == 'exit':
                    print "退出程序!"
                    break
                else:
                    print "輸入 h 或者 help 獲取幫助"
                    continue
    else:
        print ""
        sys.exit()


def Send_Mail(content):
    mail = smtplib.SMTP('smtp.sina.com.cn')
    mail_addr = '[email protected]'
    mail_pass = 'xxxxxxx'
    mail.login(mail_addr, mail_pass)
    To = mail_addr
    msg = MIMEText(content, _subtype='html', _charset='utf-8')
    msg['Subject'] = "12306 Inspection results TIME:{0}".format(time.ctime())
    msg['From'] = mail_addr
    msg['To'] = To
    mail.sendmail(mail_addr, To, msg.as_string())


def Get_Msg(From_city, To_city, trainDate):
    TdSty = "style='color:#fff;width:100px;text-align:center'"
    TdstyT = "style='text-align:center'"
    content = "<center><table><tr bgcolor='#333'><td {0}>列車類別</td><td {0}>出發時間</td>" \
              "<td {0}>到達時間</td>" \
              "<td {0}>歷時</td>" \
              "<td {0}>硬座</td>" \
              "<td {0}>硬臥</td>" \
              "<td {0}>軟臥</td>" \
              "<td {0}>無座</td>" \
              "<td {0}>列車狀態</td></tr>".format(TdSty)
    seq = 0
    for i in info:
        seq += 1
        if re.search('K', i['station_train_code']):
            liche_code = "快速"
        elif re.search('G', i['station_train_code']):
            liche_code = "高鐵"
        elif re.search('Z', i['station_train_code']):
            liche_code = "直達"
        elif re.search('T', i['station_train_code']):
            liche_code = "特快"
        elif re.search('D', i['station_train_code']):
            liche_code = "動車組"
        elif re.search('Y', i['station_train_code']):
            liche_code = "旅行專列"
        else:
            liche_code = "列車編號"
        content += \
            "<tr style='color:blue'><td>{0}</td>" \
            "<td {9}>{1}</td>" \
            "<td {9}>{2}</td>" \
            "<td {9}>{3}</td>" \
            "<td {9}>{4}</td>" \
            "<td {9}>{5}</td>" \
            "<td {9}>{6}</td>" \
            "<td {9}>{7}</td>" \
            "<td {9}>{8}</td></tr>" \
                .format(liche_code + i['station_train_code'], i['start_time'], i['arrive_time'], i['lishi'],
                        i['yz_num'], i['yw_num'], i['rw_num'], i['wz_num'], i['controlled_train_message'], TdstyT)
    content += "</table></center><h1 style='color:red'> {0} => {1} Time: {2}; Total: {3}; QueryTime: {4}".format(
        From_city, To_city, trainDate, seq, time.strftime('%F %H:%M:%S'))
    try:
        Send_Mail(content)
        return True
    except smtplib.SMTPAuthenticationError as e:
        print "驗證失敗", e
        return False


try:
    Get_City_code()
    Check_Input()
except KeyboardInterrupt:
    print "\n\033[31m程序終止,已退出!\033[0m"


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章