爬虫进阶教程:极验(GEETEST)验证码破解教程

article/2025/9/10 16:23:51

摘要:

爬虫最大的敌人之一是什么?没错,验证码!Geetest作为提供验证码服务的行家,市场占有率还是蛮高的。遇到Geetest提供的滑动验证码怎么破?授人予鱼不如授人予渔,接下来就为大家呈现本教程的精彩内容。

一、前言

爬虫最大的敌人之一是什么?没错,验证码!Geetest作为提供验证码服务的行家,市场占有率还是蛮高的。遇到Geetest提供的滑动验证码怎么破?

一种方法是分析它的js加密方法,通过大量抓包分析找到它的返回参数,直接自动生成需要的参数即可,这种方法工程量大一些,并且官方js脚本一升级,就得重新分析,耗时耗力。

今天为大家介绍的一种方法是,通过Selenium模拟用户滑动解锁。这个方法的优势在于简单,方便更新。但是它的缺点也很明显,速度慢,并且不能制作成api接口的形式。

授人予鱼不如授人予渔,接下来就为大家呈现本教程的精彩内容。不过,在阅读本篇文章之前,请确保你已经掌握网络爬虫基础,如果不具备爬虫基础,请到我的CSDN专栏学习。然后,再来阅读本文,我的专栏地址:点我查看

二、先睹为快

左侧显示的为自动识别过程,右边是一些打印信息。

三、实战分析

我们以国家企业信用信息公式系统为例,这是一个企业信息查询的网站,在每次查询都需要进行一次验证码识别。它所使用的就是GEETEST验证码,它的URL:点我查看

这个网站是这个样子的:

1、过程分析

要想把大象装冰箱,总共分几步?

  • 把冰箱门打开
  • 把大象赛冰箱里
  • 把冰箱门关上

那么,现在思考一个问题,通过Selenium模拟用户滑动解锁,总共分几步?请停在这里,思考五分钟,再继续阅读!

我们先公布一个粗率的答案:

  • 使用Selenium打开页面。
  • 匹配到输入框,输入要查询的信息,并点击查询按钮。
  • 读取验证码图片,并做缺口识别。
  • 根据缺口位置,计算滑动距离。
  • 根据滑动距离,拖拽滑块到需要匹配的位置。

其实,将每个步骤拆分开来一点一点实现并不难,接下来进入正文。

2、实战开始

第一步:使用Selenium打开网页,并输入信息,点击查询按钮。

这部分内容很简单,Selenium基础性的东西我不再讲解,如有不懂,请看我专栏的Selenium相关内容。

编写代码如下:

# -*-coding:utf-8 -*-
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium import webdriver
class Crack():def __init__(self,keyword):self.url = 'http://bj.gsxt.gov.cn/sydq/loginSydqAction!sydq.dhtml';self.browser = webdriver.Chrome('D:\\chromedriver.exe')self.wait = WebDriverWait(self.browser, 100)self.keyword = keyworddef open(self):"""打开浏览器,并输入查询内容"""self.browser.get(self.url)keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))bowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))keyword.send_keys(self.keyword)bowton.click()def crack(self):# 打开浏览器self.open()
if __name__ == '__main__':print('开始验证')crack = Crack(u'中国移动')crack.crack()

运行效果如下:

第二步:保存验证码图片

我们审查元素找打图片的地址,审查结果如下:

可以看到,图片是很多图片合成的,也就是说你只保存所有地址的图片是不行的。它是通过background-position的方法进行合成的。每一个图片是乱的,这个怎么搞?很简单,抓取这些图片的链接,然后根据链接的图片,再合成这张没有缺口的图片,获取缺口图的方法也是如此,都是自己合成。

编写代码如下:

