一個用python實現的東方時尚(駕校)搶課程序


  2014-12-23

  東方時尚約車還要網上選課,資源太緊張了,於是我決定自己寫一個搶票程序來幫我刷票。

  第一步,抓包。瀏覽器登陸選課系統,用抓包工具觀察網絡行為,這里我用的fildder。這里抓包主要需要獲取兩個信息,一是瀏覽器發送了哪幾條url請求,二是獲得http報文的頭部以及post出去的data內容和格式。

  第二步,模擬登陸。將抓取來的頭部抄過來,讓python模擬瀏覽器進行登陸,輸入用戶名和密碼。這里登陸需要驗證碼,先來一個手工識別的,讓python把獲取到的.jpg文件保存到本地,然后暫停等待輸入,人眼識別出驗證碼后輸入給程序,讓程序繼續執行。一開始總是說驗證碼錯誤,最后發現時請求方式不對,這里要注意,每次獲取url都要用同一個opener去獲取,這樣服務器才會認為是同一只瀏覽器。修改后,登錄成功。

  第三步,解決驗證碼。手工輸入驗證碼總非長遠之計,還是要讓機器去做。這里用了PIL包和pytesser包,里面有實現好了的解析驗證碼的方法,直接拿來用就行了。由於這個包對驗證碼的識別率不是100%,所以我把登錄的代碼放進一個while循環里,直到順利登錄為止。

  第四步,搶課。繼續抓包,分析選課的ur請求過程,然后讓程序模擬。比如在瀏覽器發現周五晚上有一節課可以選,然后讓程序去搶周五晚上的課,程序返回結果顯示ok,刷新瀏覽器,這節課確實選上了,說明程序大功告成了!

  后續,讀數據。東方時尚網站的制作者也不是完全吃素的。由於好的時間段通常都選不到課,我讓我的程序做while循環,一直刷課直到選上為止。刷了幾小時后,網頁說我的操作次數過多,今天禁止我的訪問。為了解決這個問題,我把刷課的頻率改為10分鍾一次。由於選課也需要驗證碼,而驗證碼識別率不高,這樣如果有課的時候因為驗證碼錯誤卻要等待10分鍾豈不是浪費機會了,所以我又將程序改為如果沒課,就等待10分鍾,如果有課就一直刷。這樣就又需要提取數據,分析網頁結構,發現有一個單獨的url用來存儲數據,剩余課時在其中的一個json格式的字符串里。先用正則匹配提取出這個串,然后解析這個json數據就得到需要的數據啦!

  最后,貼上我的代碼:

  1 import re
  2 import json
  3 import time
  4 import urllib
  5 import urllib2
  6 import urlparse
  7 import cookielib
  8 from PIL import Image, ImageDraw, ImageFont, ImageFilter
  9 from pytesser import *
 10 from datetime import date
 11 import os
 12 
 13 os.chdir('C://Python27/Lib/site-packages/pytesser')
 14 
 15 def getVerify(name):
 16     #data = urllib2.urlopen(
 17     im = Image.open(name)
 18     imgry = im.convert('L')
 19     text = image_to_string(imgry)
 20     text = re.sub('\W','',text)
 21     return text
 22 
 23 def urlToString(url):
 24     data = urllib2.urlopen(url).read()
 25     f = open('buffer/temp.jpg', 'wb')
 26     f.write(data)
 27     f.close()
 28     return getVerify('buffer/temp.jpg')
 29 
 30 def openerUrlToString(opener, url):
 31     data = opener.open(url).read()
 32     f = open('buffer/temp.jpg', 'wb')
 33     f.write(data)
 34     f.close()
 35     return getVerify('buffer/temp.jpg')
 36   
 37 def getOpener(head):
 38     # deal with the Cookies
 39     cj = cookielib.CookieJar()
 40     pro = urllib2.HTTPCookieProcessor(cj)
 41     opener = urllib2.build_opener(pro)
 42     header = []
 43     for key, value in head.items():
 44         elem = (key, value)
 45         header.append(elem)
 46     opener.addheaders = header
 47     return opener
 48 
 49 def decodeAnyType(data):
 50     ret = data
 51     try:
 52         temp = data.decode('utf-8')
 53         ret = temp
 54     except:
 55         pass
 56     try:
 57         temp = data.decode('gbk')
 58         ret = temp
 59     except:
 60         pass
 61     try:
 62         temp = data.decode('gb2312')
 63         ret = temp
 64     except:
 65         pass
 66     return ret
 67 
 68 header = {
 69     'Connection': 'Keep-Alive',
 70     'Accept': 'text/html, application/xhtml+xml, */*',
 71     'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
 72     'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
 73     'Accept-Encoding': 'gzip, deflate',
 74     'Host': 'wsyc.dfss.com.cn',
 75     'DNT': '1'
 76 }
 77 
 78 ## the data below are settled by customer to select the class needed
 79 start = 13
 80 end = 17
 81 numid = '3'
 82 year = 2014
 83 month = 12
 84 day = 22
 85 username = 'myname'
 86 password = 'mypasswd'
 87 
 88 opener = getOpener(header)
 89 url1 = 'http://wsyc.dfss.com.cn/'
 90 url2 = 'http://wsyc.dfss.com.cn/DfssAjax.aspx'
 91 url3 = 'http://wsyc.dfss.com.cn/validpng.aspx?aa=3&page=lg'
 92 url4 = 'http://wsyc.dfss.com.cn/pc-client/jbxx.aspx'
 93 url5 = 'http://wsyc.dfss.com.cn/validpng.aspx'
 94 
 95 ## try to login until the validcode is right
 96 count = 0
 97 while True:
 98     print '------------------------'
 99     print 'have tryed to login %d times, now try again!' % (count)
