马上暑假集训结束,又要开学了,暑假集训老师让我们交暑假的学习成果~~虽然制作了几个小的网络爬虫的东西软件,什么淘宝,京东,天气,图片爬取的....但还是想要交个更好的,毕竟还是有成绩的。。。爬取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(上色)
先放一张效果图镇楼!!
当你打开12306网址进行查票的时候,查询了后可以看到结果:
然后按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
但是你会惊讶的发现,请求网址里面没有我们输入的成都和杭州!这里我就不拐弯了,因为12306把输入的车站名字,转换成对应的车站代码了,比如上面的成都和杭州就转成了CDW,HZH。所以当我们程序输入的时候需要处理一下输入,把输入的车站转换成对应的车站代码。
在12306里面有所有车站和车站对应的代码:访问这个网址:https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971
发现密密麻麻的车站和对应的车站代码,所以这里我们要用正则提取车站和车站代码,把对应车站和代码关联起来,同时在把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对没有票的用-代替美观化的。
这里面我们要说一下请求网址返回的数据:
虽然数据看起来确实比较乱,不过发现里面是双重字典,我们提取里面的信息先转成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()))