python 3爬取 12306余票

 

马上暑假集训结束,又要开学了,暑假集训老师让我们交暑假的学习成果~~虽然制作了几个小的网络爬虫的东西软件,什么淘宝,京东,天气,图片爬取的....但还是想要交个更好的,毕竟还是有成绩的。。。爬取12306功能是实现了,但是图形界面看到一位博主模仿12306把图形界面写的那么好看,我就搬过来,不停的修改,一直有个BUG错,两个py文件调用其中一个函数返回的列表居然是空的!!!然而打印那个列表有正确的数据!!难受,网上也搜索了问题,也自己试了一下,感觉方法没错。。。。就是有问题

还是回归正题吧,这里直说爬取12306功能的到实现,以及展示,图形界面(升级版)点击传送门:https://blog.csdn.net/memory_qianxiao/article/details/81987185

环境:python3.6 (虽然目前最新版是3.7 但是一些库还没与3.7调试好,可能会出问题)编译器用你自己喜欢的就好~

第三方库:requests(网页请求),re(正则提取信息),datetime(日期时间),time,json(把数据转字典),prettytable(使输出美观),colorama(上色)

先放一张效果图镇楼!!

python 3爬取 12306余票_第1张图片

 

当你打开12306网址进行查票的时候,查询了后可以看到结果:

python 3爬取 12306余票_第2张图片

 

然后按F12,我们可以看到查票的网址:https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2018-08-22&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=HZH&purpose_codes=ADULT

python 3爬取 12306余票_第3张图片

但是你会惊讶的发现,请求网址里面没有我们输入的成都杭州!这里我就不拐弯了,因为12306把输入的车站名字,转换成对应的车站代码了,比如上面的成都和杭州就转成了CDW,HZH。所以当我们程序输入的时候需要处理一下输入,把输入的车站转换成对应的车站代码。

在12306里面有所有车站和车站对应的代码:访问这个网址:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971

python 3爬取 12306余票_第4张图片

发现密密麻麻的车站和对应的车站代码,所以这里我们要用正则提取车站和车站代码,把对应车站和代码关联起来,同时在把key和values交换关联一下,建立起来键值对。对输入的汉字转换成代码,同时也可以把代码转换成汉字。

这里贴出代码:

import re,requests
#访问12306存有的所有车站
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971'
response = requests.get(url, verify=False)
#提取车站名字和代码
stations = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
station_codes=dict(stations)
#把车站名字和代码,交换一下,重新建立键值对
station_names=dict(zip(station_codes.values(),station_codes.keys()))


这里我们打印一下结果:由于很长,我就截取一部分,可以看见建立了字典。

到时候就能够对输入的数据进行转化。

接下来是重点:是功能的实现

创建三个函数

主函数:入口     请求的网址我们需要传入三个参数,起点,终点,时间,在上面网址我标红了。

def main():
    #调用系统本地时间
    date=time.strftime("%Y-%m-%d",time.localtime())
    #调用上面处理所有车站的代码,需要当做第三方库引入,待会完整版代码展示
    from_station=station_codes[input("请输入起始站:\n")]
    to_station=station_codes[input("请输入目地站:\n")]
    url="https://kyfw.12306.cn/otn/leftTicket/query?"
    headers={
        "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5702.400 QQBrowser/10.2.1893.400"
        }
    #time=entry_time.get()
    #start=entry_start.get()
    #end=entry_end.get()
    url=url+'leftTicketDTO.train_date='+date+'&leftTicketDTO.from_station='+from_station+'&leftTicketDTO.to_station='+to_station+'&purpose_codes=ADULT'
    html=getHtmltext(url,headers)
    showTicket(html)

请求网址以及数据的返回:

def getHtmltext(url,headers):
    r=requests.get(url,headers=headers)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    return r.text

展示票的函数:里面有颜色,也可以把颜色去掉,因为在pycharm里面,用了也不会显示颜色,只有在dos窗口就可以显示颜色。这里需要说一下prettytable,它能够是输出的数据美观化,具体用法,请百度~其实展示票的函数可以不要这么长的代码的,几十行就可以的,只是为了模仿12306对没有票的用-代替美观化的。

这里面我们要说一下请求网址返回的数据:

python 3爬取 12306余票_第5张图片

