测试结果:
整个买票流程可以再快一点,不过为了稳定起见,有些地方等待了一些时间
完整程序,拿去可用
整个程序分了三个模块:购票模块(主体)、验证码识别模块、余票查询模块
购票模块:
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) #调用验证码识别模块 c.main() button.click() time.sleep( 2 ) #等待页面跳转,如果验证码识别错误,就执行下面的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 ): #调用余票查询模块 check = Check( self .date, self .start, self .end, self .purpose) start_end = check.look_up_station() self .num = check.get_info() #cookie的添加,json.dumps把以汉字形式呈现的起始、终点站转化成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 = = '学生' : 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 ( '开始预订车票...' ) #先查找出所有车次对应的预订按钮,再根据余票查询模块返回的车次序号,点击相应的预订按钮 button = browser.find_elements_by_class_name( 'btn72' ) button[ self .num - 1 ].click() time.sleep( 3 ) button2 = browser.find_element_by_id( 'normalPassenger_0' ) #按实际情况,可自行修改,这里就选择的第一个常用联系人, #第二个是normalPassenger_1,依此类推 button2.click() button3 = browser.find_element_by_id( 'submitOrder_id' ) time.sleep( 1 ) button3.click() time.sleep( 3 ) #等待页面加载完毕,不然后面可能会报错,等待时间自行决定 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 ( '车票预定成功!请在30分钟内完成付款!' ) def main( self ): self .login() self .book_ticket() if __name__ = = '__main__' : begin = time.time() browser = webdriver.Chrome() b = Buy_Ticket( '上海' , '重庆' , '2018-09-18' , '账号' , '密码' , 'ADULT' ) #账号、密码自行修改 b.main() end = time.time() print ( '总耗时:%d秒' % int (end - begin)) #browser.close() |
验证码识别模块:
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/' #验证码识别网址,返回识别结果 #确定验证码的位置 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 #截取整个网页页面 def get_screenshot( self ): screenshot = self .browser.get_screenshot_as_png() screenshot = Image. open (BytesIO(screenshot)) return screenshot #从截取的网页,裁剪出验证码图片,并保存到本地 def get_touclick_img( self , name = 'captcha.png' ): position = self .get_position() print ( '验证码的位置:' , position) screenshot = self .get_screenshot() captcha = screenshot.crop(position) captcha.save( 'captcha.png' ) #验证码解析 def parse_img( self ): files = { 'file' : open ( 'captcha.png' , 'rb' )} #打开保存到本地的验证码图片 response = requests.post( self .verify_url, files = files) num = response.text.split( '<B>' )[ 1 ].split( '<' )[ 0 ] print ( '验证码识别成功!图片位置:%s' % num) try : if int (num): return [ int (num)] except ValueError: num = list ( map ( int ,num.split())) return num #识别结果num都以列表形式返回,方便后续验证码的点击 #实现验证码自动点击 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 = = '学生' : self .purpose = '0X00' else : self .purpose = purpose #查找出车站的英文简称,用于构造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() #构造请求参数 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 #用于对车次编号 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 ], '历时' :s[ 7 ], '一等座' :s[ - 5 ], '二等座' :s[ - 6 ] } try : #余票的结果有3种:有、一个具体的数字(如:18、6等)、无,判断如果余票是有或者一个具体的数字就直接输出对应的车次信息,然后返回 if info[ '二等座' ] = = '有' or int (info[ '二等座' ]): print ( '[%d]' % count, info) return count except ValueError: continue |
总结
以上所述是小编给大家介绍的Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:http://www.cnblogs.com/mumengyun/p/10001109.html