基于selenium自动化的滑动验证码破解

2019-07-30 14:47:47 浏览数 (1)

常见的滑动验证码就是哔哩哔哩了,就以哔哩哔哩作为例子

1、拿到完整图片

2、拿到有缺口的图片

3、计算滑动距离

4、滑动(如何避免被识别出来)

当鼠标放在上面不点击的时候,就会产生完整的图

当点击的时候就会出现有缺口的图片

在图片上右击检查,就会到图片的位置

然后打开图片的URL,你会发现这是一个乱序的图片

如何解决这个问题,

第一种解决办法:

就是将获取到的图片的按照前端的顺序,重新拼接

代码语言:javascript复制
def get_merge_image(self, filename, location_list):
    """
    合并图像,并还原图像
    :return:
    """
    im = image.open(filename)
    new_im = image.new('RGB', (260, 116))  # width 260px, height 116px
    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)))

    x_offset = 0
    for im in im_list_upper:
        new_im.paste(im, (x_offset, 0))
        x_offset  = im.size[0]
    x_offset = 0
    for im in im_list_down:
        new_im.paste(im, (x_offset, 58))
        x_offset  = im.size[0]
    new_im.save(filename)
    time.sleep(1)
    # 将图片碎片组合到复制到新图片上 
    return new_im

第二种:

截图,分别截图,完整的时候截一次图(截图全屏),不用管是否乱序,获取图片的坐标,将截图再次按照坐标截图处理,这样就能获得完整的图和有缺口的图。

代码语言:javascript复制
def get_image(driver):
    img = driver.find_element_by_class_name('gt_cut_fullbg')
    time.sleep(2)
    location = img.location
    size = img.size
    left = location['x']
    top = location['y']
    right = left   size['width']
    bottom = top   size['height']

    page_snap_obj = get_snap(driver)
    image_obj = page_snap_obj.crop((left, top, right, bottom))
    # image_obj.show()
    return image_obj
代码语言:javascript复制
def get_snap(driver):
    driver.save_screenshot('full_snap.png')
    page_snap_obj = Image.open('full_snap.png')
    return page_snap_obj

使用PIL裁切图片

使用PIL需要引用Image,使用Image的open(file)方法可以返回打开的图片,使用crop((x0,y0,x1,y1))方法可以对图片做裁切。

区域由一个4元组定义,表示为坐标是 (left, upper, right, lower),Python Imaging Library 使用左上角为 (0, 0)的坐标系统

box(100,100,200,200)就表示在原始图像中以左上角为坐标原点,截取一个100*100(像素为单位)的图像,为方便理解,如下为示意图box(b1,a1,b2,a2)

这样我们就拿到了图片一个是完整的,一个是有缺口的。

第三步就是计算要滑动的距离了

代码语言:javascript复制
def is_pixel_equal(self, img1, img2, x, y):
    """
    判断两个像素是否相同
    :param img1:
    :param img2:
    :param x: 位置x
    :param y: 位置y
    :return:
    """
    # 取两张图片的像素点
    pix1 = img1.load()[x, y]
    pix2 = img2.load()[x, y]
    threshold = 60
    if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(
        pix1[2] - pix2[2] < threshold)):
        return True
    else:
        return False
代码语言:javascript复制
def get_pag(self, img1, img2):
    """
    获取偏移量
    :param img1:不带缺口
    :param img2:带缺口
    :return:
    """
    left = 43
    for 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 = i
                return left
    return left

得到了滑动的距离就是计算如何去滑动了

代码语言:javascript复制
def get_track(self, distance):
    """
    根据偏移量获取移动轨迹
    :param distance:
    :return:
    """
    distance  = 24
    # 移动轨迹
    track = []
    # 当前位移
    current = 0
    # 减速阀值
    mid = distance * 3 / 5
    # 计算阀值
    t = 0.2
    # 计算初速度
    v = 0
    while current < distance:
        if current < mid:
            # 加速度
            a = 2
        else:
            # 加速度为-3
            a = -3
        # 初速度为
        v0 = v
        # 移动距离x = v0t 1/2*a^2
        move = v0*t   1/2*a*t*t
        # 当前位移
        current  = move
        # 加入轨迹
        track.append(round(move))
        # 速度已到达v,该速度作为下次的初速度
        v = v0   a*t
    # track = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]
    return track
代码语言:javascript复制
def get_track(self, x_offset):

        track = list()
        length = x_offset - 6
        x = random.randint(1,5)
        while length - x >4:
            track.append([x,0,0])
            length=length - x
            x= random.randint(1,15)

        for i in range(length):
            if x_offset>47:
                track.append([1,0,random.randint(10,12)/100.0])
            else:
                track.append([1, 0, random.randint(13, 14)/100.0])
        return track

上面是两种获取移动轨迹的方式,大家可以自己试一下,那个效率更高。

最后就是控制滑块去滑动了

代码语言:javascript复制
def 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)
    tracks = [-3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1]
    time.sleep(0.5)

    for track in tracks:
        ActionChains(self.browser).move_by_offset(xoffset=track, yoffset=0).perform()
    # 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率
    ActionChains(self.browser).move_by_offset(xoffset=-1, yoffset=0).perform()
    ActionChains(self.browser).move_by_offset(xoffset=1, yoffset=0).perform()

    time.sleep(0.5)
    ActionChains(self.browser).release().perform()
代码语言:javascript复制
def simulate_drag(self, track):

        dom_div_slider = self.browser.find_element_by_xpath('//*[@id="gc-box"]/div/div[3]/div[2]')

        ActionChains(self.browser).click_and_hold(on_element=dom_div_slider).perform()

        for x,y,z in track:
            ActionChains(self.browser).move_to_element_with_offset(
                to_element=dom_div_slider,
                xoffset=x 22,
                yoffset=y 22).perform()
            time.sleep(z)

        time.sleep(0.9)
        ActionChains(self.browser).release(on_element=dom_div_slider).perform()
        time.sleep(1)
        dom_div_gt_info = self.browser.find_element_by_class_name('gt_info_type')
        return dom_div_gt_info.text

试试两种不同的滑动方式有什么不同

方法都给大家了,大家可以自己多试试,找出最高效的滑动方式,

如果碰到其他的滑动验证码只需修改元素的定位,思路一样

关于ActionChains,不懂的就去查查

最后源码地址:https://github.com/AndrewAndrea/check_img_code

不要忘了star, 谢谢!

有问题可以留言

抖音剩下的下次的更新,如何破解api,还有appnium 的使用。

0 人点赞