# -*-coding:utf-8 -*-
import time, random
import PIL.Image as image
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import requests, json, re, urllib
from bs4 import BeautifulSoup
from urllib.request import urlretrieve
class Crack():def __init__(self,keyword):self.url = 'http://bj.gsxt.gov.cn/sydq/loginSydqAction!sydq.dhtml';self.browser = webdriver.Chrome('D:\\chromedriver.exe')self.wait = WebDriverWait(self.browser, 100)self.keyword = keywordself.BORDER = 6def __del__(self):time.sleep(2)self.browser.close()def get_screenshot(self):"""获取网页截图:return: 截图对象"""screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))return screenshotdef get_position(self):"""获取验证码位置:return: 验证码位置元组"""img = self.browser.find_element_by_class_name("gt_box")time.sleep(2)location = img.locationsize = img.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']return (top, bottom, left, right)def get_image(self, name='captcha.png'):"""获取验证码图片:return: 图片对象"""top, bottom, left, right = self.get_position()print('验证码位置', top, bottom, left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captchadef get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):"""获取验证码图片:return: 图片的location信息"""bg = []fullgb = []while bg == [] and fullgb == []:bf = BeautifulSoup(self.browser.page_source, 'lxml')bg = bf.find_all('div', class_ = 'gt_cut_bg_slice')fullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')bg_url = re.findall('url\(\"(.*)\"\);', bg[0].get('style'))[0].replace('webp', 'jpg')fullgb_url = re.findall('url\(\"(.*)\"\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')bg_location_list = []fullbg_location_list = []for each_bg in bg:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])bg_location_list.append(location)for each_fullgb in fullgb:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])fullbg_location_list.append(location)urlretrieve(url = bg_url, filename = bg_filename)print('缺口图片下载完成')urlretrieve(url = fullgb_url, filename = fullbg_filename)print('背景图片下载完成')return bg_location_list, fullbg_location_listdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y']==-58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))if location['y']==0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef open(self):self.browser.get(self.url)keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))bowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))keyword.send_keys(self.keyword)bowton.click()def get_slider(self):"""获取滑块:return: 滑块对象"""while True:try:slider = self.browser.find_element_by_xpath("//div[@class='gt_slider_knob gt_show']")breakexcept:time.sleep(0.5)return sliderdef get_gap(self, img1, img2):"""获取缺口偏移量:param img1: 不带缺口图片:param img2: 带缺口图片:return:"""left = 43for i in range(left, img1.size[0]):for j in range(img1.size[1]):if not self.is_pixel_equal(img1, img2, i, j):left = ireturn leftreturn left    def is_pixel_equal(self, img1, img2, x, y):"""判断两个像素是否相同:param image1: 图片1:param image2: 图片2:param x: 位置x:param y: 位置y:return: 像素是否相同"""# 取两个图片的像素点pix1 = img1.load()[x, y]pix2 = img2.load()[x, y]threshold = 60if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(pix1[2] - pix2[2] < threshold)):return Trueelse:return Falsedef get_track(self, distance):"""根据偏移量获取移动轨迹:param distance: 偏移量:return: 移动轨迹"""# 移动轨迹track = []# 当前位移current = 0# 减速阈值mid = distance * 4 / 5# 计算间隔t = 0.2# 初速度v = 0while current < distance:if current < mid:# 加速度为正2a = 2else:    # 加速度为负3a = -3# 初速度v0v0 = v# 当前速度v = v0 + atv = v0 + a * t# 移动距离x = v0t + 1/2 * a * t^2move = v0 * t + 1 / 2 * a * t * t# 当前位移current += move# 加入轨迹track.append(round(move))return trackdef move_to_gap(self, slider, track):"""拖动滑块到缺口处:param slider: 滑块:param track: 轨迹:return:"""ActionChains(self.browser).click_and_hold(slider).perform()while track:x = random.choice(track)ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()track.remove(x)time.sleep(0.5)ActionChains(self.browser).release().perform()def crack(self):# 打开浏览器self.open()# 保存的图片名字bg_filename = 'bg.jpg'fullbg_filename = 'fullbg.jpg'# 获取图片bg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)# 根据位置对图片进行合并还原bg_img = self.get_merge_image(bg_filename, bg_location_list)fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)# 点按呼出缺口slider = self.get_slider()# 获取缺口位置gap = self.get_gap(fullbg_img, bg_img)print('缺口位置', gap)track = self.get_track(gap-self.BORDER)print('滑动滑块')print(track)self.move_to_gap(slider, track)
if __name__ == '__main__':print('开始验证')crack = Crack(u'中国移动')crack.crack()print('验证成功')

运行效果如下:

可以看到,运行之后,我们已经顺利生成了两张图片,一个是缺口图,另一个是非缺口图。

