測(cè)試結(jié)果:
整個(gè)買票流程可以再快一點(diǎn),不過為了穩(wěn)定起見,有些地方等待了一些時(shí)間
完整程序,拿去可用
整個(gè)程序分了三個(gè)模塊:購票模塊(主體)、驗(yàn)證碼識(shí)別模塊、余票查詢模塊
購票模塊:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, ElementNotVisibleException import time import requests from urllib.parse import urlencode from pyquery import PyQuery as pq from check_ticket import Check from verify import Code import json class Buy_Ticket(): def __init__( self , start_station, end_station, date, username, password, purpose): self .num = 1 self .start = start_station self .end = end_station self .date = date self .username = username self .password = password self .purpose = purpose self .login_url = 'https://kyfw.12306.cn/otn/login/init' self .ticket_url = 'https://kyfw.12306.cn/otn/leftTicket/init' def login( self ): browser.get( self .login_url) try : input_name = browser.find_element_by_id( 'username' ) input_pd = browser.find_element_by_id( 'password' ) button = browser.find_element_by_id( 'loginSub' ) time.sleep( 1 ) input_name.send_keys( self .username) input_pd.send_keys( self .password) c = Code(browser) #調(diào)用驗(yàn)證碼識(shí)別模塊 c.main() button.click() time.sleep( 2 ) #等待頁面跳轉(zhuǎn),如果驗(yàn)證碼識(shí)別錯(cuò)誤,就執(zhí)行下面的while語句 while browser.current_url = = self .login_url + '#' : c = Code(browser) c.main() button.click() time.sleep( 2 ) #self.get_passenger() self .check() except NoSuchElementException: self .login() def check( self ): #調(diào)用余票查詢模塊 check = Check( self .date, self .start, self .end, self .purpose) start_end = check.look_up_station() self .num = check.get_info() #cookie的添加,json.dumps把以漢字形式呈現(xiàn)的起始、終點(diǎn)站轉(zhuǎn)化成unicode編碼,可在審查元素里查看cookie browser.add_cookie({ 'name' : '_jc_save_fromStation' , 'value' :json.dumps( self .start).strip( '"').replace('\\', '%') + '%2C' + start_end[0]}) browser.add_cookie({'name':'_jc_save_toStation', 'value':json.dumps(self.end).strip('"' ).replace( '\\', ' % ') + ' % 2C ' + start_end[ 1 ]}) browser.add_cookie({ 'name' : '_jc_save_fromDate' , 'value' : self .date}) browser.get( self .ticket_url) if self .purpose = = '學(xué)生' : btn = browser.find_element_by_id( 'sf2' ) time.sleep( 1 ) btn.click() button = browser.find_element_by_id( 'query_ticket' ) time.sleep( 1 ) button.click() def book_ticket( self ): print ( '開始預(yù)訂車票...' ) #先查找出所有車次對(duì)應(yīng)的預(yù)訂按鈕,再根據(jù)余票查詢模塊返回的車次序號(hào),點(diǎn)擊相應(yīng)的預(yù)訂按鈕 button = browser.find_elements_by_class_name( 'btn72' ) button[ self .num - 1 ].click() time.sleep( 3 ) button2 = browser.find_element_by_id( 'normalPassenger_0' ) #按實(shí)際情況,可自行修改,這里就選擇的第一個(gè)常用聯(lián)系人, #第二個(gè)是normalPassenger_1,依此類推 button2.click() button3 = browser.find_element_by_id( 'submitOrder_id' ) time.sleep( 1 ) button3.click() time.sleep( 3 ) #等待頁面加載完畢,不然后面可能會(huì)報(bào)錯(cuò),等待時(shí)間自行決定 try : button4 = browser.find_element_by_id( 'qr_submit_id' ) button4.click() except ElementNotVisibleException: button4 = browser.find_element_by_id( 'qr_submit_id' ) button4.click() print ( '車票預(yù)定成功!請(qǐng)?jiān)?0分鐘內(nèi)完成付款!' ) def main( self ): self .login() self .book_ticket() if __name__ = = '__main__' : begin = time.time() browser = webdriver.Chrome() b = Buy_Ticket( '上海' , '重慶' , '2018-09-18' , '賬號(hào)' , '密碼' , 'ADULT' ) #賬號(hào)、密碼自行修改 b.main() end = time.time() print ( '總耗時(shí):%d秒' % int (end - begin)) #browser.close() |
驗(yàn)證碼識(shí)別模塊:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
import requests from PIL import Image from selenium.webdriver import ActionChains import time from io import BytesIO class Code(): def __init__( self , browser): self .browser = browser self .verify_url = 'http://littlebigluo.qicp.net:47720/' #驗(yàn)證碼識(shí)別網(wǎng)址,返回識(shí)別結(jié)果 #確定驗(yàn)證碼的位置 def get_position( self ): time.sleep( 3 ) element = self .browser.find_element_by_class_name( 'touclick-img-par' ) time.sleep( 2 ) location = element.location size = element.size position = (location[ 'x' ], location[ 'y' ], location[ 'x' ] + size[ 'width' ], location[ 'y' ] + size[ 'height' ]) return position #截取整個(gè)網(wǎng)頁頁面 def get_screenshot( self ): screenshot = self .browser.get_screenshot_as_png() screenshot = Image. open (BytesIO(screenshot)) return screenshot #從截取的網(wǎng)頁,裁剪出驗(yàn)證碼圖片,并保存到本地 def get_touclick_img( self , name = 'captcha.png' ): position = self .get_position() print ( '驗(yàn)證碼的位置:' , position) screenshot = self .get_screenshot() captcha = screenshot.crop(position) captcha.save( 'captcha.png' ) #驗(yàn)證碼解析 def parse_img( self ): files = { 'file' : open ( 'captcha.png' , 'rb' )} #打開保存到本地的驗(yàn)證碼圖片 response = requests.post( self .verify_url, files = files) num = response.text.split( '<B>' )[ 1 ].split( '<' )[ 0 ] print ( '驗(yàn)證碼識(shí)別成功!圖片位置:%s' % num) try : if int (num): return [ int (num)] except ValueError: num = list ( map ( int ,num.split())) return num #識(shí)別結(jié)果num都以列表形式返回,方便后續(xù)驗(yàn)證碼的點(diǎn)擊 #實(shí)現(xiàn)驗(yàn)證碼自動(dòng)點(diǎn)擊 def move( self ): num = self .parse_img() try : element = self .browser.find_element_by_class_name( 'touclick-img-par' ) for i in num: if i < = 4 : ActionChains( self .browser).move_to_element_with_offset(element, 40 + 72 * (i - 1 ), 73 ).click().perform() else : i - = 4 ActionChains( self .browser).move_to_element_with_offset(element, 40 + 72 * (i - 1 ), 145 ).click().perform() except : print ( '元素不可選!' ) def main( self ): self .get_touclick_img() self .move() |
余票查詢模塊:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
import requests from urllib.parse import urlencode class Check(): def __init__( self , date, start, end, purpose): self .base_url = 'https://kyfw.12306.cn/otn/leftTicket/queryA?' self .url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9018' self .date = date self .start_station = start self .end_station = end if purpose = = '學(xué)生' : self .purpose = '0X00' else : self .purpose = purpose #查找出車站的英文簡(jiǎn)稱,用于構(gòu)造cookie、完整的余票查詢鏈接 def look_up_station( self ): response1 = requests.get( self .url) a = response1.text.split( '@' ) a.pop( 0 ) for each in a: i = each.split( '|' ) if self .start_station = = i[ 1 ]: self .start_station = i[ 2 ] elif self .end_station = = i[ 1 ]: self .end_station = i[ 2 ] return [ self .start_station, self .end_station] def get_info( self ): start_end = self .look_up_station() #構(gòu)造請(qǐng)求參數(shù) data = { 'leftTicketDTO.train_date' : self .date, 'leftTicketDTO.from_station' :start_end[ 0 ], 'leftTicketDTO.to_station' :start_end[ 1 ], 'purpose_codes' : self .purpose } url = self .base_url + urlencode(data) response = requests.get(url) json = response.json() maps = json[ 'data' ][ 'map' ] count = 0 #用于對(duì)車次編號(hào) for each in json[ 'data' ][ 'result' ]: count + = 1 s = each.split( '|' )[ 3 :] info = { 'train' :s[ 0 ], 'start_end' :maps[s[ 3 ]] + '-' + maps[s[ 4 ]], 'time' :s[ 5 ] + '-' + s[ 6 ], '歷時(shí)' :s[ 7 ], '一等座' :s[ - 5 ], '二等座' :s[ - 6 ] } try : #余票的結(jié)果有3種:有、一個(gè)具體的數(shù)字(如:18、6等)、無,判斷如果余票是有或者一個(gè)具體的數(shù)字就直接輸出對(duì)應(yīng)的車次信息,然后返回 if info[ '二等座' ] = = '有' or int (info[ '二等座' ]): print ( '[%d]' % count, info) return count except ValueError: continue |
總結(jié)
以上所述是小編給大家介紹的Python + selenium + requests實(shí)現(xiàn)12306全自動(dòng)搶票及驗(yàn)證碼破解加自動(dòng)點(diǎn)擊功能,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.cnblogs.com/mumengyun/p/10001109.html