记一次帮HTSS用python抢票经历

引子

话说最近上线的电影很多,根据饶雪漫阿姨的小说《左耳》改编的电影明天就该上映了,据说又是青春类的,找个机会去看看。。。
不对,跑题了,接着回来说这个抢票的事情,起因是这个样子的,《左耳》的导演苏有朋下周一要来我校进行一个专题讲座,按照惯例,这种著名嘉宾的讲座的票就会比较火(要我说,以后再有这种大家来,直接去体育馆开得了,座位绝对够)。当然,像我这种孤独寂寞冷的单身狗,一般对这种“大众偶像”啥的不大感冒,但是呢,关键来了,HT叔叔(某个室友的外号)的“准女票”想去听讲座,但是又苦于抢不到票,于是就来求助HT叔叔了,恰好学校开通了网上抢票通道(就是一个叫做ML的App//ML不是make love的缩写,别想歪了)。作为一个“程序猿”,好好抢票肯定不是我的风格,学了那么多语言不是用来写hello world自己看着玩儿的,该用的时候就得拿出来实践一下。

前期准备

既然是App,那就先抓包看看,有请Burp Suite上场。
burp suite

配置好代理之后,在手机上登陆ML,刷新文章列表,正好看到一个可以抢的票,点一下,竟然抢到了,也截获了一个数据包,开始分析。

这是登陆数据包(username和password敏感信息已替换)

GET /api/login?password=PASSWORD&account=USERNAME&device=iPhone6%2C2&mac=02-00-00-00-00-00&ip=192.168.1.101 HTTP/1.1

登陆竟然用GET,我也是醉了。。。后面device啥的也不知道怎么获取的。。。明明是5s,非要说我是6,无所谓了。。。
返回数据(除了token之外,都做了一定程度的替换):


{
"code": 1,
"response": {
    "uid": "UID",
    "account": "USERNAME",
    "school_id": "SID",
    "department_id": "DID",
    "ids": "1",
    "email_v": 0,
    "nick": "NICKNAME",
    "name": "REALNAME",
    "gender": "1",
    "phone": "",
    "email": "",
    "start_year": "2014",
    "short_desc": "",
    "last_update": LASTUPDATE,
    "guid": "GUID",
    "type": "s",
    "school": "SCHOOL",
    "department": "DEPARTMENT",
    "avatar": "",
    "avatar_full": "",
    "bg_img": "",
    "contact_privacy": 0,
    "primary_badges": [],
    "primary_badge": 0,
    "primary_badge_image": "",
    "badges": [],
    "photos": [],
    "face_id": "",
    "exp": 1540,
    "points": 1540,
    "token": "72d649559baca8de",
    "checkin": 0
    }
}

大概看了看,估计也就uid和token有点用。

关于刷文章列表的数据包:

GET /api/broadcast?time=0&request=broadcast_latest&uid=UID&token=72d649559baca8de HTTP/1.1

很明显嘛,uid和token这两个数据在后面的操作中会用到,返回数据就不贴了,没有太大的价值。

抢票数据包:

POST request=grab&uid=UID&bid=BID&token=72d649559baca8de

UID和token前面都说过,bid又是啥玩意?
回去看看文章列表,发现每个文章对应一个id,这个bid恰恰就是文章对应的id,重要的数据全部分析完毕!

初步尝试

既然相关的细节均已搞定,那么就来试一试吧,我用的是chrome的插件postman,这是一个可以模拟post、get等请求的应用,在平时测试网站的时候还是蛮好用的。
登陆功能通过一个GET请求就解决掉了,获取到UID和TOKEN之后,请求抢票接口,会返回类似的数据

{
"code": 1,
"response": {
    "enable": false,
    "code": "",
    "tip": "开始时间:2015-04-23 14:45\ 结束时间:2015-04-23 16:45",
    "button": "抢票已结束"
    }
}

既然是这样,那就通过code的值判断是否抢到就好了,button可以判断当前抢票的状态。

实践

贴一下大致的代码,敏感信息已隐去,请各位大牛们轻喷。

import urllib
import urllib2
import ssl
import json
import time

ssl._create_default_https_context = ssl._create_unverified_context # 关闭ssl验证

login = "URL?password=PASSWORD&account=USERNAME" # 登陆地址
login_data = urllib2.urlopen(login)
login_data = login_data.read()

login_data = json.loads(login_data)

if login_data["code"] != 1: #判断是否登陆成功,如果不成功,会返回非1的错误代码
    print login_data["code"]
else:
    token = login_data["response"]["token"]
    uid = login_data["response"]["uid"]

ticket_url = 'URL'
post_data = {
    'request':'grab',
    'uid':uid,
    'bid':'BID',
    'token':token,
}
ticket_data = urllib.urlencode(post_data)
ticket = urllib2.Request(ticket_url, ticket_data)
while(1): # 重复发送数据包,肯定不能抢一次就完了
    response = urllib2.urlopen(ticket)
    result = response.read()
    result_data = json.loads(result)
    print time.strftime('%Y-%m-%d %H:%M:%S') # 输出当前的时间,便于判断速度
    print result_data["response"]["code"] # 看看抢没抢到,其实可以用if语句的,只是因为懒就没写
    print result_data["response"]["button"] # 看看当前的抢票状态

尾声

接近十点的时候,服务器响应变得异常的慢,估计都在请求数据,最后总算是抢到了,祝HT叔叔和准女票早日在一起。
抢票成功

反思

其实抑制住我这样的小白刷票也不是没有办法,加上验证码就是最简单的办法(我知道有打码平台),就像小米抢手机防黄牛一样(虽然没怎么防住)。
抢票结束之后,看到ML平台上,有很多人在卖票,¥150一张。
从另一方面来讲,学校是否应该考虑一下如何合理的分发门票(黄牛是不可能100%杜绝的),或者对于一些知名的嘉宾,安排一个更大的场地。就写这么多吧,因为部分内容比较敏感,介绍的不是很详细,只是在这里记录一些自己的想法,欢迎交流。

标签: python, 抢票, 门票