100     count = count + 1
101     validcode = openerUrlToString(opener, url3)
102     print 'the validcode is ' + validcode
103     postDict = {
104         'AjaxMethod': 'LOGIN',
105         'Account': username,
106         'ValidCode': validcode,
107         'Pwd': password
108     }
109 
110     postData = urllib.urlencode(postDict).encode()
111     op = opener.open(url2, postData)
112     result = op.read().decode('utf-8')
113     print 'the result of login is ' + result
114     #if result.find('true') >= 0:
115     if result == 'true':
116         print 'login success!'
117         break
118     else:
119         continue
120     
121 
122 yuechedate = date(year, month, day)
123 today = date.today()
124 intervaldays = (yuechedate - today).days
125 print intervaldays
126 if intervaldays < 2:
127     exit()
128 validcode = ''
129 count = 0
130 ## try to select a class until success
131 while True:
132     print '--------------------------'
133     print 'have tryed to select %d times, now try again!' % (count)
134     count = count + 1
135     try:
136         validcode = openerUrlToString(opener, url5)
137     except:
138         continue
139     url7 = 'http://wsyc.dfss.com.cn/Ajax/StuHdl.ashx?loginType=2&method=stu'\
140            + '&stuid=%s&sfznum=&carid=&ValidCode=%s' % (username, validcode)
141     data = opener.open(url7).read().decode('utf-8')
142     strs = re.search('\[\{\"fchrdate.*?\}\]', data)
143     #print data
144     print strs
145     if strs is None:
146         continue
147     jsontext = json.loads(strs.group())
148     num = jsontext[intervaldays][numid].split('/')[1]
149     print 'remain num is ' + num
150     if num == '0':
151         print 'no class avaliable!'
152         time.sleep(600)
153         continue
154     try:
155         validcode = openerUrlToString(opener, url5)
156     except:
157         continue
158     url6 = 'http://wsyc.dfss.com.cn/Ajax/StuHdl.ashx?loginType=2&method=yueche'\
159            + '&stuid=%s&bmnum=BD14101500687&start=%d&end=%d' % (username, start, end)\
160            + '&lessionid=001&trainpriceid=BD13040300001&lesstypeid=02'\
161            + '&date=%d-%d-%d' % (year, month, day)\
162            + '&id=1&carid=&ycmethod=03&cartypeid=01&trainsessionid=0' + numid\
163            + '&ReleaseCarID=&ValidCode=' + validcode
164     result = opener.open(url6).read().decode('utf-8')
165     print 'result of select is ' + result
166     if result == 'success':
167         print 'select success!'
168         break
169     else:
170         continue
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM