Python爬蟲之如何用代碼識別滑動驗證碼

本文來自小帥b《學習python的正確姿勢》

今天說說Python爬蟲裡面經常遇到的滑動驗證碼。大家應該都很熟悉。點擊滑塊然後移動到圖片缺口進行驗證:


Python爬蟲之如何用代碼識別滑動驗證碼


現在越來越多的網站使用這樣的驗證方式,為的是增加驗證碼識別的難度。


那麼,

對於這種驗證碼,

應該怎麼破呢?

打開 某 站的登錄頁面,


Python爬蟲之如何用代碼識別滑動驗證碼

可以看到登錄的時候需要進行滑塊驗證。


按下 F12,

進入 Network,

看下我們將滑塊移到缺口鬆開之後做了什麼提交:

Python爬蟲之如何用代碼識別滑動驗證碼

可以看到是一個 GET 請求。


但是,

這請求鏈接也太長了吧。


我們來看看請求的參數是怎麼樣的

Python爬蟲之如何用代碼識別滑動驗證碼

  • gt?
  • challenge?
  • w?

這些都是什麼鬼參數?還加密了。


既然以請求的方式不好弄,

我們從它們的源代碼入手,

看看有什麼突破口。


回到 某 站的登錄頁,

按下 F12,

進入 Element,

然後點擊滑塊出現了圖片。


定位一下

Python爬蟲之如何用代碼識別滑動驗證碼

發現有兩個 a 標籤:

  • 一個 class 是 gt_bg gt_show
  • 一個 class 是 gt_fullbg gt_show


  • 一張是完全的背景圖片
  • 一張是缺口的圖片

那把這兩張圖片下載下來對比一下不就行了。


打開 a 標籤一看:

Python爬蟲之如何用代碼識別滑動驗證碼

發現:一張圖片被切割成很多小塊。


原來這張圖片是拼出來的!

我們看看原始圖片是怎麼樣的。

Python爬蟲之如何用代碼識別滑動驗證碼

什麼亂七八糟的。


再仔細看下源代碼,

原來是在同一張圖片通過偏移量合成了一張完整的圖片:

<code>background-position: -277px -58px;/<code>

厲害厲害!


到這裡,

我們的第一個思路就是,

下載這兩張原始圖片,

然後通過偏移量合成兩張真正的圖片。


背景圖:

Python爬蟲之如何用代碼識別滑動驗證碼

合成之後:

Python爬蟲之如何用代碼識別滑動驗證碼

缺口圖:

Python爬蟲之如何用代碼識別滑動驗證碼

合成之後:

Python爬蟲之如何用代碼識別滑動驗證碼

那麼怎麼做呢?


因為我們還要模擬滑動滑塊,

所以呢,

我們要用到 selenium。


打開某站的登錄頁,

然後等到那個滑塊顯示出來:

