Python爬虫实战之12306抢票开源


Posted in Python onJanuary 24, 2019

今天就和大家一起来讨论一下python实现12306余票查询(pycharm+python3.7),一起来感受一下python爬虫的简单实践

我们说先在浏览器中打开开发者工具(F12),尝试一次余票的查询,通过开发者工具查看发出请求的包

Python爬虫实战之12306抢票开源

余票查询界面

可以看到红框框中的URL就是我们向12306服务器发出的请求,那么具体是什么呢?我们来看看
https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-01-21&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=SZQ&purpose_codes=ADULT
可以看到发出请求的几个字段:

leftTicketDTO.train_date:查询的日期
leftTicketDTO.from_station:查询的出发地
leftTicketDTO.to_station:查询的目的地
purpose_codes:不太清楚这个字段是用来做什么的,就默认吧

可以从我们递交的URL请求看出,我们输入的成都,深圳都变成了对应的编号,比如,成都(CDW)、深圳(SZQ),所以当我们程序进行输入的时候要进行一下处理,12306的一个地方存储着这些城市名与编码对应的文档:

https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.8971

Python爬虫实战之12306抢票开源

站点编码对应

下面我们就编写一个小程序,将这些城市名与编号提取出来:

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)
#将车站的名字和编码进行提取
chezhan = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
chezhan_code = dict(chezhan)
#进行交换
chezhan_names = dict(zip(chezhan_code.values(),chezhan_code.keys()))
#打印出得到的车站字典
print(chezhan_names)

得到的打印结果如下(只截取部分显示):

{'VAP': '北京北', 'BOP': '北京东', 'BJP': '北京', 'VNP': '北京南', 'BXP': '北京西', 'IZQ': '广州南', 'CUW': '重庆北', 'CQW': '重庆', 'CRW': '重庆南', 'CXW': '重庆西', 'GGQ': '广州东', 'SHH': '上海', 'SNH': '上海南', 'AOH': '上海虹桥', 'SXH': '上海西', 'TBP': '天津北', 'TJP': '天津', 'TIP': '天津南', 'TXP': '天津西', 'XJA': '香港西九龙', 'CCT': '长春', 'CET': '长春南', 'CRT': '长春西', 'ICW': '成都东', 'CNW': '成都南', 'CDW': '成都', 'CSQ': '长沙', 'CWQ': '长沙南',}

接下来我们就动手开始程序的主要代码编写:

def main():
  date     = input("请输入时间(如2019-01-22):\n")
  from_station = chezhan_code[input("请输入起始站点:\n")]
  to_station  = chezhan_code[input("请输入目的站点:\n")]
  url     = "https://kyfw.12306.cn/otn/leftTicket/queryZ?"
  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"
  }
  url=url+"leftTicketDTO.train_date="+date+"&leftTicketDTO.from_station="+from_station+"&leftTicketDTO.to_station="+to_station+"&purpose_codes=ADULT"
  #print(url) 已经检查过生成的URL是正确的
  #request请求获取主页
  r = requests.get(url,headers=headers)
  r.raise_for_status()  #如果发送了一个错误的请求,会抛出异常
  r.encoding = r.apparent_encoding
  showTicket(r.text)

用户输入时间、起始站点、目的站点,然后通过get来请求,然后我们对返回的网页信息进行解析。我们现将上面代码的r.text进行打印,看看我们请求之后,返回了什么样的信息,然后决定我们应该如何解析

Python爬虫实战之12306抢票开源

运行结果

这样看着不方便,我们粘贴到记事本中,进行详细的分析:

Python爬虫实战之12306抢票开源

请求返回的结果信息

可以与12306显示的信息进行对比,K829是车次,CDW与BJQ是出发地和目的地,10:10是出发时间,06:13是到达时间,44:21是历时时间,20190123为查询的日期,剩下的就是一系列票的各种信息。

下面就是对这些返回的信息进行解析,其实这也是python爬虫的关键,就是解析!!!

我们先把信息转化为json格式,可以看到都是用“|”隔开的,那么我们就用split函数分割出来,下面是主要功能代码:

def showTicket(html):
  html = json.loads(html)
  table = PrettyTable([" 车次 ","出发车站","到达车站","出发时间","到达时间"," 历时 ","商务座"," 一等座","二等座","高级软卧","软卧","动卧","硬卧","软座","硬座","无座","其他","备注"])
  for i in html['data']['result']:
    name = [
          "station_train_code",
          "from_station_name",
          "to_station_name",
          "start_time",
          "arrive_time",
          "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"
        ]
    data = {
          "station_train_code": '',
          "from_station_name": '',
          "to_station_name": '',
          "start_time": '',
          "arrive_time": '',
          "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]       #高级软卧信息在21号位置
    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()
    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(chezhan_names[data["from_station_name"]])
          tmp.append(s)
        elif y == "to_station_name":
          s = color.red(chezhan_names[data["to_station_name"]])
          tmp.append(s)
        elif y == "start_time":
          s = color.green(data["start_time"])
          tmp.append(s)
        elif y == "arrive_time":
          s = 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)

那么我们程序就成功啦!!!

Python爬虫实战之12306抢票开源

运行结果

但是在编译器里面Prettytable的格子没有对齐,不要担心,我们到终端运行一下脚本,就可以看到很好看的输出啦:

Python爬虫实战之12306抢票开源

终端运行结果

Python爬虫实战之12306抢票开源

完成!!!下面是完整代码

main.py