虽然数据看起来确实比较乱,不过发现里面是双重字典,我们提取里面的信息先转成json,然后for i in html['data']['result']这种遍历提取里面所有的信息,然后有很多||符号,所以用split('|')分割。就把对应的数据取出来。里面是一个列表,车次在3号位置,始发站信息在6号位置,出发时间信息在8号位置,抵达时间在9号位置......

def showTicket(html):
    html=json.loads(html)
    table=PrettyTable(["车次","出发/到达车站","出发/到达时间","历时","商务座","一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座","其他","备注"])
    for i in html['data']['result']:
        name = [
        "station_train_code",
        "from_station_name",
        'start_time',
        "lishi",
        "swz_num",
        "zy_num",
        "ze_num",
        "gr_num",
        "rw_num",
        "dw_num",
        "yw_num",
        "rz_num",
        "yz_num",
        "wz_num",
        "qt_num",
        "note_num"
        ]
        data={
             "station_train_code": '',
            "from_station_name": '',
            "to_station_name": '',
            'start_time': '',
            'end': '',
            "lishi": '',
            "swz_num": '',
            "zy_num": '',
            "ze_num": '',
            "dw_num": '',
            "gr_num": '',
            "rw_num": '',
            "yw_num": '',
            "rz_num": '',
            "yz_num": '',
            "wz_num": '',
            "qt_num": '',
            "note_num": ''
            }
        item = i.split('|')#用"|"进行分割
        data['station_train_code'] = item[3]#车次在3号位置
        data['from_station_name'] = item[6]#始发站信息在6号位置
        data['to_station_name'] = item[7]#终点站信息在7号位置
        data['start_time'] = item[8]#出发时间信息在8号位置
        data['arrive_time'] = item[9]#抵达时间在9号位置
        data['lishi'] = item[10]#经历时间在10号位置
        data['swz_num'] = item[32] or item[25]# 特别注意:商务座在32或25位置
        data['zy_num'] = item[31]#一等座信息在31号位置
        data['ze_num'] = item[30]#二等座信息在30号位置
        data['gr_num'] = item[21]#高级软卧信息在31号位置
        data['rw_num'] = item[23]#软卧信息在23号位置
        data['dw_num'] = item[27]#动卧信息在27号位置
        data['yw_num'] = item[28]#硬卧信息在28号位置
        data['rz_num'] = item[24]#软座信息在24号位置
        data['yz_num'] = item[29]#硬座信息在29号位置
        data['wz_num'] = item[26]#无座信息在26号位置
        data['qt_num'] = item[22]#其他信息在22号位置
        data['note_num'] = item[1]#备注在1号位置
        color = Colored()#创建Colored对象
        data["note_num"]=color.white(item[1])
        #如果没有信息用'-'代替
        for pos in name:
            if data[pos]=='':
                data[pos]='-'
        tickets=[]
        cont=[]
        cont.append(data)
        for x in cont:
            tmp = []
            for y in name:
                if y == "from_station_name":
                    s = color.green(station_names[data['from_station_name']])+ '\n' +color.red(station_names[data["to_station_name"]])
                    tmp.append(s)
                elif y == "start_time":
                    s = color.green(data['start_time']) + '\n' + color.red(data["arrive_time"])
                    tmp.append(s)
                elif y == "station_train_code":
                    s = color.yellow(data['station_train_code'])
                    tmp.append(s)
                else:
                    tmp.append(data[y])
            tickets.append(tmp)
        for ticket in tickets:
            table.add_row(ticket)
    print(table)

在上面数据有color的,只是为了更方便看,但是编译器显示不出来,所以可以去掉。

接下来贴出完整代码:


#
import requests,re,datetime,time,json,datetime
from tkinter import*
from tkinter import messagebox
from prettytable import PrettyTable
#这个库是自己写的,把所有车站提取出来的.py文件,我也会放在下面。
from stationsInfo import station_codes,station_names
from colorama import init,Fore,Back,Style
def getHtmltext(url,headers):
    r=requests.get(url,headers=headers)
    r.raise_for_status()
    r.encoding=r.apparent_encoding
    return r.text
init(autoreset=False)
class Colored(object):
    #  前景色:红色  背景色:默认
    def red(self, s):
        return Fore.LIGHTRED_EX + s + Fore.RESET
    #  前景色:绿色  背景色:默认
    def green(self, s):
        return Fore.LIGHTGREEN_EX + s + Fore.RESET
    def yellow(self, s):
        return Fore.LIGHTYELLOW_EX + s + Fore.RESET
    def white(self,s):
        return Fore.LIGHTWHITE_EX + s + Fore.RESET
    def blue(self,s):
        return Fore.LIGHTBLUE_EX + s + Fore.RESET 
