爬虫:滑动验证解决方法及python实现

摘要:
当爬虫遇到滑动验证时,基本思想是使用selenium操作浏览器,将滑动验证的原始图像与缺口图像进行比较,找出缺口的位置,然后使用seleniun模拟拖动滑块,以达到验证的目的。点击按钮后,滑动验证码出现:滑动验证图像没有原始图像,缺口图像直接出现。观察图像标签并找到标签。如果标签上设置的显示属性设置为无,则验证图将更改为下图:拼接的小框被隐藏,这使我们更容易与原始图像进行比较。

爬虫时遇到滑动验证,基本思路是通过selenium操作浏览器,将滑动验证的原始图片和缺口图片进行对比,找出缺口位置,然后在利用selenium模拟拖动滑块,达到验证的目的。下面就以猪八戒网为例,进行操作。

一、分析

首先访问 https://account.zbj.com/login:

爬虫:滑动验证解决方法及python实现第1张

登陆页面主要为上图。

点击按钮(div标签,类名为 geetest_radar_tip)后,出现滑动验证码:

 爬虫:滑动验证解决方法及python实现第2张

  这个滑动验证图片并没有原始图片,直接出现的就是缺口图片。而我们要找的就是深色缺口的位置。

观察图片标签,找到   <canvas height="160"></canvas>这个标签,如果把该标签设置的display属性设置为none,则验证图则会变为下图:

爬虫:滑动验证解决方法及python实现第3张

 拼接的小方块隐藏掉了,这张图片更容易让我们与原图比对。

然后再找到<canvas style="display: none; opacity: 1;"></canvas>这个标签,将display设置为block:

爬虫:滑动验证解决方法及python实现第4张

 原图显示出来,我们可以将上面两张图片的滑动验证图片进行截取,比对,即可找到缺口位置。

实现过程中要注意几点:

1.selenium在加载后,不要立即寻找标签,因为如果标签没有加载完成,会找不到,这样就会抛出错误。

2.截图的大小可以跟浏览器的内显示的大小不一样,需要计算浏览器显示大小与截图大小的比例。

3.进行滑动时,要先加速后减速,如果一直匀速,会被网站识别。

 二、实现

 1、导入相关要用的包:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from PIL import Image
from six import BytesIO
import time
from selenium.webdriver import ActionChains

2.主函数:

if __name__ == '__main__':
    browser = get_url('https://account.zbj.com/login','11111111111','********')  #此函数的定义在第3点
    time.sleep(1)
    slice_img_label = browser.find_element_by_css_selector('div.geetest_slicebg') #找到滑动图片标签
    browser.execute_script("document.getElementsByClassName('geetest_canvas_slice')[0].style['display'] = 'none'") #将小块隐藏
    full_img_label = browser.find_element_by_css_selector('canvas.geetest_canvas_fullbg') #原始图片的标签
    position = get_position(slice_img_label) #获取滑动验证图片的位置,此函数的定义在第4点
    screenshot = get_screenshot(browser) # 截取整个浏览器图片,此函数的定义在第5点
    position_scale = get_position_scale(browser,screenshot) #获取截取图片宽高和浏览器宽高的比例,此函数的定义在第6点
    slice_img = get_slideimg_screenshot(screenshot,position,position_scale) #截取有缺口的滑动验证图片,此函数的定义在第7点
   

    browser.execute_script("document.getElementsByClassName('geetest_canvas_fullbg')[0].style['display'] = 'block'") #在浏览器中显示原图
    screenshot = get_screenshot(browser) #获取整个浏览器图片
    full_img = get_slideimg_screenshot(screenshot,position,position_scale) # 截取滑动验证原图
    browser.execute_script("document.getElementsByClassName('geetest_canvas_slice')[0].style['display'] = 'block'")  #将小块重新显示
    left = compare(full_img,slice_img) #将原图与有缺口图片进行比对,获得缺口的最左端的位置,此函数定义在第8点
    left = left / position_scale[0] #将该位置还原为浏览器中的位置

    slide_btn = browser.find_element_by_css_selector('.geetest_slider_button') #获取滑动按钮
    track = get_track(left) #获取滑动的轨迹,此函数定义在第9点
    move_to_gap(browser,slide_btn,track) #进行滑动,此函数定义在第10点
    success = browser.find_element_by_css_selector('.geetest_success_radar_tip') #获取显示结果的标签
    time.sleep(2)
    if success.text == "验证成功":
        login_btn = browser.find_element_by_css_selector('button.j-login-btn') #如果验证成功,则点击登录按钮
        login_btn.click()
    else:
        print(success.text)
        print('失败') 

下面是主函数中用到的各个功能性函数:

3..定义访问页面的函数:

def get_url(url,user,password):
    browser = webdriver.Chrome()
    browser.get(url)
    browser.maximize_window()
    wait = WebDriverWait(browser,10)
    wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_radar_btn')))
    user_input = browser.find_element_by_id('username')
    pwd_input = browser.find_element_by_id('password')
    btn = browser.find_element_by_css_selector('.geetest_radar_btn')
    user_input.send_keys(user)
    pwd_input.send_keys(password)
    btn.click()
    time.sleep(0.5)
    return browser

此函数主要是用于启动Chrome,打开网页,将用户名和密码填入相应位置,并点击验证按钮。

4.获取滑动验证图片在浏览器的位置。

使用location是获取标签左上角的位置,然后再通过该标签的大小,即可算出其四个角的位置。

