测试结果:
整个买票流程可以再快一点,不过为了稳定起见,有些地方等待了一些时间
完整程序,拿去可用
整个程序分了三个模块:购票模块(主体)、验证码识别模块、余票查询模块
购票模块:
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