def showTicket(html):
    html=json.loads(html)
    table=PrettyTable(["车次","出发/到达车站","出发/到达时间","历时","商务座","一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座","其他","备注"])
    for i in html['data']['result']:
        name = [
        "station_train_code",
        "from_station_name",
        'start_time',
        "lishi",
        "swz_num",
        "zy_num",
        "ze_num",
        "gr_num",
        "rw_num",
        "dw_num",
        "yw_num",
        "rz_num",
        "yz_num",
        "wz_num",
        "qt_num",
        "note_num"
        ]
        data={
             "station_train_code": '',
            "from_station_name": '',
            "to_station_name": '',
            'start_time': '',
            'end': '',
            "lishi": '',
            "swz_num": '',
            "zy_num": '',
            "ze_num": '',
            "dw_num": '',
            "gr_num": '',
            "rw_num": '',
            "yw_num": '',
            "rz_num": '',
            "yz_num": '',
            "wz_num": '',
            "qt_num": '',
            "note_num": ''
            }
        item = i.split('|')#用"|"进行分割
        data['station_train_code'] = item[3]#车次在3号位置
        data['from_station_name'] = item[6]#始发站信息在6号位置
        data['to_station_name'] = item[7]#终点站信息在7号位置
        data['start_time'] = item[8]#出发时间信息在8号位置
        data['arrive_time'] = item[9]#抵达时间在9号位置
        data['lishi'] = item[10]#经历时间在10号位置
        data['swz_num'] = item[32] or item[25]# 特别注意:商务座在32或25位置
        data['zy_num'] = item[31]#一等座信息在31号位置
        data['ze_num'] = item[30]#二等座信息在30号位置
        data['gr_num'] = item[21]#高级软卧信息在31号位置
        data['rw_num'] = item[23]#软卧信息在23号位置
        data['dw_num'] = item[27]#动卧信息在27号位置
        data['yw_num'] = item[28]#硬卧信息在28号位置
        data['rz_num'] = item[24]#软座信息在24号位置
        data['yz_num'] = item[29]#硬座信息在29号位置
        data['wz_num'] = item[26]#无座信息在26号位置
        data['qt_num'] = item[22]#其他信息在22号位置
        data['note_num'] = item[1]#备注在1号位置
        color = Colored()#创建Colored对象
        data["note_num"]=color.white(item[1])
        #如果没有信息用'-'代替
        for pos in name:
            if data[pos]=='':
                data[pos]='-'
        tickets=[]
        cont=[]
        cont.append(data)
        for x in cont:
            tmp = []
            for y in name:
                if y == "from_station_name":
                    s = color.green(station_names[data['from_station_name']])+ '\n' +color.red(station_names[data["to_station_name"]])
                    tmp.append(s)
                elif y == "start_time":
                    s = color.green(data['start_time']) + '\n' + color.red(data["arrive_time"])
                    tmp.append(s)
                elif y == "station_train_code":
                    s = color.yellow(data['station_train_code'])
                    tmp.append(s)
                else:
                    tmp.append(data[y])
            tickets.append(tmp)
        for ticket in tickets:
            table.add_row(ticket)
    print(table)
def main():
    date=time.strftime("%Y-%m-%d",time.localtime())
    from_station=station_codes[input("请输入起始站:\n")]
    to_station=station_codes[input("请输入目地站:\n")]
    url="https://kyfw.12306.cn/otn/leftTicket/query?"
    headers={
        "User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.5702.400 QQBrowser/10.2.1893.400"
        }
    #time=entry_time.get()
    #start=entry_start.get()
    #end=entry_end.get()
    url=url+'leftTicketDTO.train_date='+date+'&leftTicketDTO.from_station='+from_station+'&leftTicketDTO.to_station='+to_station+'&purpose_codes=ADULT'
    print(url)
    html=getHtmltext(url,headers)
    showTicket(html)
main()

stationinfo:

import re,requests

url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971'
response = requests.get(url, verify=False)
stations = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
station_codes=dict(stations)
station_names=dict(zip(station_codes.values(),station_codes.keys()))

 

你可能感兴趣的:(python,学习之路,网络爬虫)