寫在前面:
其實本程式還有很多需要完善和改進的地方,后面會進行完善,大家多多包涵
概述
- 通過完整圖片與缺失滑塊的圖片進行像素對比,確定滑塊位置
- 邊緣檢測演算法,確定位置
- 規避檢測,模擬人的行為進行滑動滑塊
實作
-這里以帶刷網為例,展示驗證碼滑動的效果
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/1/2 18:34
# @Author : huni
# @File : 驗證碼2.py
# @Software: PyCharm
from selenium import webdriver
import time
import base64
from PIL import Image
from io import BytesIO
from selenium.webdriver.support.ui import WebDriverWait
import random
import copy
class VeriImageUtil():
def __init__(self):
self.defaultConfig = {
"grayOffset": 20,
"opaque": 1,
"minVerticalLineCount": 30
}
self.config = copy.deepcopy(self.defaultConfig)
def updateConfig(self, config):
# temp = copy.deepcopy(config)
for k in self.config:
if k in config.keys():
self.config[k] = config[k]
def getMaxOffset(self, *args):
# 計算偏移平均值最大的數
av = sum(args) / len(args)
maxOffset = 0
for a in args:
offset = abs(av - a)
if offset > maxOffset:
maxOffset = offset
return maxOffset
def isGrayPx(self, r, g, b):
# 是否是灰度像素點,允許波動offset
return self.getMaxOffset(r, g, b) < self.config["grayOffset"]
def isDarkStyle(self, r, g, b):
# 灰暗風格
return r < 128 and g < 128 and b < 128
def isOpaque(self, px):
# 不透明
return px[3] >= 255 * self.config["opaque"]
def getVerticalLineOffsetX(self, bgImage):
# bgImage = Image.open("./image/bg.png")
# bgImage.im.mode = 'RGBA'
bgBytes = bgImage.load()
x = 0
while x < bgImage.size[0]:
y = 0
# 點》》線,灰度線條數量
verticalLineCount = 0
while y < bgImage.size[1]:
px = bgBytes[x, y]
r = px[0]
g = px[1]
b = px[2]
# alph = px[3]
# print(px)
if self.isDarkStyle(r, g, b) and self.isGrayPx(r, g, b) and self.isOpaque(px):
verticalLineCount += 1
else:
verticalLineCount = 0
y += 1
continue
if verticalLineCount >= self.config["minVerticalLineCount"]:
# 連續多個像素都是灰度像素,直線,認為需要滑動這么多
# print(x, y)
return x
y += 1
x += 1
pass
class DragUtil():
def __init__(self, driver):
self.driver = driver
def __getRadomPauseScondes(self):
"""
:return:隨機的拖動暫停時間
"""
return random.uniform(0.6, 0.9)
def simulateDragX(self, source, targetOffsetX):
"""
模仿人的拖拽動作:快速沿著X軸拖動(存在誤差),再暫停,然后修正誤差
防止被檢測為機器人,出現“圖片被怪物吃掉了”等驗證失敗的情況
:param source:要拖拽的html元素
:param targetOffsetX: 拖拽目標x軸距離
:return: None
"""
action_chains = webdriver.ActionChains(self.driver)
# 點擊,準備拖拽
action_chains.click_and_hold(source)
# 拖動次數,二到三次
dragCount = random.randint(2, 3)
if dragCount == 2:
# 總誤差值
sumOffsetx = random.randint(-15, 15)
action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
# 暫停一會
action_chains.pause(self.__getRadomPauseScondes())
# 修正誤差,防止被檢測為機器人,出現圖片被怪物吃掉了等驗證失敗的情況
action_chains.move_by_offset(-sumOffsetx, 0)
elif dragCount == 3:
# 總誤差值
sumOffsetx = random.randint(-15, 15)
action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
# 暫停一會
action_chains.pause(self.__getRadomPauseScondes())
# 已修正誤差的和
fixedOffsetX = 0
# 第一次修正誤差
if sumOffsetx < 0:
offsetx = random.randint(sumOffsetx, 0)
else:
offsetx = random.randint(0, sumOffsetx)
fixedOffsetX = fixedOffsetX + offsetx
action_chains.move_by_offset(-offsetx, 0)
action_chains.pause(self.__getRadomPauseScondes())
# 最后一次修正誤差
action_chains.move_by_offset(-sumOffsetx + fixedOffsetX, 0)
action_chains.pause(self.__getRadomPauseScondes())
else:
raise Exception("莫不是系統出現了問題?!")
# 參考action_chains.drag_and_drop_by_offset()
action_chains.release()
action_chains.perform()
def simpleSimulateDragX(self, source, targetOffsetX):
"""
簡單拖拽模仿人的拖拽:快速沿著X軸拖動,直接一步到達正確位置,再暫停一會兒,然后釋放拖拽動作
B站是依據是否有暫停時間來分辨人機的,這個方法適用,
:param source:
:param targetOffsetX:
:return: None
"""
action_chains = webdriver.ActionChains(self.driver)
# 點擊,準備拖拽
action_chains.click_and_hold(source)
action_chains.pause(0.2)
action_chains.move_by_offset(targetOffsetX, 0)
action_chains.pause(0.6)
action_chains.release()
action_chains.perform()
def checkVeriImage(driver):
WebDriverWait(driver, 5).until(
lambda driver: driver.find_element_by_css_selector('.geetest_canvas_bg.geetest_absolute'))
time.sleep(1)
im_info = driver.execute_script(
'return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png");')
# 拿到base64編碼的圖片資訊
im_base64 = im_info.split(',')[1]
# 轉為bytes型別
im_bytes = base64.b64decode(im_base64)
with open('./temp_bg.png', 'wb') as f:
# 保存圖片到本地
f.write(im_bytes)
image_data = BytesIO(im_bytes)
bgImage = Image.open(image_data)
# 滑塊距離左邊有 5 像素左右誤差
offsetX = VeriImageUtil().getVerticalLineOffsetX(bgImage)
print("offsetX: {}".format(offsetX))
if not type(offsetX) == int:
# 計算不出,重新加載
driver.find_element_by_css_selector(".geetest_refresh_1").click()
checkVeriImage(driver)
return
elif offsetX == 0:
# 計算不出,重新加載
driver.find_element_by_css_selector(".geetest_refresh_1").click()
checkVeriImage(driver)
return
else:
dragVeriImage(driver, offsetX)
def dragVeriImage(driver, offsetX):
# 可能產生檢測到右邊緣的情況
# 拖拽
eleDrag = driver.find_element_by_css_selector(".geetest_slider_button")
dragUtil = DragUtil(driver)
dragUtil.simulateDragX(eleDrag, offsetX - 10)
time.sleep(2.5)
if isNeedCheckVeriImage(driver):
checkVeriImage(driver)
return
dragUtil.simulateDragX(eleDrag, offsetX - 6)
time.sleep(2.5)
if isNeedCheckVeriImage(driver):
checkVeriImage(driver)
return
# 滑塊寬度40左右
dragUtil.simulateDragX(eleDrag, offsetX - 56)
time.sleep(2.5)
if isNeedCheckVeriImage(driver):
checkVeriImage(driver)
return
dragUtil.simulateDragX(eleDrag, offsetX - 52)
if isNeedCheckVeriImage(driver):
checkVeriImage(driver)
return
def isNeedCheckVeriImage(driver):
if driver.find_element_by_css_selector(".geetest_panel_error").is_displayed():
driver.find_element_by_css_selector(".geetest_panel_error_content").click();
return True
return False
def task():
# 此步驟很重要,設定chrome為開發者模式,防止被各大網站識別出來使用了Selenium
# options = webdriver.ChromeOptions()
# options.add_experimental_option('excludeSwitches', ['enable-automation'])
# driver = webdriver.Firefox(executable_path=r"../../../res/webdriver/geckodriver_x64_0.26.0.exe",options=options)
driver = webdriver.Chrome()
driver.get('https://www.ieqq.net/?cid=222&tid=5584')
time.sleep(3)
# driver.find_element_by_xpath('//*[@id="gt-register-mobile"]/div/div[2]/div[1]/div[2]/div/div[2]/div['
# '1]/input').send_keys("17633935269")
# driver.find_element_by_xpath('//*[@id="gt-register-mobile"]/div/div[2]/div[1]/div[2]/div/div[2]/div[2]/div['
# '1]/div').click()
# driver.find_element_by_css_selector(".btn.btn-login").click()
# time.sleep(2)
# 搜索欄標簽定位
search_input = driver.find_element_by_xpath('//*[@id="inputvalue"]')
time.sleep(3)
# 標簽的互動
search_input.send_keys('xxxxxx')
# 執行一組js程式
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
# 搜索按鈕的定位
btn = driver.find_element_by_xpath('//*[@id="submit_buy"]')
# 點擊搜索按鈕
btn.click()
time.sleep(6)
driver.find_element_by_xpath('//*[@id="captcha"]/div[3]/div[3]').click()
time.sleep(3)
checkVeriImage(driver)
pass
# 該方法用來確認元素是否存在,如果存在回傳flag=true,否則回傳false
def isElementExist(driver, css):
try:
driver.find_element_by_css_selector(css)
return True
except:
return False
if __name__ == '__main__':
task()
寫在后面
雖然說驗證碼破解是可以一定程度上解決登錄爬蟲的問題,
但是識別率也不可能達到百分之百識別,所以建議需要登錄
才可以進行下去的爬蟲程式,可以使用cookies模擬登陸,
僅需第一次登陸人工識別登陸驗證碼,或者掃描二維碼,就可以使用一段時間,
當然各有利弊,cookies在一段時間后也會失效,這個和驗證碼都是見仁見智的操作,
驗證碼詳情可以參考https://blog.csdn.net/weixin_43881394/article/details/108360729
大家如果覺得小編的代碼有用,可以多多關注小編,
同時小編的公眾號也開通了,大家可以關注下,后續進行粉絲回饋,大家一起學習python叭

打賞小編點這里哦

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/244217.html
標籤:AI
上一篇:2021全國大學生計算機系統能力大賽作業系統設計賽第一場研討會隆重舉行
下一篇:二叉搜索樹的插入、洗掉
