Python爬蟲入門教程 58-100 python爬蟲高級技術之驗證碼篇4-極驗證識別技術之一
目錄
- 驗證碼類型
- 官網最新效果
- 找個用極驗證的網站
- 拼接驗證碼圖片
- 編寫自動化代碼
- 核心run方法
- 模擬拖動方法
- 圖片處理方法
- 初步運行結果
- 拼接圖
- 圖片存儲到本地
@
驗證碼類型
今天要搞定的驗證碼屬於現在使用非常多的驗證碼的一種類型---極驗證滑動驗證碼,關於這個驗證碼的詳細說明查閱他的官網,https://www.geetest.com/ 把驗證碼做到這個地步,必須點贊了。
官網最新效果
官方DEMO最新的效果如下,按照驗證碼的更新頻率,基本博客看完,驗證碼也更新了,不過套路依舊是相同的,反爬只能增加爬蟲編寫的成本,並不能完全杜絕爬蟲。
這類驗證碼,常規解決辦法,模擬人為操作,圖像比對,查找缺口,移動覆蓋缺口。
找個用極驗證的網站
今天看新聞,隨意找了一下,虎嗅使用的是直接拖拽,沒有用最新的點擊+拖拽方式,可以直接看一下如何操作。
這種驗證碼除了打碼平臺以外,直接selenium搞起
拼接驗證碼圖片
當你在谷歌瀏覽器使用F12進行查找元素的時候,隨意的去缺口圖片上面點擊一下,在控制臺DOM結構中出現如下代碼,有前端經驗的童鞋知道,這個使用的是背景局部顯示技術,是可以通過這個拼接成一個。
註意兩個地方:
- https://static.geetest.com/pictures/gt/8bc4cb7fa/8bc4cb7fa.webp 圖片地址
- background-position:後面的坐標
查閱圖片之後,發現是一張碎掉的圖片,你要做的第一步是將這個圖片進行還原,我們通過selenium進行實現。這個地方需要先備註一下圖片的尺寸,後面用size =312x116
編寫自動化代碼
使用selenium執行的操作,模擬人的點擊行為即可
最初,我們導入一些selenium的基本模塊與方法
import time import re from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.action_chains import ActionChains
基本模塊的作用如下
webdriver 核心驅動
selenium.common.exceptions 異常類 TimeoutException 超時異常
selenium.webdriver.common.by 按照什麽方式進行元素的查找 例如 By.ID,By.ClassName,By.XPATH
selenium.webdriver.support.wait 等待頁面加載某些元素
from selenium.webdriver.support import expected_conditions 場景判斷用的,一般和上面的等待加載元素一起使用
selenium.webdriver.common.action_chains 鼠標執行的動作鏈
主方法測試入口
if __name__ == '__main__':
h = Geek_Huxiu()
h.run()
構造方法,實現對部分參數的初始化操作
def __init__(self):
self.driver = webdriver.Chrome()
self.driver.set_window_size(1366,768)
webdriver.Chrome() 啟動谷歌瀏覽器,這個地方需要你提前配置好chromedriver.exe
set_window_size(1366,768) 初始化瀏覽器大小
核心run方法
def run(self):
self.driver.get("https://www.huxiu.com/") # 打開瀏覽器
WebDriverWait(self.driver,10).until(EC.element_to_be_clickable((By.XPATH,'//*[@class="js-register"]')))
reg_element = self.driver.find_element_by_xpath('//*[@class="js-register"]')
reg_element.click()
WebDriverWait(self.driver,10).until(EC.element_to_be_clickable((By.XPATH,'//div[@class="gt_slider_knob gt_show"]')))
# 模擬拖動
self.analog_drag()
WebDriverWait 方法
說明
driver: 傳入WebDriver實例,即我們上例中的driver
timeout: 超時時間,等待的最長時間(同時要考慮隱性等待時間)
poll_frequency: 調用until或until_not中的方法的間隔時間,默認是0.5秒
ignored_exceptions: 忽略的異常,如果在調用until或until_not的過程中拋出這個元組中的異常, 則不中斷代碼,繼續等待;
如果拋出的是這個元組外的異常,則中斷代碼,拋出異常。默認只有NoSuchElementException。
基本使用方法
WebDriverWait(driver, 超時時長, 調用頻率, 忽略異常).until(可執行方法, 超時時返回的信息)
模擬拖動方法
def analog_drag(self):
# 鼠標移動到拖動按鈕,顯示出拖動圖片
element = self.driver.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
ActionChains(self.driver).move_to_element(element).perform()
time.sleep(3)
# 刷新一下極驗證圖片
element = self.driver.find_element_by_xpath('//a[@class="gt_refresh_button"]')
element.click()
time.sleep(1)
# 獲取圖片地址和位置坐標列表
cut_image_url,cut_location = self.get_image_url('//div[@class="gt_cut_bg_slice"]')
print(cut_image_url)
print(cut_location)
行為鏈
ActionChains(self.driver).move_to_element(element).perform()
模擬人移動鼠標到指定DOM元素
圖片處理方法
def get_image_url(self,xpath):
link = re.compile('background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;')
elements = self.driver.find_elements_by_xpath(xpath)
image_url = None
location = list()
for element in elements:
style = element.get_attribute('style')
groups = link.search(style)
url = groups[1]
x_pos = groups[2]
y_pos = groups[3]
location.append((int(x_pos), int(y_pos)))
if not image_url:
image_url = url
return image_url, location
使用正則表達式進行匹配的時候,需要將所有的DIV匹配出來 ,采用find_elements_by_xpath
方法,尤其註意elements
WebElement 具備一些常用的方法和屬性
- size:返回元素尺寸
- text :返回元素文本
- get_attribute(name):獲得屬性值
- is_dispalyed() :該元素是否用戶可見
初步運行結果
拼接圖
看下圖,註意一些基本元素,拼接的圖片由N個小矩形構成,分為上下兩個部分,小矩形的寬度和高度為10x58
核心由上下兩部分構成,每部分都是26個小矩形
因為,整體寬度為2610 = 260px ,整體高度為582=116px
但是,還記得博客開始的時候,你記錄的那個寬度和高度麽? 312x116
高度一致,但是寬度出現偏差
312-260 = 52px
52個像素去除以26個矩形,發現每個矩形差2px,這兩個像素也就是下面我們拼接圖片的重點了
def splicing_image(self,image_url,location):
res = requests.get(image_url)
file = BytesIO(res.content)
img = Image.open(file)
image_upper = []
image_down = []
for pos in location:
if pos[1] == 0:
# y值為0的坐標 屬於圖片上半部分,高度58
image_upper.append(img.crop((abs(pos[0]), 0, abs(pos[0]) + 10, 58)))
else:
# y值為58的坐標 屬於圖片上半部分,高度58
image_down.append(img.crop((abs(pos[0]), 58, abs(pos[0]) + 10, img.height)))
# 畫布的x軸偏移量
x_offset = 0
# 創建一張畫布
new_img = Image.new("RGB", (260, img.height))
for img in image_upper:
new_img.paste(img, (x_offset, 58))
x_offset += img.width
x_offset = 0
for img in image_down:
new_img.paste(img, (x_offset, 0))
x_offset += img.width
return new_img
說明
- requests.get(image_url) 下載圖片到本地
- BytesIO(res.content) 將字節轉換成二進制文件流
- Image.open(file) 獲取圖片
- img.crop 裁切圖片 left, upper, right, lower
- Image.new("RGB", (260, img.height)) 創建一個空白的圖片,將圖片序列中的元素,依次的拼接到裏面
最終實現效果
圖片存儲到本地
# 將圖片存儲到本地
cut_image.save("cut.jpg")
full_image.save("full.jpg")
好了,今天博客就先把圖片處理到位,明天著手拼接部分。
歡迎關註「非本科程序員」 回復 【0412】獲取本篇博客源碼
Python爬蟲入門教程 58-100 python爬蟲高級技術之驗證碼篇4-極驗證識別技術之一