破解猪八戒网滑动验证码,实现登录
python3.6 + selenium + pillow
安装(推荐使用清华源):
pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple/on
# @author: ZLY
# @time: 2020-09-13
# @copytight: All Right Reversed
from time import sleep, time
import random
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
class SliderVerification:
def __init__(self):
start = time()
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(10)
# 登录网址、点击验证
self.driver.get("https://account.zbj.com/login?fromurl=https%3A%2F%2Fchangsha.zbj.com%2Fsem%2Findex%3Fpmcode%3D128434637%26utm_source%3Dbaidu%26utm_medium%3DSEM%26sdclkid%3DALeN152_15fG152Nxg")
self.driver.maximize_window()
self.driver.find_element_by_class_name("geetest_radar_tip_content").click()
# 1. 截取图片
self.get_img()
# 2. 计算距离
distance = self.caculate()
# 如果验证成功则退出循环
while True:
# 3. 生成运动轨迹
x = self.track(distance)
# 4. 操作滑块移动
self.move(x)
success = self.driver.find_element_by_class_name("geetest_success_radar_tip_content").text
if success:
end = time()
print("校验成功, 验证时长:{:.2f}秒".format(end - start))
break
else:
sleep(3)
fail_flag = self.driver.find_element_by_class_name("geetest_reset_tip_content")
# 图被怪兽吃了
if "点击重试" in fail_flag.text:
print("图被怪兽吃了^_^")
# 点击重试,重新截图
fail_flag.click()
self.get_img()
distance = self.caculate()
# 图片拼合错误
else:
print("图片拼合错误……")
distance = self.caculate()
self.driver.close()
def get_img(self):
"""
@function: Capture pictures
"""
# 优化,由于网络加载延迟原因,js加载不到元素会报错,所以允许尝试三次
for i in range(3):
# 具体睡几秒取决于网速
sleep(5)
try:
# 截取滑块图
gap_hide_js = 'document.getElementsByClassName("geetest_canvas_bg")[0].style.display="none";'
self.driver.execute_script(gap_hide_js)
self.driver.find_element_by_class_name("geetest_canvas_slice").screenshot("滑块图.png")
# 截取缺口图
slider_hide_js = "document.getElementsByClassName('geetest_canvas_slice')[0].style.display = 'none';"
gap_show_js = "document.getElementsByClassName('geetest_canvas_bg')[0].style.display = 'block';"
self.driver.execute_script(slider_hide_js + gap_show_js)
self.driver.find_element_by_class_name("geetest_canvas_bg").screenshot("缺口图.png")
# 截取缺口图
full_show_js = "document.getElementsByClassName('geetest_canvas_fullbg')[0].style.display = 'block';"
self.driver.execute_script(gap_hide_js + slider_hide_js + full_show_js)
self.driver.find_element_by_class_name("geetest_canvas_fullbg").screenshot("完整图.png")
# 还原js
slider_show_js = "document.getElementsByClassName('geetest_canvas_slice')[0].style.display = 'block';"
full_hide_js = "document.getElementsByClassName('geetest_canvas_fullbg')[0].style.display = 'none';"
self.driver.execute_script(slider_show_js + gap_show_js + full_hide_js)
break
except Exception as e:
# 格式化输出错误信息
print(str(e))
def get_slice_x(self):
"""
@function: Get the coordinates of the slider graph
@thinking: Contrast color difference
@return: X-coordinate of the slider graph
"""
# 获取滑块图的坐标
img = Image.open('滑块图.png')
w, h = img.size
for x in range(w):
for y in range(h):
# 获取rgb
rgb = img.getpixel((x, y))
rgb_sum = rgb[0] + rgb[1] + rgb[2]
if rgb_sum <= 570:
return x
def get_gap_x(self):
"""
@function: Get the coordinates of the notch graph
@thinking: Comparing RGB of gap graph and complete graph
@return: X-coordinate of the notch graph
"""
# 获取滑块图的坐标
gimg = Image.open('缺口图.png')
fimg = Image.open('完整图.png')
w, h = gimg.size
for x in range(w):
for y in range(h):
grgb = gimg.getpixel((x, y))
frgb = fimg.getpixel((x, y))
r = abs(grgb[0] - frgb[0])
g = abs(grgb[1] - frgb[1])
b = abs(grgb[2] - frgb[2])
if r + g + b > 200:
return x
def caculate(self):
"""
@function: The x-coordinate of the notch graph minus the x-coordinate of the
slider graph is used to calculate the sliding distance of the slider
@return: distance
"""
slice_x = get_slice_x()
gap_x = get_gap_x()
distance = abs(gap_x - slice_x)
print("滑块图x:%s, 缺口图x:%s, 滑动距离:%s" % (slice_x, gap_x, distance))
return distance
def track(self, distance):
"""
@function: Generating sliding trajectory by Newton's law
@return: Sliding track ---> list
"""
tracks = []
def segmentate(s):
# 分块操作,对于某块轨迹如果20px,则需要切分成更小的轨迹
if 20 <= abs(s) < 35:
tracks.extend([round(s / 2) - 3, round(s / 2) + 3])
elif 35 <= abs(s) < 50:
tracks.extend([round(s / 3) - 5, round(s / 3), round(s / 3) + 5])
elif abs(s) >= 50:
tracks.extend([
round(s / 5) - 5, round(s / 5) - 3,
round(s / 5),
round(s / 5) + 3, round(s / 5) + 5
])
else:
tracks.append(round(s))
def popitem(tracks):
"""解决向左滑过头的问题,正负抵消"""
# 最大轨迹值列表,用来抵消连续负值
maxvalue = [0 for _ in range(6)]
flag = 0
for i in range(round(len(tracks) * 2 / 3)):
if tracks[i] < 0:
if flag == 7:
# 查找连续的6个负值
neglist = [tracks.pop(k) for k in range(i - 6, i)]
# 查找6个较大的正值
for x in range(len(tracks) - 1):
if tracks[x] > min(maxvalue):
maxvalue[maxvalue.index(min(maxvalue))] = tracks[x]
[tracks.remove(maxvalue[j]) for j in range(6)]
# 做抵消后的补偿值
num = sum(maxvalue) + sum(neglist)
segmentate(num)
break
else:
flag += 1
else:
flag = 0
mid = distance * 8 / 17
distance += 15
current = v0 = 0
while current < distance:
# 一位随机小数
t = random.randint(1, 4) / 2
if current <= mid:
a = random.randint(1, 3)
else:
a = - random.randint(2, 4) / 4
v0 += a * t
s = v0 * t + 0.5 * a * t ** 2
current += s
segmentate(round(s))
popitem(tracks)
# 后退大约15px左右
for i in range(5):
tracks.append(- random.randint(2, 4))
temp = sum(tracks) - (distance - 15)
# 如果temp < 0, 则滑块还没到目标距离
if temp < 0:
segmentate(temp)
# 如果temp >= 0, 则滑块超过了目标距离
else:
segmentate(-temp)
# 如果轨迹长度小于40或者大于120则重新生成,因为短了无法通过,长了浪费时间
if len(tracks) >= 80 or len(tracks) <= 40:
return track(distance - 15)
else:
print("轨迹总长:%s,轨迹段数:%s, 轨迹:%s" % (sum(tracks), len(tracks), tracks))
return tracks
def move(self, x):
"""
@function: The slider slides according to the motion track
"""
# 选中滑块
element = self.driver.find_element_by_xpath('//div[@class="geetest_slider_button"]')
# 动作链,按住鼠标不松、滑动、释放鼠标 等于0的位移没必要移动,浪费时间
ActionChains(self.driver).click_and_hold(element).perform()
[ActionChains(self.driver).move_by_offset(i, 0).perform() for i in x if i != 0]
ActionChains(self.driver).release(element).perform()
# 等待加载,需要等待一会,要不然可能会报莫名其妙的错误
sleep(3)
SliderVerification()