第三步:计算缺口距离

根据缺口图和非缺口图,通过比对图像的像素点的大小区别,找到缺口位置。

编写代码如下:

# -*-coding:utf-8 -*-
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from urllib.request import urlretrieve
from selenium import webdriver
from bs4 import BeautifulSoup
import PIL.Image as image
import reclass Crack():def __init__(self,keyword):self.url = 'http://bj.gsxt.gov.cn/sydq/loginSydqAction!sydq.dhtml'self.browser = webdriver.Chrome('D:\\chromedriver.exe')self.wait = WebDriverWait(self.browser, 100)self.keyword = keyworddef open(self):"""打开浏览器,并输入查询内容"""self.browser.get(self.url)keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))bowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))keyword.send_keys(self.keyword)bowton.click()def get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):"""获取验证码图片:return: 图片的location信息"""bg = []fullgb = []while bg == [] and fullgb == []:bf = BeautifulSoup(self.browser.page_source, 'lxml')bg = bf.find_all('div', class_ = 'gt_cut_bg_slice')fullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')bg_url = re.findall('url\(\"(.*)\"\);', bg[0].get('style'))[0].replace('webp', 'jpg')fullgb_url = re.findall('url\(\"(.*)\"\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')bg_location_list = []fullbg_location_list = []for each_bg in bg:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])bg_location_list.append(location)for each_fullgb in fullgb:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])fullbg_location_list.append(location)urlretrieve(url = bg_url, filename = bg_filename)print('缺口图片下载完成')urlretrieve(url = fullgb_url, filename = fullbg_filename)print('背景图片下载完成')return bg_location_list, fullbg_location_listdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y'] == -58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x']) + 10, 166)))if location['y'] == 0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x']) + 10, 58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y']==-58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))if location['y']==0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef get_gap(self, img1, img2):"""获取缺口偏移量:param img1: 不带缺口图片:param img2: 带缺口图片:return:"""left = 43for i in range(left, img1.size[0]):for j in range(img1.size[1]):if not self.is_pixel_equal(img1, img2, i, j):left = ireturn leftreturn left   def crack(self):# 打开浏览器self.open()# 保存的图片名字bg_filename = 'bg.jpg'fullbg_filename = 'fullbg.jpg'# 获取图片bg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)# 根据位置对图片进行合并还原bg_img = self.get_merge_image(bg_filename, bg_location_list)fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)# 获取缺口位置gap = self.get_gap(fullbg_img, bg_img)print('缺口位置', gap)if __name__ == '__main__':print('开始验证')crack = Crack(u'中国移动')crack.crack()

运行结果如下:

这样我们就计算除了缺口位置,接下来就是根据缺口位置,滑动滑块到相应位置。

第四步:计算滑动轨迹

我们可以使用瞬间移动,直接在1s内移动到目标位置,结果就是”被吃了“。

匀速直线运动,匀速直线运动大法好!果不其然,还是”被吃了“,继续尝试。

模仿抖抖病患者运动,颤颤巍巍,如履薄冰,估计geetest服务器认为是我外婆在操作吧。

虽然这个方法偶尔会成功,但成功率极低。最好的方法是什么呢?

模拟人的运动!你想一下,人在滑动滑块的初期是不是速度快,但是当要接近缺口位置的时候,会减速,因为我得对准缺口位置啊!这怎么实现呢?使用我们初中学过的物理知识:

当前速度公式为:

v = v0 + a * t

其中,v是当前速度,v0是初始速度,a是加速度,t是时间。我们刚开始的让加速大,当过了中间位置,降低加速度。使用这个移动过程,移动滑块到缺口位置。

编写代码如下:

# -*-coding:utf-8 -*-
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from urllib.request import urlretrieve
from selenium import webdriver
from bs4 import BeautifulSoup
import PIL.Image as image
import reclass Crack():def __init__(self,keyword):self.url = 'http://bj.gsxt.gov.cn/sydq/loginSydqAction!sydq.dhtml'self.browser = webdriver.Chrome('D:\\chromedriver.exe')self.wait = WebDriverWait(self.browser, 100)self.keyword = keywordself.BORDER = 6def open(self):"""打开浏览器,并输入查询内容"""self.browser.get(self.url)keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))bowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))keyword.send_keys(self.keyword)bowton.click()def get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):"""获取验证码图片:return: 图片的location信息"""bg = []fullgb = []while bg == [] and fullgb == []:bf = BeautifulSoup(self.browser.page_source, 'lxml')bg = bf.find_all('div', class_ = 'gt_cut_bg_slice')fullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')bg_url = re.findall('url\(\"(.*)\"\);', bg[0].get('style'))[0].replace('webp', 'jpg')fullgb_url = re.findall('url\(\"(.*)\"\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')bg_location_list = []fullbg_location_list = []for each_bg in bg:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])bg_location_list.append(location)for each_fullgb in fullgb:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])fullbg_location_list.append(location)urlretrieve(url = bg_url, filename = bg_filename)print('缺口图片下载完成')urlretrieve(url = fullgb_url, filename = fullbg_filename)print('背景图片下载完成')return bg_location_list, fullbg_location_listdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y'] == -58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x']) + 10, 166)))if location['y'] == 0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x']) + 10, 58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y']==-58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))if location['y']==0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef is_pixel_equal(self, img1, img2, x, y):"""判断两个像素是否相同:param image1: 图片1:param image2: 图片2:param x: 位置x:param y: 位置y:return: 像素是否相同"""# 取两个图片的像素点pix1 = img1.load()[x, y]pix2 = img2.load()[x, y]threshold = 60if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(pix1[2] - pix2[2] < threshold)):return Trueelse:return Falsedef get_gap(self, img1, img2):"""获取缺口偏移量:param img1: 不带缺口图片:param img2: 带缺口图片:return:"""left = 43for i in range(left, img1.size[0]):for j in range(img1.size[1]):if not self.is_pixel_equal(img1, img2, i, j):left = ireturn leftreturn left   def get_track(self, distance):"""根据偏移量获取移动轨迹:param distance: 偏移量:return: 移动轨迹"""# 移动轨迹track = []# 当前位移current = 0# 减速阈值mid = distance * 4 / 5# 计算间隔t = 0.2# 初速度v = 0while current < distance:if current < mid:# 加速度为正2a = 2else:   # 加速度为负3a = -3# 初速度v0v0 = v# 当前速度v = v0 + atv = v0 + a * t# 移动距离x = v0t + 1/2 * a * t^2move = v0 * t + 1 / 2 * a * t * t# 当前位移current += move# 加入轨迹track.append(round(move))return trackdef crack(self):# 打开浏览器self.open()# 保存的图片名字bg_filename = 'bg.jpg'fullbg_filename = 'fullbg.jpg'# 获取图片bg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)# 根据位置对图片进行合并还原bg_img = self.get_merge_image(bg_filename, bg_location_list)fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)# 获取缺口位置gap = self.get_gap(fullbg_img, bg_img)print('缺口位置', gap)track = self.get_track(gap-self.BORDER)print('滑动滑块')print(track)if __name__ == '__main__':print('开始验证')crack = Crack(u'中国移动')crack.crack()

运行效果如下:

第五步:移动滑块

根据返回的每次滑动的距离,我们移动滑块至缺口位置。

编写代码如下:

# -*-coding:utf-8 -*-
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from urllib.request import urlretrieve
from selenium import webdriver
from bs4 import BeautifulSoup
import PIL.Image as image
import reclass Crack():def __init__(self,keyword):self.url = 'http://bj.gsxt.gov.cn/sydq/loginSydqAction!sydq.dhtml'self.browser = webdriver.Chrome('D:\\chromedriver.exe')self.wait = WebDriverWait(self.browser, 100)self.keyword = keywordself.BORDER = 6def open(self):"""打开浏览器,并输入查询内容"""self.browser.get(self.url)keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'keyword_qycx')))bowton = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'btn')))keyword.send_keys(self.keyword)bowton.click()def get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):"""获取验证码图片:return: 图片的location信息"""bg = []fullgb = []while bg == [] and fullgb == []:bf = BeautifulSoup(self.browser.page_source, 'lxml')bg = bf.find_all('div', class_ = 'gt_cut_bg_slice')fullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')bg_url = re.findall('url\(\"(.*)\"\);', bg[0].get('style'))[0].replace('webp', 'jpg')fullgb_url = re.findall('url\(\"(.*)\"\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')bg_location_list = []fullbg_location_list = []for each_bg in bg:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])bg_location_list.append(location)for each_fullgb in fullgb:location = {}location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])fullbg_location_list.append(location)urlretrieve(url = bg_url, filename = bg_filename)print('缺口图片下载完成')urlretrieve(url = fullgb_url, filename = fullbg_filename)print('背景图片下载完成')return bg_location_list, fullbg_location_listdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y'] == -58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x']) + 10, 166)))if location['y'] == 0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x']) + 10, 58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef get_merge_image(self, filename, location_list):"""根据位置对图片进行合并还原:filename:图片:location_list:图片位置"""im = image.open(filename)new_im = image.new('RGB', (260,116))im_list_upper=[]im_list_down=[]for location in location_list:if location['y']==-58:im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))if location['y']==0:im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))new_im = image.new('RGB', (260,116))x_offset = 0for im in im_list_upper:new_im.paste(im, (x_offset,0))x_offset += im.size[0]x_offset = 0for im in im_list_down:new_im.paste(im, (x_offset,58))x_offset += im.size[0]new_im.save(filename)return new_imdef is_pixel_equal(self, img1, img2, x, y):"""判断两个像素是否相同:param image1: 图片1:param image2: 图片2:param x: 位置x:param y: 位置y:return: 像素是否相同"""# 取两个图片的像素点pix1 = img1.load()[x, y]pix2 = img2.load()[x, y]threshold = 60if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(pix1[2] - pix2[2] < threshold)):return Trueelse:return Falsedef get_gap(self, img1, img2):"""获取缺口偏移量:param img1: 不带缺口图片:param img2: 带缺口图片:return:"""left = 43for i in range(left, img1.size[0]):for j in range(img1.size[1]):if not self.is_pixel_equal(img1, img2, i, j):left = ireturn leftreturn left   def get_track(self, distance):"""根据偏移量获取移动轨迹:param distance: 偏移量:return: 移动轨迹"""# 移动轨迹track = []# 当前位移current = 0# 减速阈值mid = distance * 4 / 5# 计算间隔t = 0.2# 初速度v = 0while current < distance:if current < mid:# 加速度为正2a = 2else:   # 加速度为负3a = -3# 初速度v0v0 = v# 当前速度v = v0 + atv = v0 + a * t# 移动距离x = v0t + 1/2 * a * t^2move = v0 * t + 1 / 2 * a * t * t# 当前位移current += move# 加入轨迹track.append(round(move))return trackdef get_slider(self):"""获取滑块:return: 滑块对象"""while True:try:slider = self.browser.find_element_by_xpath("//div[@class='gt_slider_knob gt_show']")breakexcept:time.sleep(0.5)return sliderdef move_to_gap(self, slider, track):"""拖动滑块到缺口处:param slider: 滑块:param track: 轨迹:return:"""ActionChains(self.browser).click_and_hold(slider).perform()while track:x = random.choice(track)ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()track.remove(x)time.sleep(0.5)ActionChains(self.browser).release().perform()def crack(self):# 打开浏览器self.open()# 保存的图片名字bg_filename = 'bg.jpg'fullbg_filename = 'fullbg.jpg'# 获取图片bg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)# 根据位置对图片进行合并还原bg_img = self.get_merge_image(bg_filename, bg_location_list)fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)# 获取缺口位置gap = self.get_gap(fullbg_img, bg_img)print('缺口位置', gap)track = self.get_track(gap-self.BORDER)print('滑动滑块')print(track)# 点按呼出缺口slider = self.get_slider()# 拖动滑块到缺口处self.move_to_gap(slider, track)if __name__ == '__main__':print('开始验证')crack = Crack(u'中国移动')crack.crack()print('验证成功')

运行上述代码,即实现滑动验证码破解,再看下那个nice的瞬间吧。

五、总结

  • 本文抛去了很多具体的实现过程,省略了每行代码的讲解,因为我感觉,既然是进阶教程,那些初级知识就没必要再细讲,学我的初级课程的朋友,应该已经具备了自己分析的能力。
  • 本文的破解方法仅用于学习交流,请勿用于任何非法用途。
  • 本文出现的所有代码和,均可在我的github上下载,欢迎Follow、Star:https://github.com/Jack-Cherish/python-spider
  • 如有问题欢迎留言讨论!

    阅读原文


http://chatgpt.dhexx.cn/article/Dqqi3aY4.shtml

相关文章

【已解决】安卓手机的GeeTest文件夹是什么

网上关于安卓系统手机的GeeTest目录是什么的文章和帖子&#xff0c;绝大部分打着GeeTest的标题&#xff0c;内容都是牛头不对马嘴&#xff0c;答非所问&#xff0c;没一个能解释清楚。 我刚刚找到了正式的答复如下&#xff1a; Android 手机上的“geetest”目录与名为“极验”的…

正定二次型与半正定二次型

对于实二次型其中A是实对称的&#xff0c;下列条件等价&#xff1a; 正定的 &#xff08;1&#xff09;是正定的. &#xff08;2&#xff09;它的正惯性指数p等于n. &#xff08;3&#xff09;有可逆实矩阵C&#xff0c;使得其中 &#xff08;4&#xff09;实对称矩阵A是正…

怎么对document.write写出来的内容调整对齐方式_【求职技巧】给少数人:硅谷BAT级别的简历这么写...

我什么都不会”,“我什么都没干”,“这个项目很水”,这是我在帮别人修改简历时听到的最多的几句话。难道你真的什么都不会吗?真的什么都没干吗?真的很水吗?其实很多情况下,是这样的。 但是很水就放弃治疗了吗?不会的。放下无谓的抱怨和遗憾,好好梳理自己,认真编…

c语言八皇后问题经典算法,经典算法之八皇后问题

八皇后问题是一个古老而又著名的问题&#xff0c;是学习回溯算法的一个经典案例。今天我们就一起来探究一下吧&#xff01; 时间退回到1848年&#xff0c;国际西洋棋棋手马克斯贝瑟尔提出了这样的一个问题&#xff0c; 在88格的国际象棋上摆放八个皇后&#xff0c;使其不能互相…

从八皇后问题思考回溯法

一、八皇后问题 八皇后是经典的回溯法问题&#xff0c;题目是说将八个皇后&#xff0c;放到88的国际象棋棋盘中中&#xff0c;使得任意两个皇后都不能在同一行、同一列以及同一条对角线上。下图是一个四皇后的搜索示意图。 八皇后问题可以通过暴力法求解&#xff0c;代码也很…

八皇后问题(Python)

一.问题简介 八皇后问题&#xff1a; 如何能在 8*8 的国际象棋棋盘上放置八个皇后&#xff0c;使得任何一个皇后都无法直接吃掉其他的皇后&#xff1f;为了到达此目的&#xff0c;任两个皇后都不能处于同一条横行、纵行或斜线上。 二.几种思路和方法 1.回溯法递归思想 如图所…

八皇后问题详解(四种解法)

所有源码都在github上(https://github.com/seasonyao/eight_queen_question) 如果你去百度百科八皇后这个问题,你会发现人家也是历史上有头有脸的一个问题,最后一句“计算机发明后就有一万种方式解决这个问题”读起来也让程序猿们很快活。闲话少说,开始阐述我的思路: 最…

八皇后问题

八皇后问题 八皇后问题(英文:Eight queens)&#xff0c;是由国际西洋棋棋手马克斯贝瑟尔于1848年提出的问题&#xff0c;是回溯算法的典型案例。 问题表述为:在88格的国际象棋上摆放8个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或…

八皇后问题(适合初学者的写法)C语言

什么是八皇后问题&#xff1a; 八皇后问题&#xff0c;是一个古老而著名的问题&#xff0c;是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯贝瑟尔于1848年提出&#xff1a;在88格的国际象棋上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能…

八皇后问题,秒懂递归回溯(有图详解|c语言)

目录 &#x1f478;&#x1f3fb;前言 &#x1f478;&#x1f3fb;题目介绍 &#x1f478;&#x1f3fb;引入&#xff1a; &#x1f478;&#x1f3fb;解决思路&#xff1a; &#x1f478;&#x1f3fb;理论存在&#xff0c;实践开始&#xff01; &#x1f478;&#x1f…

利用ngrok实现域名映射局域网ip

前言 相信很多开发者都有这样的需求&#xff0c;需要让外网访问你本地的服务器&#xff0c;方便调试本地代码&#xff0c;或者让别人体验到自己做的应用。那么这时&#xff0c;我们需要做的就是将我们本地的端口映射到一个外网的端口上&#xff0c;也就是内网穿透。常见的解决…

python调用手机摄像头,实现实时调用摄像头,需要你的电脑和手机在同一个局域网内

1、android手机上安装一款APP&#xff1a;IP摄像头&#xff0c;app的图片如上图 2.调用代码如下 import cv2cv2.namedWindow("camera", 1) # 开启ip摄像头 video "http://admin:admin10.0.0.32:8081/" # 此处后的ipv4 地址需要改为app提供的地址 cap c…

02、处于不同局域网下的Socket通信(网络部分理论知识)

目录 一、服务器 1、服务器的种类和功能 2、服务器的操作系统 3、IIs、Apache、Tomcat 4、云服务器 弹性云服务器&#xff08;Elastic Cloud Server&#xff0c;ECS&#xff09; 云服务器安全组 二、OSI七层模型与TCP/IP五层模型 三、外网、内网、公网、私网 内网穿透…

使用wireshark抓取聊天信息(局域网内的udp通信)

文章目录 1&#xff0c;实验目的2&#xff0c;实验操作3&#xff0c;总结4&#xff0c;附件 1&#xff0c;实验目的 1.分析这程序所采用的是udp还是tcp 2.在抓取包中找到窃取到的聊天信息 (英文字符和汉字可能经过了某种编码转换&#xff0c;数据包中不是明文) 3.如果是网络连…

安装黑群晖找不到局域网电脑_黑群晖洗白太复杂?我用蒲公英P5轻松实现

前言: 随着网盘时代的结束,剩下的网盘供应商又开启了垄断方式,所以越来越多的小伙伴开始自己组自己的家庭NAS网络存储服务器。比如笔者的一个好基友就是如此。其实开始笔者是想让他直接一步到位,买群晖或者铁威马的NAS,在放入硬盘就可“一劳永逸”。然而,这个小伙伴看到了…

内网穿透实现局域网内搭建私服务器

使用云服务器实现内网穿透。内网里建立一台老旧win机专门用来挂pt&#xff0c;在上面存储视频和软件&#xff0c;而后映射在外网中&#xff0c;通过手机和电脑随时随地的下载和在线观看win机上的视频和文件。 1、修改ssh的默认端口 在公网中使用常用软件的默认端口会导致自己的…

局域网终结者_p2p终结者怎么安装使用 p2p终结者安装使用方法【介绍】

p2p终结者是一款局域网控制软件&#xff0c;他的主要功能就是控制和限制同一个局域网内其它的上网用户&#xff0c;如限制不让别人上QQ&#xff0c;不让别人开网页和不让别人下载&#xff0c;只要他和你在同一网之内你就可以控制他&#xff0c;并且神奇的是&#xff0c;不需要动…

Kali Linux-MSF远控局域网手机

前言 严正声明&#xff1a;本文仅限于技术讨论与分享&#xff0c;严禁用于非法途径。 本文目的&#xff1a;演示如何借助Kali Linux系统的Metasploit渗透测试框架生成远程控制程序&#xff0c;然后感染局域网内的Android手机&#xff0c;从而实现对目标手机数据的读取、音频的…

超级眼局域网监控软件 员工禁止软件 可以控制时间段

第一步&#xff1a; 下载超级眼局域网监控软件&#xff1a; 将经理端安装在管理者的电脑上&#xff0c;员工端安装在需要被监控者的电脑上。 第二步&#xff1a;在经理端电脑上操作。打开管理端软件&#xff0c;扫描所有的被控电脑。然后选择需要管理的电脑&#xff08;可以全选…

Kali使用Netdiscover探测局域网中存活主机

1、netdiscover介绍 Netdiscover 是一个主动/被动的ARP 侦查工具。使用Netdiscover工具可以在网络上扫描IP地址,检查在线主机或搜索为它们发送的ARP请求。 2、 主动模式:主动模式顾名思义就是主动的探测发现网络内主机,但是这种方式往往会引起网络管理员的注意。 打开Kali终…