最近接觸了一些selenium模塊的相關(guān)知識,覺得還挺有意思的,于是決定親自嘗試寫一些爬蟲程序來強(qiáng)化selenium模塊(一定要多嘗試、多動(dòng)手、多總結(jié))。本文主要使用python爬蟲來模擬登錄鐵路12306官網(wǎng)。這兒得吐槽一句,鐵路12306網(wǎng)站的反爬機(jī)制做的還是比較好。
話不多說,下面跟小墨一起來學(xué)習(xí)如何通過爬蟲來實(shí)現(xiàn)鐵路12306的登錄。
一、 驗(yàn)證碼破解
當(dāng)我們輸入賬號和密碼后,在點(diǎn)擊登錄按鈕之前,還需要對驗(yàn)證碼進(jìn)行操作。對驗(yàn)證碼的識別,已經(jīng)有相關(guān)的處理平臺,我們只需要借助第三方平臺即可。
1.注冊并登錄超級鷹賬號:點(diǎn)擊鏈接進(jìn)行注冊https://www.chaojiying.com/user/login/;
2.點(diǎn)擊購買題分,并進(jìn)行充值;
3.點(diǎn)擊軟件id,創(chuàng)建一個(gè)軟件Id(程序中會(huì)用到);
4.下載示例代碼(開發(fā)文檔—>選擇相應(yīng)的語言–>下載示例demo),python示例代碼如下所示:
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
|
class Chaojiying_Client( object ): def __init__( self , username, password, soft_id): self .username = username password = password.encode( 'utf8' ) self .password = md5(password).hexdigest() self .soft_id = soft_id self .base_params = { 'user' : self .username, 'pass2' : self .password, 'softid' : self .soft_id, } self .headers = { 'Connection' : 'Keep-Alive' , 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)' , } def PostPic( self , im, codetype): """ im: 圖片字節(jié) codetype: 題目類型 參考 http://www.chaojiying.com/price.html """ params = { 'codetype' : codetype, } params.update( self .base_params) files = { 'userfile' : ( 'ccc.jpg' , im)} r = requests.post( 'http://upload.chaojiying.net/Upload/Processing.php' , data = params, files = files, headers = self .headers) return r.json() def ReportError( self , im_id): """ im_id:報(bào)錯(cuò)題目的圖片ID """ params = { 'id' : im_id, } params.update( self .base_params) r = requests.post( 'http://upload.chaojiying.net/Upload/ReportError.php' , data = params, headers = self .headers) return r.json() |
二、Selenium功能簡介
Selenium模塊和爬蟲之間的關(guān)聯(lián):
–便捷的獲取網(wǎng)站中的動(dòng)態(tài)加載數(shù)據(jù)
–便捷實(shí)現(xiàn)模擬登錄
Selenium模塊的使用流程:
–環(huán)境安裝:pip install selenium
–下載瀏覽器的驅(qū)動(dòng)程序(谷歌瀏覽器):
–下載路徑:http://chromedriver.storage.googleapis.com/index.html
– 驅(qū)動(dòng)程序和瀏覽器的映射關(guān)系:映射鏈接
–將下載好的驅(qū)動(dòng)程序放在當(dāng)前項(xiàng)目目錄下
Selenium模塊的相關(guān)方法:http://www.jfrwli.cn/article/90512.html
上述內(nèi)容完成后,我們就可以正式進(jìn)入正題了,是不是很期待,那就跟著小墨往下走吧。
三、模擬登錄
1. 進(jìn)入官網(wǎng)
1
2
3
4
5
6
7
|
#創(chuàng)建對象 #executable_path=path:下載好的驅(qū)動(dòng)程序的路徑 bro = webdriver.Chrome(executable_path = 'chromedriver.exe' ) #12306的登錄網(wǎng)址 bro.get( 'https://kyfw.12306.cn/otn/resources/login.html' ) #窗口最大化 bro.maximize_window() |
2、進(jìn)入登錄界面并獲取驗(yàn)證碼
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
|
#save_screenshot就是將當(dāng)前頁面進(jìn)行截圖且保存 bro.save_screenshot( 'aa.png' ) #確定驗(yàn)證碼圖片對應(yīng)的左上角和右下角的坐標(biāo)(裁剪的區(qū)域就確定) code_img_ele = bro.find_element_by_xpath( '//*[@id="J-loginImg"]' ) location = code_img_ele.location # 驗(yàn)證碼圖片左上角的坐標(biāo) x,y #print('location:',location) size = code_img_ele.size #驗(yàn)證碼標(biāo)簽對應(yīng)的長和寬 #print('size:',size) #左上角和右下角坐標(biāo) rangle = ( int (location[ 'x' ]), int (location[ 'y' ]), int (location[ 'x' ] + size[ 'width' ]), int (location[ 'y' ] + size[ 'height' ])) #至此驗(yàn)證碼圖片區(qū)域就確定下來了 i = Image. open ( './aa.png' ) code_img_name = './code.png' #crop根據(jù)指定區(qū)域進(jìn)行圖片裁剪 frame = i.crop(rangle) frame.save(code_img_name) #將驗(yàn)證碼圖片提交給超級鷹進(jìn)行識別 chaojiying = Chaojiying_Client( '########' , '#######' , '#######' ) #用戶賬號>>密碼>>軟件 ID im = open ( 'code.png' , 'rb' ).read() #本地圖片文件路徑 來替換 a.jpg 有時(shí)WIN系統(tǒng)須要// id = chaojiying.PostPic(im, 9004 )[ 'pic_id' ] #截取的驗(yàn)證碼照片以及驗(yàn)證碼的類別代號 result = chaojiying.PostPic(im, 9004 )[ 'pic_str' ] #識別結(jié)果 all_list = [] #要存儲(chǔ)即將被點(diǎn)擊的點(diǎn)的坐標(biāo) [[x1,y1],[x2,y2]] #識別錯(cuò)誤后,會(huì)返回題分,示例代碼并沒有這個(gè),就是想讓你花錢 chaojiying.ReportError( id ) if '|' in result: list_1 = result.split( '|' ) print (list_1) count_1 = len (list_1) for i in range (count_1): xy_list = [] x = int (list_1[i].split( ',' )[ 0 ]) y = int (list_1[i].split( ',' )[ 1 ]) xy_list.append(x) xy_list.append(y) all_list.append(xy_list) else : x = int (result.split( ',' )[ 0 ]) y = int (result.split( ',' )[ 1 ]) xy_list = [] xy_list.append(x) xy_list.append(y) all_list.append(xy_list) #遍歷列表,使用動(dòng)作鏈對每一個(gè)列表元素對應(yīng)的x,y指定的位置進(jìn)行點(diǎn)擊操作 for l in all_list: x = l[ 0 ] y = l[ 1 ] ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform() time.sleep( 0.5 ) |
這樣我們就實(shí)現(xiàn)了驗(yàn)證碼的識別操作。
3、輸入賬號和密碼,并點(diǎn)擊登錄按鈕
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#輸入賬號和密碼 put1 = bro.find_element_by_id( 'J-userName' ) #當(dāng)驗(yàn)證碼識別錯(cuò)誤后,需要清空賬號重新輸入 put1.clear() #輸入賬號 put1.send_keys( '########' ) time.sleep( 1 ) put2 = bro.find_element_by_id( 'J-password' ) put2.clear() #輸入密碼 put2.send_keys( '##########' ) time.sleep( 1 ) #點(diǎn)擊登錄按鈕 bro.find_element_by_id( 'J-login' ).click() |
點(diǎn)擊登錄按鈕后,會(huì)出現(xiàn)如下圖所示的彈框
因此,我們需要定位到該提示框,并實(shí)現(xiàn)滑塊的向右滑動(dòng)
4、滑塊滑動(dòng)
1
2
3
4
5
6
7
|
#處理提示框 time.sleep( 0.5 ) span = bro.find_element_by_xpath( '//*[@id="nc_1_n1z"]' ) action = ActionChains(bro) #點(diǎn)擊長按指定的標(biāo)簽 action.click_and_hold(span).perform() action.drag_and_drop_by_offset(span, 400 , 0 ).perform() |
有的時(shí)候,當(dāng)滑塊移動(dòng)后,會(huì)出現(xiàn)如下圖所示的情況:
因此,我們需要點(diǎn)擊刷新,并重新進(jìn)行滑塊的移動(dòng),所以對代碼做稍微的改動(dòng):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
while True : try : info = bro.find_element_by_xpath( '//*[@id="J-slide-passcode"]/div/span' ).text print (info) if info = = '哎呀,出錯(cuò)了,點(diǎn)擊刷新再來一次' : #點(diǎn)擊刷新 bro.find_element_by_xpath( '//*[@id="J-slide-passcode"]/div/span/a' ).click() time.sleep( 0.2 ) #重新移動(dòng)滑塊 span = bro.find_element_by_xpath( '//*[@id="nc_1_n1z"]' ) action = ActionChains(bro) # 點(diǎn)擊長按指定的標(biāo)簽 action.click_and_hold(span).perform() action.drag_and_drop_by_offset(span, 400 , 0 ).perform() time.sleep( 7 ) except : print ( 'ok!' ) break |
至此,我們便實(shí)現(xiàn)了鐵路12306的登錄,如下圖所示
是不是覺得很簡單啊。
5、完整代碼
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# -*- coding: utf-8 -*- #驗(yàn)證碼識別示例 import requests from hashlib import md5 class Chaojiying_Client( object ): def __init__( self , username, password, soft_id): self .username = username password = password.encode( 'utf8' ) self .password = md5(password).hexdigest() self .soft_id = soft_id self .base_params = { 'user' : self .username, 'pass2' : self .password, 'softid' : self .soft_id, } self .headers = { 'Connection' : 'Keep-Alive' , 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)' , } def PostPic( self , im, codetype): """ im: 圖片字節(jié) codetype: 題目類型 參考 http://www.chaojiying.com/price.html """ params = { 'codetype' : codetype, } params.update( self .base_params) files = { 'userfile' : ( 'ccc.jpg' , im)} r = requests.post( 'http://upload.chaojiying.net/Upload/Processing.php' , data = params, files = files, headers = self .headers) return r.json() def ReportError( self , im_id): """ im_id:報(bào)錯(cuò)題目的圖片ID """ params = { 'id' : im_id, } params.update( self .base_params) r = requests.post( 'http://upload.chaojiying.net/Upload/ReportError.php' , data = params, headers = self .headers) return r.json() #使用selenium打開登錄頁面 from selenium import webdriver import time from PIL import Image from selenium.webdriver import ActionChains from selenium.webdriver.support import expected_conditions as EC, wait #創(chuàng)建對象 #executable_path=path:下載好的驅(qū)動(dòng)程序的路徑 bro = webdriver.Chrome(executable_path = 'chromedriver.exe' ) #12306的登錄網(wǎng)址 bro.get( 'https://kyfw.12306.cn/otn/resources/login.html' ) #窗口最大化 bro.maximize_window() #點(diǎn)擊賬號登錄 bro.find_element_by_xpath( '/html/body/div[2]/div[2]/ul/li[2]/a' ).click() time.sleep( 1 ) while True : try : #save_screenshot就是將當(dāng)前頁面進(jìn)行截圖且保存 bro.save_screenshot( 'aa.png' ) #確定驗(yàn)證碼圖片對應(yīng)的左上角和右下角的坐標(biāo)(裁剪的區(qū)域就確定) code_img_ele = bro.find_element_by_xpath( '//*[@id="J-loginImg"]' ) location = code_img_ele.location # 驗(yàn)證碼圖片左上角的坐標(biāo) x,y #print('location:',location) size = code_img_ele.size #驗(yàn)證碼標(biāo)簽對應(yīng)的長和寬 #print('size:',size) #左上角和右下角坐標(biāo) rangle = ( int (location[ 'x' ]), int (location[ 'y' ]), int (location[ 'x' ] + size[ 'width' ]), int (location[ 'y' ] + size[ 'height' ])) #至此驗(yàn)證碼圖片區(qū)域就確定下來了 i = Image. open ( './aa.png' ) code_img_name = './code.png' #crop根據(jù)指定區(qū)域進(jìn)行圖片裁剪 frame = i.crop(rangle) frame.save(code_img_name) #將驗(yàn)證碼圖片提交給超級鷹進(jìn)行識別 chaojiying = Chaojiying_Client( '#####' , '#######' , '######' ) #用戶賬號>>密碼>>軟件 ID im = open ( 'code.png' , 'rb' ).read() #本地圖片文件路徑 來替換 a.jpg 有時(shí)WIN系統(tǒng)須要// id = chaojiying.PostPic(im, 9004 )[ 'pic_id' ] #截取的驗(yàn)證碼照片以及驗(yàn)證碼的類別代號 result = chaojiying.PostPic(im, 9004 )[ 'pic_str' ] #識別結(jié)果 all_list = [] #要存儲(chǔ)即將被點(diǎn)擊的點(diǎn)的坐標(biāo) [[x1,y1],[x2,y2]] #識別錯(cuò)誤后,會(huì)返回題分,官網(wǎng)給的demo并沒有這一句,哈哈哈,坑吧,就是讓你多花錢 chaojiying.ReportError( id ) if '|' in result: list_1 = result.split( '|' ) print (list_1) count_1 = len (list_1) for i in range (count_1): xy_list = [] x = int (list_1[i].split( ',' )[ 0 ]) y = int (list_1[i].split( ',' )[ 1 ]) xy_list.append(x) xy_list.append(y) all_list.append(xy_list) else : x = int (result.split( ',' )[ 0 ]) y = int (result.split( ',' )[ 1 ]) xy_list = [] xy_list.append(x) xy_list.append(y) all_list.append(xy_list) #遍歷列表,使用動(dòng)作鏈對每一個(gè)列表元素對應(yīng)的x,y指定的位置進(jìn)行點(diǎn)擊操作 for l in all_list: x = l[ 0 ] y = l[ 1 ] ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform() time.sleep( 0.5 ) #輸入賬號和密碼 put1 = bro.find_element_by_id( 'J-userName' ) #當(dāng)驗(yàn)證碼識別錯(cuò)誤后,需要清空賬號重新輸入 put1.clear() put1.send_keys( 'username' ) #你的賬號 time.sleep( 1 ) put2 = bro.find_element_by_id( 'J-password' ) put2.clear() put2.send_keys( 'password' ) #你的密碼 time.sleep( 1 ) bro.find_element_by_id( 'J-login' ).click() #處理提示框 time.sleep( 3 ) span = bro.find_element_by_xpath( '//*[@id="nc_1_n1z"]' ) action = ActionChains(bro) #點(diǎn)擊長按指定的標(biāo)簽 action.click_and_hold(span).perform() action.drag_and_drop_by_offset(span, 400 , 0 ).perform() time.sleep( 8 ) while True : try : info = bro.find_element_by_xpath( '//*[@id="J-slide-passcode"]/div/span' ).text print (info) if info = = '哎呀,出錯(cuò)了,點(diǎn)擊刷新再來一次' : bro.find_element_by_xpath( '//*[@id="J-slide-passcode"]/div/span/a' ).click() time.sleep( 0.2 ) span = bro.find_element_by_xpath( '//*[@id="nc_1_n1z"]' ) action = ActionChains(bro) # 點(diǎn)擊長按指定的標(biāo)簽 action.click_and_hold(span).perform() action.drag_and_drop_by_offset(span, 400 , 0 ).perform() time.sleep( 7 ) except : print ( 'ok!' ) break #釋放動(dòng)作鏈 action.release() break except : time.sleep( 3 ) time.sleep( 12 ) #登錄成功 bro.find_element_by_link_text( '確定' ).click() time.sleep( 0.5 ) bro.find_element_by_link_text( '首頁' ).click() #輸入起點(diǎn)、終點(diǎn)以及時(shí)間,查詢車票 start_city = '北京' end_city = '上海' date = '2020-08-05' #選擇起點(diǎn) bro.find_element_by_xpath( '//*[@id="fromStationText"]' ).click() time.sleep( 2 ) #這只遍歷了熱門城市,要是想遍歷其他城市,自己寫一個(gè)循環(huán)就行 city_list = bro.find_elements_by_xpath( '//*[@id="ul_list1"]/li' ) for city in city_list: if city.text = = start_city: city.click() break time.sleep( 2 ) #選擇終點(diǎn) bro.find_element_by_xpath( '//*[@id="toStationText"]' ).click() for city in city_list: if city.text = = end_city: city.click() break time.sleep( 2 ) js = "$('input[id=train_date]').removeAttr('readonly')" bro.execute_script(js) dt = bro.find_element_by_id( 'train_date' ) dt.clear() dt.send_keys(date) time.sleep( 2 ) bro.find_element_by_xpath( '/html/body/div[3]/div[2]/div/div[1]/div/div[1]/ul/li[1]/a' ).click() time.sleep( 0.5 ) bro.find_element_by_xpath( '//*[@id="isStudentDan"]/i' ).click() time.sleep( 2 ) bro.find_element_by_id( 'search_one' ).click() time.sleep( 2 ) |
到此這篇關(guān)于Selenium之模擬登錄鐵路12306的示例代碼的文章就介紹到這了,更多相關(guān)Selenium 模擬登錄12306內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/wangbiao9982019/article/details/107634479