def get_position(img_label):
    location = img_label.location
    size = img_label.size
    top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
        'width']
    return (left, top, right, bottom)

 5.获取整个浏览器的截图。并从内存进行读取。 

def get_screenshot(browser):
    screenshot = browser.get_screenshot_as_png()
    f = BytesIO()
    f.write(screenshot)
    return Image.open(f)

 6.通过对比截图和浏览器宽高的大小,算出换算比例。

由于截图是有浏览器的边缘的拖拽条,所以浏览器的宽度+10px

def get_position_scale(browser,screen_shot):
    height = browser.execute_script('return document.documentElement.clientHeight')
    width = browser.execute_script('return document.documentElement.clientWidth')
    x_scale = screen_shot.size[0] / (width+10)
    y_scale = screen_shot.size[1] / (height)
    return (x_scale,y_scale)

 7.截取有缺口的滑动图片:

def get_slideimg_screenshot(screenshot,position,scale):
    x_scale,y_scale = scale
    position = [position[0] * x_scale, position[1] * y_scale, position[2] * x_scale, position[3] * y_scale]
  return screenshot.crop(position)

 8.将原始图片和有缺口的图片进行比较:

def compare_pixel(img1,img2,x,y):
    pixel1 = img1.load()[x,y]
    pixel2 = img2.load()[x,y]
    threshold = 50
    if abs(pixel1[0]-pixel2[0])<=threshold:
        if abs(pixel1[1]-pixel2[1])<=threshold:
            if abs(pixel1[2]-pixel2[2])<=threshold:
                return True
    return False


def compare(full_img,slice_img):
    left = 0
    for i in range(full_img.size[0]):
        for j in range(full_img.size[1]):
            if not compare_pixel(full_img,slice_img,i,j):
                return i
    return left

 9.计算出滑动的轨迹,其实就是简单的s = 1/2*a*t*t的简单公式。这部分代码,直接用的崔庆才博主的代码:

def get_track(distance):
    """
    根据偏移量获取移动轨迹
    :param distance: 偏移量
    :return: 移动轨迹
    """
    # 移动轨迹
    track = []
    # 当前位移
    current = 0
    # 减速阈值
    mid = distance * 4 / 5
    # 计算间隔
    t = 0.2
    # 初速度
    v = 0

    while current < distance:
        if current < mid:
            # 加速度为正 2
            a = 4
        else:
            # 加速度为负 3
            a = -3
        # 初速度 v0
        v0 = v
        # 当前速度 v = v0 + at
        v = v0 + a * t
        # 移动距离 x = v0t + 1/2 * a * t^2
        move = v0 * t + 1 / 2 * a * t * t
        # 当前位移
        current += move
        # 加入轨迹
        track.append(round(move))
    return track

 10.进行移动:

def move_to_gap(browser,slider, tracks):
    """
    拖动滑块到缺口处
    :param slider: 滑块
    :param tracks: 轨迹
    :return:
    """
    ActionChains(browser).click_and_hold(slider).perform()
    for x in tracks:
        ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
    time.sleep(0.5)
    ActionChains(browser).release().perform()

以上3-10是定义的功能性函数,放在前面,2是主函数,放在后面。运行即可。也可以将其写为一个类,使用时更为方便。此处,只是为实现功能,所以就没有进一步进行规范。

免责声明:文章转载自《爬虫:滑动验证解决方法及python实现》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Vue.set的使用groovy脚本操作sql下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Echarts配置项详解

1.图表标题 title: { x: 'left', // 水平安放位置,默认为左对齐,可选为: // 'center' ¦ 'left' ¦ 'right' // ¦...

CSS定位

css定位css有三种基本的定位机制:普通流,浮动和绝对定位。css中position属性static,relative,absolute,fixed      position:relative相对定位会按照元素的原始位置对该元素进行移动      position:absolute绝对定位,元素可以放置到页面上的任何位置,后面加上left,top等值 ...

3D画廊

3D画廊 之前我都是写的学习的内容,我在写这些教程时遇到有趣的炫酷的小例子也会专门拿出来写一篇文章,今天就写一个酷炫的小例子,叫3D画廊,它是属于ViewPage的进阶版。 此项目下载地点:https://github.com/qySvip/3D-gallery 下面的指示器是使用的一大神的第三方库,会在文章下方简单讲述一下。 效果图 3D画廊的实现 首...

Unity 学习笔记5

一:绘制线段:为物体添加linerendenerer组件,在脚本中获得该组件的引用后,增加position数组大小,设置position数组数值即可绘制折线。在脚本中获取了Linerenderer组件后,可以使用linerenderer.SetVertexCount (int numberOfPosition);方法动态的为position的size赋值,然...

VSCode插件开发全攻略(五)跳转到定义、自动补全、悬停提示

更多文章请戳VSCode插件开发全攻略系列目录导航。 跳转到定义 跳转到定义其实很简单,通过vscode.languages.registerDefinitionProvider注册一个provider,这个provider如果返回了new vscode.Location()就表示当前光标所在单词支持跳转,并且跳转到对应location。 为了示例更加有意...

js开启和关闭页面滚动【亲测有效】

在移动端的页面开发过程中,经常会遇到点击弹框禁止页面滚动的情景,下面就来说下具体的做法。。。 第一步:构建一个函数 function bodyScroll(event){ event.preventDefault(); } 第二步:点击禁止页面滚动 $(".button").click(function(){ document.body....