爬虫学习笔记(十九)—— 滑动验证码
文章目錄
- 一、概念
- 二、實(shí)現(xiàn)步驟
- 2.1、獲取驗(yàn)證碼圖片
- 2.1.1、獲取缺口圖
- 2.1.2、獲取滑塊圖
- 2.1.3、獲取完整圖
- 2.1.4、完整代碼
- 2.2、計(jì)算缺口位置
- 2.3、模擬人工移動(dòng)
- 2.3.1、直接根據(jù)距離移動(dòng)
- 2.3.2、牛頓運(yùn)動(dòng)定律模擬人工移動(dòng)
- 2.4、selenium 移動(dòng)滑塊
一、概念
滑動(dòng)驗(yàn)證碼也叫行為驗(yàn)證碼,比較流行的一種驗(yàn)證碼,通過用戶的操作行為來(lái)完成驗(yàn)證,其中最出名的就是極驗(yàn)。滑動(dòng)驗(yàn)證碼的原理就是使用機(jī)器學(xué)習(xí)中的深度學(xué)習(xí)技術(shù),根據(jù)一些特征來(lái)區(qū)分是否為正常用戶。通過記錄用戶的滑動(dòng)速度,還有每一小段時(shí)間的瞬時(shí)速度,用戶鼠標(biāo)點(diǎn)擊情況,以及滑動(dòng)后的匹配程度來(lái)識(shí)別。而且,不是說(shuō)滑動(dòng)到正確位置就是驗(yàn)證通過,而是根據(jù)特征識(shí)別來(lái)區(qū)分是否為真用戶,滑到正確位置只是一個(gè)必要條件。
二、實(shí)現(xiàn)步驟
通過代碼來(lái)模擬驗(yàn)證碼滑動(dòng)主要有4個(gè)步驟:
(練習(xí)網(wǎng)址:https://captcha1.scrape.center/)
2.1、獲取驗(yàn)證碼圖片
首先通過“網(wǎng)頁(yè)調(diào)試工具“觀察 缺口圖,滑塊圖,完整圖 在源碼中的位置
三個(gè)canvas標(biāo)簽分別對(duì)應(yīng)了缺口圖,滑塊圖和完整圖。接下來(lái),我們要通過修改頁(yè)面樣式來(lái)獲取這三個(gè)圖:
注意: 獲取完圖片記得還原,避免在接下來(lái)的操作中出錯(cuò)
2.1.1、獲取缺口圖
通過修改display屬性,隱藏滑塊,獲取缺口圖
代碼:
js_hide_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="none"' driver.execute_script(js_hide_slice) #截取缺口圖 gap_imgpath = './gap.png' driver.find_element_by_class_name('geetest_canvas_bg').screenshot(gap_imgpath)2.1.2、獲取滑塊圖
通過修改display屬性,隱藏缺口圖,顯示滑塊圖,截取滑塊圖
代碼:
js_hide_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="none"' js_show_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="block"' driver.execute_script(js_hide_gap+';'+js_show_slice) #截取滑塊圖 slice_imgpath='./slice.png' driver.find_element_by_class_name('geetest_canvas_slice').screenshot(slice_imgpath)2.1.3、獲取完整圖
通過修改display屬性,獲取完整圖
代碼:
js_show_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="block"' driver.execute_script(js_show_full) full_imgpath = './full.png' driver.find_element_by_class_name('geetest_canvas_fullbg').screenshot(full_imgpath)2.1.4、完整代碼
def get_captcha_img():"獲取驗(yàn)證碼圖片"#1、隱藏滑塊 獲取缺口圖time.sleep(3)js_hide_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="none"'driver.execute_script(js_hide_slice)#獲取缺口圖gap_imgpath = './gap.png'driver.find_element_by_class_name('geetest_canvas_bg').screenshot(gap_imgpath)# 2、隱藏缺口圖 獲取滑塊圖js_hide_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="none"'js_show_slice = 'document.getElementsByClassName("geetest_canvas_slice")[0].style.display="block"'driver.execute_script(js_hide_gap+';'+js_show_slice)#獲取滑塊圖slice_imgpath='./slice.png'driver.find_element_by_class_name('geetest_canvas_slice').screenshot(slice_imgpath)# 3、獲取完整圖 js_show_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="block"'driver.execute_script(js_show_full)full_imgpath = './full.png'driver.find_element_by_class_name('geetest_canvas_fullbg').screenshot(full_imgpath)# 4、還原驗(yàn)證碼圖形js_hide_full = 'document.getElementsByClassName("geetest_canvas_fullbg")[0].style.display="none"'js_show_gap = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="block"'driver.execute_script(js_hide_full+';'+js_show_gap)return gap_imgpath,slice_imgpath,full_imgpath2.2、計(jì)算缺口位置
通過計(jì)算缺口位置與滑塊位置的差值,獲得滑塊要移動(dòng)的距離
代碼:
def get_gapX(gap_imgpath,full_imgpath):"獲取缺口的位置,缺口左邊緣X坐標(biāo) "gap_img = Image.open(gap_imgpath)full_img = Image.open(full_imgpath)w,h = gap_img.sizefor x in range(w):for y in range(h):# 彩色圖片,每一個(gè)點(diǎn)是一個(gè)元組gap_rgb = gap_img.getpixel((x,y))full_rgb = full_img.getpixel((x,y))#比較缺口圖與完整圖,相同的坐標(biāo)上的點(diǎn),求顏色差值#相同的顏色通道相減之后的絕對(duì)值相加;非缺口的地方差別小,缺口大的色差大r = gap_rgb[0]-full_rgb[0]g = gap_rgb[1]-full_rgb[1]b = gap_rgb[2]-full_rgb[2]value = abs(r)+abs(g)+abs(b)print((x,y),value)if value>120:return xdef get_sliceX(slice_imgpath):"獲取滑塊的位置,滑塊左邊緣X坐標(biāo)"slice_img = Image.open(slice_imgpath)w,h = slice_img.size#滑塊為彩色,其余地方為白色for x in range(w):for y in range(h):rgb = slice_img.getpixel((x,y))value = rgb[0] + rgb[1] + rgb[2] #740# print((x,y),value)if value < 570:return xdef get_distance(gap_imgpath, slice_imgpath, full_imgpath):"計(jì)算距離"gapX = get_gapX(gap_imgpath,full_imgpath)sliceX = get_sliceX(slice_imgpath)return abs(gapX - sliceX)2.3、模擬人工移動(dòng)
通過算法模擬人工移動(dòng)的軌跡:
- 1、直接根據(jù)獲取到的距離移動(dòng),滑塊與缺口能夠契合,但是不能通過
- 2、牛頓運(yùn)動(dòng)定律模擬人工移動(dòng),偶爾有通過,概率低;
- 3、增加隨機(jī)性模擬人工移動(dòng)軌跡,通過概率高
2.3.1、直接根據(jù)距離移動(dòng)
def move_slice1(distance):"1、直接根據(jù)距離移動(dòng)"elem = driver.find_element_by_class_name('geetest_slider_button')ActionChains(driver).click_and_hold(elem).perform()ActionChains(driver).move_by_offset(xoffset=distance,yoffset=0).perform()ActionChains(driver).release(elem).perform()2.3.2、牛頓運(yùn)動(dòng)定律模擬人工移動(dòng)
拖動(dòng)時(shí)的速度要平滑變化,開始變快,后來(lái)變慢,最后可能拖過頭然后回拖,則速度可能為負(fù),拋物線只是一種方案,軌跡方案體現(xiàn)拖動(dòng)滑塊時(shí)速度變化的理解。
這里要稍微用一點(diǎn)高中的物理知識(shí):
代碼實(shí)現(xiàn):
def get_track(distance):'''獲得移動(dòng)軌跡,模仿人的滑動(dòng)行為,先勻加速后勻減速勻變速運(yùn)動(dòng)基本公式:①v=v0+at②s=v0t+0.5at^2:param distance: 需要移動(dòng)的距離:return: 每0.2s移動(dòng)的距離'''#初速度v0 = 0#單位時(shí)間0.2st = 0.2#軌跡列表,每個(gè)元素代表0.2s的位移tracks = []#當(dāng)前的位移current = 0#達(dá)到mid開始減速mid = distance*5/8#先滑過一點(diǎn),最后再反著滑動(dòng)回來(lái)distance+=10while current<=distance:# 增加運(yùn)動(dòng)隨機(jī)性t = random.randint(1,4)/10if current<mid:#加速度越小,單位時(shí)間的位移越小,模擬的軌跡就越多越詳細(xì)a = random.randint(2,7) #加速運(yùn)動(dòng)else:a = -random.randint(2,6) #減速運(yùn)動(dòng)#0.2s時(shí)間的位移s = v0*t + 0.5*a*(t**2)#當(dāng)前位置current+=s#添加到軌跡列表tracks.append(round(s))#當(dāng)前速度,作為下次的初速度v0 = v0+a*t#反著滑動(dòng)到大概準(zhǔn)確位置 for i in range(4):tracks.append(-random.randint(1,3))return tracks2.4、selenium 移動(dòng)滑塊
def move_slice1(distance):"1、直接根據(jù)距離移動(dòng)"elem = driver.find_element_by_class_name('geetest_slider_button')ActionChains(driver).click_and_hold(elem).perform()ActionChains(driver).move_by_offset(xoffset=distance,yoffset=0).perform()ActionChains(driver).release(elem).perform()def move_slice2(distance):"2、牛頓運(yùn)動(dòng)定律模擬人工移動(dòng)"elem = driver.find_element_by_class_name('geetest_slider_button')ActionChains(driver).click_and_hold(elem).perform()tracks = get_track(distance)for track in tracks:ActionChains(driver).move_by_offset(xoffset=track,yoffset=0).perform()ActionChains(driver).release(elem).perform()移動(dòng)卡頓問題
代碼在運(yùn)行的過程中可能會(huì)出現(xiàn)卡頓的問題,解決方法是修改pointer_input.py文件中的DEFAULT_MOVE_DURATION的值,把這個(gè)值改小一點(diǎn),這個(gè)值的主要作用是控制花費(fèi)多少毫秒來(lái)完成移動(dòng)鼠標(biāo)的動(dòng)作。
python文件下 Lib\site-packages\selenium\webdriver\common\actions\pointer_input.py
總結(jié)
以上是生活随笔為你收集整理的爬虫学习笔记(十九)—— 滑动验证码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 爬虫学习笔记(十七)—— 字符验证码
- 下一篇: 爬虫学习笔记(二十)—— 字体反爬