selenium、PIL之JD滑动验证码破解

selenium、PIL之单层图滑动验证码破解

如何破解滑动验证码

市面上反爬有各种机制,其中滑动验证码分为双层图与单层图:
1.双层图可以通过在控制台修改JS中style的代码,通过修改display属性来获取带有滑动缺口的图片与完整图,进行两图颜色比对从而判断滑动距离,通过模拟人类活动验证码的轨迹来实现登陆;
2.单层图不具有完整图,所以难度与双层图相比更大,其中最大的难点在于距离的计算与滑动轨迹

前期准备

在开始之前我们需要准备:

  1. 下载需要的类包:Pillow(继承了PIL)、Selenium、time
  2. 安装浏览器调试工具(本文使用Chromedriver)
    Chromedriver调试下载地址:链接: http://npm.taobao.org/mirrors/chromedriver/
  3. 准备好调试工具,本文使用的工具是Jupyter-notebook、可以是pycharm

代码操作

导入的类与包:

from collections import Counter
from PIL import Image
from selenium import webdriver
import time
from selenium.webdriver.common.action_chains import ActionChains
  1. 首先,先通过建立类来进行方法封装,方便后续调用
class JDlogin():
    def __init__(self):#定义函数,链接登录页面
        self.driver = driver = webdriver.Chrome()#启动调试工具
        self.driver.get('https://passport.jd.com/new/login.aspx')#获取JD登陆页面
        time.sleep(2)
  1. 之后就是通过selenium进行图片获取,再交付给PIL进行图片处理
	def get_picture(self):#获取图片
		#通过xpath寻找按键点击“账户登陆”
        self.driver.find_element_by_xpath('.//div[@class="login-tab login-tab-r"]/a').click()
        time.sleep(1)
        #定位账号输入框
        self.driver.find_element_by_xpath('.//input[@id="loginname"]').send_keys('你的JD账号')
        time.sleep(1)
        #定位密码输入框
        self.driver.find_element_by_xpath('.//input[@id="nloginpwd"]').send_keys('你的JD密码')
        time.sleep(1)
        #定位登陆按钮,并点击,此时会展示出验证码图片
        self.driver.find_element_by_xpath('.//div[@class="login-btn"]/a').click()
        time.sleep(1)
        #通过修改JS隐藏滑块并截屏获取验证码图片,保存至当前目录,名为slice.png(双层图也是这么干,不过ClassName与xpath需要改动)
        js = 'document.getElementsByClassName("JDJRV-smallimg")[0].style.display="none"'
        self.driver.execute_script(js)
        slice_path = './slice.png'
        self.driver.find_element_by_xpath('.//div[@class="JDJRV-bigimg"]').screenshot(slice_path)
        time.sleep(1)
        #停止1秒后恢复JS改动,回到页面最初状态(双层图亦然)
        js = 'document.getElementsByClassName("JDJRV-smallimg")[0].style.display="block"'
        self.driver.execute_script(js)

隐藏滑块以后的效果:
在这里插入图片描述

  1. 将获得的验证码图片进行处理(灰度化、二值化、降噪)
    双层图可以直接进行两张图片的比较,而单层图则需要处理之后进行自身比较,本文为单层图事例
 	def shape(self, w, h, image):  # 二值化,将所有的点位,全部换成0或255
        tem = 0
        for x in range(w):
            for y in range(h):
                tem += image.getpixel((x, y))
        pixel_ave = tem / w / h * 0.7
        for x in range(w):
            for y in range(h):
                p = image.getpixel((x, y))
                if p < pixel_ave:
                    image.putpixel((x, y), 0)
                else:
                    image.putpixel((x, y), 255)
        return image

    def reducenoise(self, image):#降噪处理
        w, h = image.size
        for x in range(0, 40):  # 处理最左边
            for y in range(h):
                image2 = image.putpixel((x, y), 255)
        return image

    def make_picture(self):  # 处理图片,灰度化与二值化、降噪
        im = Image.open('slice.png')
        im2 = im.convert("L")
        w, h = im2.size
        im3 = self.shape(w, h, im2)
        im4 = self.reducenoise(im3)
        return im3

图片处理完以后的效果:
处理完的图片

  1. 计算验证图片左边边界到缺口左边边界的距离(即滑块拖动距离)
    def get_juli(self, image):  # 计算距离
        w, h = image.size
        ls = []
        for i in range(31, w - 31):#图片最左边放置滑块,缺口坐标x不可能小于31
            for j in range(10, h):
                if image.getpixel((i, j)) < 100:
                    count = 0
                    for k in range(i, i + 31):
                        if image.getpixel((k, j)) < 100:
                            count += 1
                        else:
                            break
                    if count > 27: ls.append(i)

        return Counter(ls).most_common(1)[0][0]
  1. 设计拖动轨迹(最难的点)
    我们要设计拖动滑块的规则,模拟人类操作,从而躲避电脑的验证
    我设计的时候有1/3-1/5的成功率,但是每天的成功率不一样,机器也在学习,我推荐大家也一起设计,可以在本文下方评论
    def get_track(self, distance):  # 设计拖动轨迹
        ls = [1]
        while 1:
            i = ls[-1] * 2
            ls.append(i)
            if sum(ls) > distance * 0.7:
                break

        ls.append(int(distance - sum(ls)))

        return ls
  1. 通过selenium执行拖动滑块的指令,实现验证登陆
    def drog_btn(self, track):  # 拖动滑块
    	#定位滑块
        ele = self.driver.find_element_by_xpath('.//div[@class="JDJRV-slide-inner JDJRV-slide-btn"]')
        #设计拖动动作链(点击且不放)
        ActionChains(self.driver).click_and_hold(ele).perform()
        #根据设计的轨迹,实现滑块拖动
        for i in track:
            ActionChains(self.driver).move_by_offset(i, 0).perform()
		#睡眠0.25秒,伪装成人的判断过程
        time.sleep(0.25)
        #释放滑块,类似于松开鼠标
        ActionChains(self.driver).release().perform()
        time.sleep(2)
  1. 判断是否成功,若失败则重试(哎呀,验证码哪会那么容易破解,JD也是厉害的公司,肯定反爬很强,总要实验嘛)
    def check(self):#再次尝试
        self.get_picture()
        image = self.make_picture()
        distance = self.get_juli(image)
        track = self.get_track(distance)
        self.drog_btn(track)
  1. 执行代码:
if __name__ == '__main__':
    login = JDlogin()
    login.get_picture()
    image = login.make_picture()
    distance = login.get_juli(image)
    track = login.get_track(distance)
    login.drog_btn(track)
    time_int = 0
    while time_int < 5:
        input("是否需要再次尝试")
        login.driver.refresh()
        login.check()
        time_int += 1

成功验证,实现登陆的图片:
验证成功

免责声明:本文仅作为技术分享,纯粹个人爱好,不从事任何商业行为与违法行为,也希望借鉴的人借鉴为主,遵纪守法。如果JD产生任何损失,本人概不负责,我只是尝试偷懒,不用手登陆,并无做出任何违法行为,如果有人以本文代码为由造成贵公司的损失,则需JD方自行寻找凶手,与我无关~O.O

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值