# -*- coding: utf-8 -*-
import re,requests,datetime,time,json
from prettytable import PrettyTable
from colorama import init,Fore
from stationinfo import chezhan_code,chezhan_names
init(autoreset=False)
class Colored(object):
  def yeah(self,s):
    return Fore.LIGHTCYAN_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",
          "to_station_name",
          "start_time",
          "arrive_time",
          "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"
        ]
    data = {
          "station_train_code": '',
          "from_station_name": '',
          "to_station_name": '',
          "start_time": '',
          "arrive_time": '',
          "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]       #高级软卧信息在21号位置
    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()
    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(chezhan_names[data["from_station_name"]])
          tmp.append(s)
        elif y == "to_station_name":
          s = color.yeah(chezhan_names[data["to_station_name"]])
          tmp.append(s)
        elif y == "start_time":
          s = color.green(data["start_time"])
          tmp.append(s)
        elif y == "arrive_time":
          s = color.yeah(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     = input("请输入时间:\n")
  from_station = chezhan_code[input("请输入起始站点:\n")]
  to_station  = chezhan_code[input("请输入目的站点:\n")]
  url     = "https://kyfw.12306.cn/otn/leftTicket/queryZ?"
  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"
  }
url=url+"leftTicketDTO.train_date="+date+"&leftTicketDTO.from_station="+from_station+"&leftTicketDTO.to_station="+to_station+"&purpose_codes=ADULT"
  #print(url) 已经检查过生成的URL是正确的
  #request请求获取主页
  r = requests.get(url,headers=headers)
  r.raise_for_status()  #如果发送了一个错误的请求,会抛出异常
  r.encoding = r.apparent_encoding
  showTicket(r.text)
  #print(r.text)
main()

stationinfo.py

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)
#将车站的名字和编码进行提取
chezhan = re.findall(r'([\u4e00-\u9fa5]+)\|([A-Z]+)', response.text)
chezhan_code = dict(chezhan)
chezhan_names = dict(zip(chezhan_code.values(),chezhan_code.keys()))
#print(chezhan_names)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。如果你想了解更多相关内容请查看下面相关链接

Python 相关文章推荐
Python的lambda匿名函数的简单介绍
Apr 25 Python
讲解Python中if语句的嵌套用法
May 14 Python
Python3生成手写体数字方法
Jan 30 Python
ubuntu17.4下为python和python3装上pip的方法
Jun 12 Python
Python中GIL的使用详解
Oct 03 Python
Python实现登陆文件验证方法
Oct 06 Python
python 快速把超大txt文件转存为csv的实例
Oct 26 Python
Python代码打开本地.mp4格式文件的方法
Jan 03 Python
Python实现的读取文件内容并写入其他文件操作示例
Apr 09 Python
基于python实现文件加密功能
Jan 06 Python
使用Python合成图片的实现代码(图片添加个性化文本,图片上叠加其他图片)
Apr 30 Python
python3实现Dijkstra算法最短路径的实现
May 12 Python
python+pyqt5实现24点小游戏
Jan 24 #Python
python中实现控制小数点位数的方法
Jan 24 #Python
对python以16进制打印字节数组的方法详解
Jan 24 #Python
python3实现点餐系统
Jan 24 #Python
使用Python批量修改文件名的代码实例
Jan 24 #Python
Python并发:多线程与多进程的详解
Jan 24 #Python
python用opencv批量截取图像指定区域的方法
Jan 24 #Python
You might like
使用 eAccelerator加速PHP代码的方法
2007/09/30 PHP
mysql 性能的检查和优化方法
2009/06/21 PHP
php通过修改header强制图片下载的方法
2015/03/24 PHP
动态表格Table类的实现
2009/08/26 Javascript
IE下支持文本框和密码框placeholder效果的JQuery插件分享
2015/01/31 Javascript
jQuery插件实现文字无缝向上滚动效果代码
2016/02/25 Javascript
详解XMLHttpRequest(一)同步请求和异步请求
2016/09/14 Javascript
angular中的http拦截器Interceptors的实现
2017/02/21 Javascript
bootstrap常用组件之头部导航实现代码
2017/04/20 Javascript
微信小程序调用PHP后台接口 解析纯html文本
2017/06/13 Javascript
vue实现图书管理demo详解
2017/10/17 Javascript
Vuex新手的理解与使用详解
2019/05/31 Javascript
turn.js异步加载实现翻书效果
2019/07/25 Javascript
angular中的post请求处理示例详解
2020/06/30 Javascript
[16:56]heroes英雄教学 司夜刺客
2014/09/18 DOTA
Python的collections模块中的OrderedDict有序字典
2016/07/07 Python
Python中的二维数组实例(list与numpy.array)
2018/04/13 Python
python3下使用cv2.imwrite存储带有中文路径图片的方法
2018/05/10 Python
python中的常量和变量代码详解
2018/07/25 Python
win7下python3.6安装配置方法图文教程
2018/07/31 Python
pytorch 中pad函数toch.nn.functional.pad()的用法
2020/01/08 Python
Python基于类路径字符串获取静态属性
2020/03/12 Python
keras导入weights方式
2020/06/12 Python
英国男士时尚网站:Dandy Fellow
2018/02/09 全球购物
英国鲜花递送:Blossoming Gifts
2020/07/10 全球购物
Android面试题附答案
2014/12/08 面试题
EJB3.1都有哪些改进
2012/11/17 面试题
自考生自我鉴定范文
2013/10/01 职场文书
自动化系在校本科生求职信
2013/10/23 职场文书
秘书岗位职责
2013/11/18 职场文书
招标保密承诺书
2015/01/20 职场文书
店铺转让协议书
2015/01/29 职场文书
办公室主任岗位职责范本
2015/03/31 职场文书
公司欠款证明
2015/06/24 职场文书
html+css合并表格边框的示例代码
2021/03/31 HTML / CSS
详解Oracle数据库中自带的所有表结构(sql代码)
2021/11/20 Oracle