利用Python攻破12306的最后一道防线

各位同学大家好,我是强子,好久没跟大家带来最新的技术文章了,最近有好几个同学问我12306自动抢票能否实现,我就趁这两天有时间用Python做了个12306自动抢票的项目,在这里我来带着大家一起来看看到底如何一步一步攻克万恶的12306,嘻嘻~~

我们要做12306抢票而官方又没有提供相应的接口(也不可能提供),那么我们就只能通过自己寻找12306的数据包和买票流程来模拟浏览器行为实现自动化操作了,说直白一点就是爬虫,接下来进入正题,前方高能,请系好好全带~~

首先在买票前我们需要先确认是否有票,那么进行正常的查票,打开12306查票网站https://kyfw.12306.cn/otn/leftTicket/init输入出发地和目的地进行搜索

那么一般在看到这个页面的时候我们能想到的获取车次及相关信息的方式是什么呢?对于零基础的同学而言第一时间就会想到在源代码里面找,但这里事实上源代码里面根本没有相关内容,因为该请求是采用的js中ajax异步请求的方式动态加载的,并不包含在源代码里面,所以我们只能够通过抓包的方式来查看浏览器与服务器的数据交互情况,我用的是谷歌浏览器所以打开开发者工具的快捷键是F12

注意选中红线框出来的那一个选项,此时只要是浏览器和服务器发生数据交互都会在下面列表框显示出来,我们再次点击查询按钮

结果发现列表当中有了两个请求,也就是说我们点击查询按钮以后浏览器向服务器发起了两次请求,那么我们来通过返回值分析下那个请求才是真正获取到车次相关数据的请求,以便我们用Python来模拟浏览器操作

第一次请求:

很明显第一次请求返回的值没有我们需要的车次信息
第二次请求:

第二次请求里面看到了很多数据,虽然我们暂时还没看到车次信息,但是我们发现它有个特性,就是有个列表的值里面有6个元素,而刚好我们搜索出来的从长沙到成都的车辆也是6条数据,所以这两者肯定有一定关系,那么我们先用Python来获取到这些数据再进行下一步分析

# -*- coding: utf-8 -*-
import urllib2
import ssl

ssl._create_default_https_context = ssl._create_unverified_context

def getList():
    req = urllib2.Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT')
    req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
    html = urllib2.urlopen(req).read()
    return html

print getList()

首先定义一个函数来获取车次列表信息
从抓包数据中获取到该请求的url:https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT
为了防止被12306检测到屏蔽我们的请求那么我们可以简单的增加个头信息来模拟浏览器的请求

req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')

其中的

ssl._create_default_https_context = ssl._create_unverified_context

是因为12306采用的是https协议,而ssl证书是它自己做的并没有得到浏览器的认可,所以Python默认是不会请求不受信任的证书的网站的,我们可以通过这行代码来关闭掉证书的验证
那么我们先来看看能不能正常获取到我们想要的信息


事实证明我们的操作没有问题,接下来先拿到包含有6条数据的这个列表再说
返回的数据是json格式,但是Python标准数据类型中没有json这个类型,所以对于Python而言它就是个字符串,如果要非常方便的操作这个json我们就可以借助Python中的json这个包来把json这个字符串变成dict类型,然后通过dict的键值对操作方法把列表取出来并进行返回

# -*- coding: utf-8 -*-
import urllib2
import ssl
import json

ssl._create_default_https_context = ssl._create_unverified_context

def getList():
    req = urllib2.Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT')
    req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
    html = urllib2.urlopen(req).read()
    dict = json.loads(html)
    result = dict['data']['result']
    return result

最终返回的是一个list数据,我们先把这个数据for出来再看看每一条数据都有些什么东西

for i in getList():
    print i

for出来之后我们先来看看第一条数据是什么样的:

|预订|76000G131805|G1318|ICW|IZQ|ICW|CWQ|07:54|18:54|11:00|N|UHESFcaIDeX22Z0zWfqttDuZXJFuWPdIa148i6TNk5spIqfp|20170710|3|W2|01|16|0|0|||||||||||无|无|无||O0M090|OM9

其实我们稍微留一下就会发现里面有包含G1318,07:54,18:54,无这样的车次信息的,只不过看起来比较乱,但是他们都有一个特点,每个数据都是由|这个符号分开的,所以我们可以通过用|分割看看能发现什么呢?

for i in getList():
    for n in i.split('|'):
        print n
    break

可以看到所有的值都打印出来了,我们再在前面加上一个序号就能清楚到看到每个序号所对应的值到底是什么了,比如有辆火车硬座还剩3张票,软卧还剩8张票,那我们就查看哪个序号对应的值是3哪个序号对应的值是8就搞清楚了哪个序号是代表什么座次或者其他参数了

c = 0
for i in getList():
    for n in i.split('|'):
        print '[%s] %s' %(c,n)
        c += 1
    c = 0
    break
#索引3=车次
#索引8=出发时间
#索引9=到达时间

到了这里不知道同学们有没有发现一个问题,就是我用的这个函数只能够获取到从长沙成都的数据,而别人不一定是买这个方向的火车,所以我们还得搞清楚请求的url当中的出发站和到达站的值是怎么来的

https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT

先找到出发站和到达站的参数分别是

leftTicketDTO.from_station=CDW
leftTicketDTO.to_station=CSQ

然而通过查找和分析我并没有发现这两个参数有规律,那么也就是说这两个值是在之前的请求里面就已经获取到了的,通过检查网页源代码没有找到,那么又只能通过抓包的方式来找

在抓包过程中找到了一个包的返回值是附带有各城市的代号的,url如下:

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

那么我们把这里面的城市数据复制出来单独新建一个cons.py的文件保存起来

然后我们通过把参数做成通过输入出发城市和到达城市就可以直接在这个数据里面匹配到相应的城市代号,代码如下:

station = {}
for i in cons.station_names.split('@'):
    if i:
        tmp = i.split('|')
        station[tmp[1]] = tmp[2]
#print station

train_date = raw_input('请输入出发时间')
from_station = station[raw_input('请输入出发城市')]
to_station = station[raw_input('请输入到达城市')]

到这里就已经能够通过输入时间,城市获取相应的车次信息了

那么我们再进行一些简单的判断,就能实现检查相应的时间,地点,车次是否有余票了

同时再结合登录,购票等流程,通过自动判断是否有票,如果无票就继续刷新,直到有票之后自动登录下单后通过短信或者电话等方式全自动联系购票人手机就可以了,如下图

由于涉及到的知识点太多,仅仅用文字的方式很难表现的面面俱到,所以有时间的同学可以到我的课堂做客,现场直播讲解更加的生动易懂!

好了,本次分享就到这里了,我是强子,我们下次再见!


主题大纲

在学习Python的过程中很多时候对于ajax异步加载和验证码的爬虫束手无策,其实只要了解了原理就容易解决了,一个爬虫高手一定是一个web开发高手,因为爬虫和反爬是相辅相成的,能够完成12306网站的自动购票基本能搞定互联网80%以上的爬虫
知识点1:Python爬虫包的使用
知识点2:文本处理,提取指定信息,过滤无用信息
知识点3:爬虫开发思想和原则,爬取深度定义
知识点4:ajax异步的处理
知识点5:项目完整业务逻辑实现

听众收益

  1. 了解爬虫开发流程
  2. 熟练使用相应的包来实现目标数据爬取
  3. 编码的原理及处理方法,独立处理所有的编码问题
  4. 学习高级爬虫的分析和开发
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容