<code># 獲取滑塊按鈕
driver.get(url)
slider = WAIT.until(EC.element_to_be_clickable(
(By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))/<code>

接下來我們就獲取頁面的源碼:

<code>driver.page_source/<code>

然後使用 bs 獲取兩張原始背景圖片的 url :

<code>bs = BeautifulSoup(driver.page_source,'lxml')
# 找到背景圖片和缺口圖片的div
bg_div = bs.find_all(class_='gt_cut_bg_slice')
fullbg_div = bs.find_all(class_='gt_cut_fullbg_slice')

# 獲取缺口背景圖片url
bg_url = re.findall('background-image:\\surl\\("(.*?)"\\)',bg_div[0].get('style'))
# 獲取背景圖片url
fullbg_url = re.findall('background-image:\\surl\\("(.*?)"\\)',fullbg_div[0].get('style'))/<code>

拿到了圖片地址之後,

將圖片下載下來:

<code> # 將圖片格式存為 jpg 格式
bg_url = bg_url[0].replace('webp', 'jpg')
fullbg_url = fullbg_url[0].replace('webp', 'jpg')
# print(bg_url)
# print(fullbg_url)

# 下載圖片
bg_image = requests.get(bg_url).content

fullbg_image = requests.get(fullbg_url).content
print('完成圖片下載')/<code>


Python爬蟲之如何用代碼識別滑動驗證碼

Python爬蟲之如何用代碼識別滑動驗證碼


我們已經把兩張原始圖片下載下來了,那麼接下來就是要合成圖片了。


我們要根據圖片的位置來合成,

也就是源碼中的 background-position:

Python爬蟲之如何用代碼識別滑動驗證碼

獲取每一個小圖片的位置,

我們可以通過字典的形式來表示這些位置,

然後將數據放到列表中:

<code>    # 存放每個合成缺口背景圖片的位置
bg_location_list = []
# 存放每個合成背景圖片的位置
fullbg_location_list = []

for bg in bg_div:
location = {}
location['x'] = int(re.findall('background-position:\\s(.*?)px\\s(.*?)px;', bg.get('style'))[0][0])
location['y'] = int(re.findall('background-position:\\s(.*?)px\\s(.*?)px;', bg.get('style'))[0][1])
bg_location_list.append(location)

for fullbg in fullbg_div:
location = {}
location['x'] = int(re.findall('background-position:\\s(.*?)px\\s(.*?)px;', fullbg.get('style'))[0][0])
location['y'] = int(re.findall('background-position:\\s(.*?)px\\s(.*?)px;', fullbg.get('style'))[0][1])
fullbg_location_list.append(location)
/<code>

那麼,

現在我們已經有了原始圖片,

還知道了每個位置應該顯示原始圖片的什麼部分。


接下來我們就寫一個方法,

<code>    # 寫入圖片
bg_image_file = BytesIO(bg_image)
fullbg_image_file = BytesIO(fullbg_image)

# 合成圖片

bg_Image = mergy_Image(bg_image_file, bg_location_list)
fullbg_Image = mergy_Image(fullbg_image_file, fullbg_location_list)/<code>

那麼問題又來了,怎麼合成啊?


Python爬蟲之如何用代碼識別滑動驗證碼

這裡圖片被分割成的每一個小圖片的尺寸是

  • 10 * 58


所以我們也要將我們剛剛下載的原始圖片切割成相應的尺寸大小。而且,這張圖片是由上半部分的小圖片和下半部分的小圖片合成的,所以我們定義兩個 list 來裝這些小圖片:

<code>  # 存放上下部分的各個小塊
upper_half_list = []
down_half_list = []/<code>

然後將原始的圖片切割好放進去:

<code>image = Image.open(image_file)

# 通過 y 的位置來判斷是上半部分還是下半部分,然後切割
for location in location_list:
if location['y'] == -58:
# 間距為10,y:58-116
im = image.crop((abs(location['x']), 58, abs(location['x'])+10, 116))
upper_half_list.append(im)
if location['y'] == 0:
# 間距為10,y:0-58
im = image.crop((abs(location['x']), 0, abs(location['x']) + 10, 58))
down_half_list.append(im)/<code>

至此,

我們這兩個 list 就分別放好了各個切割的圖片了。


那麼接下來就創建一張空白的圖片,

然後將小圖片一張一張(間距為10)的粘貼到空白圖片裡,

這樣我們就可以得到一張合成好的圖片了:

<code># 創建一張大小一樣的圖片
new_image = Image.new('RGB', (260, 116))

# 粘貼好上半部分 y座標是從上到下(0-116)
offset = 0
for im in upper_half_list:
new_image.paste(im, (offset, 0))
offset += 10

# 粘貼好下半部分
offset = 0
for im in down_half_list:
new_image.paste(im, (offset, 58))
offset += 10/<code>

那麼到現在,

我們可以得到網頁上顯示的那兩張圖片了。


Python爬蟲之如何用代碼識別滑動驗證碼

Python爬蟲之如何用代碼識別滑動驗證碼

接下來我們就要通過對比這兩張圖,

看看我們要滑動的距離是多遠:

<code>
# 合成圖片
bg_Image = mergy_Image(bg_image_file, bg_location_list)
fullbg_Image = mergy_Image(fullbg_image_file, fullbg_location_list)
# bg_Image.show()
# fullbg_Image.show()

# 計算缺口偏移距離
distance = get_distance(bg_Image, fullbg_Image)
print('得到距離:%s' % str(distance))/<code>

可以通過圖片的 RGB 來計算。


我們設定一個閾值,

如果 r、g、b 大於這個閾值,

我們就返回距離:

<code>def get_distance(bg_Image, fullbg_Image):

#閾值
threshold = 200

print(bg_Image.size[0])
print(bg_Image.size[1])


for i in range(60, bg_Image.size[0]):
for j in range(bg_Image.size[1]):
bg_pix = bg_Image.getpixel((i, j))
fullbg_pix = fullbg_Image.getpixel((i, j))
r = abs(bg_pix[0] - fullbg_pix[0])
g = abs(bg_pix[1] - fullbg_pix[1])
b = abs(bg_pix[2] - fullbg_pix[2])

if r + g + b > threshold:
return i/<code>

我們使用 selenium,

拿到滑塊的元素,

然後根據這個距離拖動到缺口位置不就好了麼。


馬上打開 selenium 的文檔,

看到了這個函數:

Python爬蟲之如何用代碼識別滑動驗證碼

它可以使用左鍵點擊元素,

然後拖動到指定距離,

最後釋放鼠標左鍵。


趕緊試一下:

<code>knob =  WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show"))) 

ActionChains(driver).drag_and_drop_by_offset(knob, distance, 0).perform()/<code>

運行一下試試看吧:


Python爬蟲之如何用代碼識別滑動驗證碼

看來直接拖拽是不行的,這太快了。

試著拖完滑塊讓它睡一下再釋放:

<code>ActionChains(driver).click_and_hold(knob).perform()
ActionChains(driver).move_by_offset(xoffset=distance, yoffset=0.1).perform()
time.sleep(0.5)
ActionChains(driver).release(knob).perform()/<code>

發現拼圖還是被吃了:


我們可以使用它來構造一個運動路徑,

該加速時加速,

該減速的時候減速,

這樣的話就更像人類在滑動滑塊了。

<code>def get_path(distance):
result = []
current = 0
mid = distance * 4 / 5
t = 0.2
v = 0
while current < (distance - 10):
if current < mid:
a = 2
else:
a = -3
v0 = v
v = v0 + a * t
s = v0 * t + 0.5 * a * t * t
current += s
result.append(round(s))
return result/<code>

這次,

我們使用這個軌跡來滑動:

<code>    knob = WAIT.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#gc-box > div > div.gt_slider > div.gt_slider_knob.gt_show")))
result = get_path(distance)
ActionChains(driver).click_and_hold(knob).perform()

for x in result:
ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()

time.sleep(0.5)

ActionChains(driver).release(knob).perform() /<code>

好了,再來運行一下吧:


Python爬蟲之如何用代碼識別滑動驗證碼

完美!

當然,成功率不是 100%,可以多試幾次。


以上就是識別滑動驗證碼的具體過程了,

對於其它大部分的滑動驗證碼,

也是可以使用這招搞定的。


私信發送“滑動”獲取源碼!

私信發送“滑動”獲取源碼!

私信發送“滑動”獲取源碼!


本文來自小帥b《學習python的正確姿勢》


分享到:


相關文章: