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标准日志模块logging的使用方法
Nov 01 Python
python3使用tkinter实现ui界面简单实例
Jan 10 Python
python通过线程实现定时器timer的方法
Mar 16 Python
python获取本机mac地址和ip地址的方法
Apr 29 Python
Python中列表元素转为数字的方法分析
Jun 14 Python
python3.0 模拟用户登录,三次错误锁定的实例
Nov 02 Python
Python实现的井字棋(Tic Tac Toe)游戏示例
Jan 31 Python
Python cookbook(数据结构与算法)实现对不原生支持比较操作的对象排序算法示例
Mar 15 Python
Linux下python制作名片示例
Jul 20 Python
Python实现的插入排序,冒泡排序,快速排序,选择排序算法示例
May 04 Python
python实时监控logstash日志代码
Apr 27 Python
Win 10下Anaconda虚拟环境的教程
May 18 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
获取php页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
2013/06/03 PHP
PHP扩展模块memcached长连接使用方法分析
2014/12/24 PHP
php异常处理方法实例汇总
2015/06/24 PHP
PHP模块化安装教程
2016/06/01 PHP
Yii2主题(Theme)用法详解
2016/07/23 PHP
php workerman定时任务的实现代码
2018/12/23 PHP
firefox浏览器下javascript 拖动层效果与原理分析代码
2007/12/04 Javascript
jquery 之 $().hover(func1, funct2)使用方法
2012/06/14 Javascript
document.documentElement和document.body区别介绍
2013/09/16 Javascript
JavaScript字符串对象substring方法入门实例(用于截取字符串)
2014/10/17 Javascript
Wireshark基本介绍和学习TCP三次握手
2016/08/15 Javascript
AngularJS的依赖注入实例分析(使用module和injector)
2017/01/19 Javascript
AngularJS实现页面定时刷新
2017/03/14 Javascript
微信小程序开发之从相册获取图片 使用相机拍照 本地图片上传
2017/04/18 Javascript
浅谈js获取ModelAndView值的问题
2018/03/28 Javascript
Angular5.0 子组件通过service传递值给父组件的方法
2018/07/13 Javascript
ES6知识点整理之函数对象参数默认值及其解构应用示例
2019/04/17 Javascript
javascript合并两个数组最简单的实现方法
2019/09/14 Javascript
Python安装Imaging报错:The _imaging C module is not installed问题解决方法
2014/08/22 Python
Python遍历目录的4种方法实例介绍
2015/04/13 Python
13个最常用的Python深度学习库介绍
2017/10/28 Python
用pandas按列合并两个文件的实例
2018/04/12 Python
Python编程在flask中模拟进行Restful的CRUD操作
2018/12/28 Python
Python简单I/O操作示例
2019/03/18 Python
对Django中内置的User模型实例详解
2019/08/16 Python
Python 通过截图匹配原图中的位置(opencv)实例
2019/08/27 Python
python随机生成库faker库api实例详解
2019/11/28 Python
Python导入模块包原理及相关注意事项
2020/03/25 Python
HTML5 本地存储之如果没有数据库究竟会怎样
2013/04/25 HTML / CSS
比较基础的php面试题及答案-编程题
2012/10/14 面试题
CLR与IL分别是什么含义
2016/08/23 面试题
党员自我剖析材料范文
2014/10/06 职场文书
商铺门面租房协议书
2014/10/21 职场文书
呐喊读书笔记
2015/06/30 职场文书
Pandas搭配lambda组合使用详解
2022/01/22 Python
Java使用HttpClient实现文件下载
2022/08/14 Java/Android