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

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

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

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

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

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

第一次请求:服务器

很明显第一次请求返回的值没有咱们须要的车次信息。异步

第二次请求:函数

第二次请求里面看到了不少数据,虽然咱们暂时还没看到车次信息,可是咱们发现它有个特性,就是有个列表的值里面有6个元素,而恰好咱们搜索出来的从长沙到成都的车辆也是6条数据,因此这二者确定有必定关系,那么咱们先用Python来获取到这些数据再进行下一步分析:工具

# -*- coding: utf-8 -*- importurllib2 importsslssl._create_default_https_context = ssl._create_unverified_context defgetList():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() returnhtml printgetList()网站

首先定义一个函数来获取车次列表信息:

从抓包数据中获取到该请求的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

zhengshu5.com
dajinnylee.cn
xc.xyseo.net
xyseo.net/xuancai/

是由于12306采用的是https协议,而ssl证书是它本身作的并无获得浏览器的承认,因此Python默认是不会请求不受信任的证书的网站的,咱们能够经过这行代码来关闭掉证书的验证

那么咱们先来看看能不能正常获取到咱们想要的信息 :

事实证实咱们的操做没有问题,接下来先拿到包含有6条数据的这个列表再说。

返回的数据是json格式,可是Python标准数据类型中没有json这个类型,因此对于Python而言它就是个字符串,若是要很是方便的操做这个json咱们就能够借助Python中的json这个包来把json这个字符串变成dict类型,而后经过dict的键值对操做方法把列表取出来并进行返回。

# -*- coding: utf-8 -*-importurllib2 importssl importjsonssl._create_default_https_context = ssl._create_unverified_contextdef 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( WindowsNT10. 0; Win64; x64)AppleWebKit/ 537. 36( KHTML, like Gecko) Chrome/ 59. 0. 3071. 115Safari/ 537. 36') html = urllib2.urlopen(req).read() dict = json.loads(html) result= dict['data'][' result'] returnresult

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

foriingetList(): 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,无这样的车次信息的,只不过看起来比较乱,可是他们都有一个特色,每一个数据都是由|这个符号分开的,因此咱们能够经过用|分割看看能发现什么呢?

foriingetList(): forn ini. split('|'): print n break

能够看到全部的值都打印出来了,咱们再在前面加上一个序号就能清楚到看到每一个序号所对应的值究竟是什么了,好比有辆火车硬座还剩3张票,软卧还剩8张票,那咱们就查看哪一个序号对应的值是3哪一个序号对应的值是8就搞清楚了哪一个序号是表明什么座次或者其余参数了。

c = 0fori ingetList(): forn ini.split( '|'): print'[%s] %s'%(c,n) c += 1c = 0break#索引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= CDWleftTicketDTO.to_station=CSQ

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

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

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

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

而后咱们经过把参数作成经过输入出发城市和到达城市就能够直接在这个数据里面匹配到相应的城市代号,代码以下:

station = {} fori incons.station_names. split( '@'): ifi: tmp = i. split( '|') station[tmp[ 1]] = tmp[ 2] #print stationtrain_date = raw_input( '请输入出发时间')from_station = station[raw_input( '请输入出发城市')]to_station = station[raw_input( '请输入到达城市')]

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

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

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

相关文章
相关标签/搜索