python爬虫处理滑块验证_python爬虫基础(9:验证识别之滑块验证)
上篇我们借助 tesserocr 库解决了图片码的识别验证,但在实际生活中,用得更多的是滑动验证,这篇就来解决滑动验证的问题
滑动验证
大部分网站都采用的是滑动验证,就是拖动滑块拼接图片,其中又大都采用极验(http://www.geetest.com/)所提供的技术,官方网页如下
本篇案例选用哔哩哔哩动画验证登录(https://passport.bilibili.com/login)
所需工具
chromedriver:浏览器驱动,可以理解为一个没有界面的chrome浏览器
selenium:用于模拟人对浏览器进行点击、输出、拖拽等操作,就相当于是个人在使用浏览器,也常常用来应付反爬虫措施,配合chromedriver使用,使用方法直接粘大神写的:https://cuiqingcai.com/5630.html
Image模块:提供很多对图片进行处理的方法的库,用法请查看https://www..com/kongzhagen/p/6295925.html
解决思路
1. 获取验证图片
通过访问登录页面,分析源码找到完整图片和带滑块缺口的图片 ,通过 selenium 键入登录信息
2. 获取缺口位置
通过对比原始的图片和带滑块缺口的图片的像素,计算出滑块缺口的位置,得到所需要滑动的距离
3.模拟拖动
利用selenium进行对滑块的拖拽,注意模仿人的行为:先快后慢,有个对准过程
案例步骤
第一大步:获取验证图片
1.初始化一些需要用到的参数
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
# 初始化
def init():
# 定义为全局变量,方便其他模块使用
global url, browser, username, password, wait
# 登录界面的url
url = 'https://passport.bilibili.com/login'
# 实例化一个chrome浏览器
browser = webdriver.Chrome()
# 用户名
username = '***********'
# 密码
password = '***********'
# 设置等待超时
wait = WebDriverWait(browser, 20)
2.通过 selenium 键入登录信息
(by the way:之前用 post 提交表单做过模拟登录,其实用selenium模拟人键入登录信息和点击提交也可以成功登录,但问题是selenium相率太低,所以一般能不用selenium就不用)
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# 登录
def login():
# 打开登录页面
browser.get(url)
# 获取用户名输入框
user = wait.until(EC.presence_of_element_located((By.ID, 'login-username')))
# 获取密码输入框
passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))
# 输入用户名
user.send_keys(username)
# 输入密码
passwd.send_keys(password)
3.获取验证图片
通过分析源码找到原始完整图片和带滑块缺口的图片
from urllib.request import urlretrieve
from bs4 import BeautifulSoup
import re
from PIL import Image
# 获取图片信息
def get_image_info(img):
'''
:param img: (Str)想要获取的图片类型:带缺口、原始
:return: 该图片(Image)、位置信息(List)
'''
# 将网页源码转化为能被解析的lxml格式
soup = BeautifulSoup(browser.page_source, 'lxml')
# 获取验证图片的所有组成片标签
imgs = soup.find_all('div', {'class': 'gt_cut_'+img+'_slice'})
# 用正则提取缺口的小图片的url,并替换后缀
img_url = re.findall('url\(\"(.*)\"\);', imgs[0].get('style'))[0].replace('webp', 'jpg')
# 使用urlretrieve()方法根据url下载缺口图片对象
urlretrieve(url=img_url, filename=img+'.jpg')
# 生成缺口图片对象
image = Image.open(img+'.jpg')
# 获取组成他们的小图片的位置信息
position = get_position(imgs)
# 返回图片对象及其位置信息
return image, position
但是这里有一个问题,验证图片是由两行许多的小图片组成的,而这些小图片不是按顺序排列的,这样就无法计算滑块缺口的位置了
因此我们上面的代码在有一个获取每个小图片正确位置信息的方法 get_position()
其代码如下:
# 获取小图片位置
def get_position(img):
'''
:param img: (List)存放多个小图片的标签
:return: (List)每个小图片的位置信息
'''
img_position = []
for small_img in img:
position = {}
# 获取每个小图片的横坐标
position['x'] = int(re.findall('background-position: (.*)px (.*)px;', small_img.get('style'))[0][0])
# 获取每个小图片的纵坐标
position['y'] = int(re.findall('background-position: (.*)px (.*)px;', small_img.get('style'))[0][1])
img_position.append(position)
return img_position
4.把这些小图片裁剪下来以方便重新拼成顺序正确的图片
from PIL import Image
# 裁剪图片
def Corp(image, position):
'''
:param image:(Image)被裁剪的图片
:param position: (List)该图片的位置信息
:return: (List)存放裁剪后的每个图片信息
'''
# 第一行图片信息
first_line_img = []
# 第二行图片信息
second_line_img = []
for pos in position:
if pos['y'] == -58:
first_line_img.append(image.crop((abs(pos['x']), 58, abs(pos['x']) + 10, 116)))
if pos['y'] == 0:
second_line_img.append(image.crop((abs(pos['x']), 0, abs(pos['x']) + 10, 58)))
return first_line_img, second_line_img
5.拼接处正确图片
按两行逐次拼接,使用 paste()方法按照位置信息得到正确图片
# 拼接大图
def put_imgs_together(first_line_img, second_line_img, img_name):
'''
:param first_line_img: (List)第一行图片位置信息
:param second_line_img: (List)第二行图片信息
:return: (Image)拼接后的正确顺序的图片
'''
# 新建一个图片,new()第一个参数是颜色模式,第二个是图片尺寸
image = Image.new('RGB', (260,116))
# 初始化偏移量为0
offset = 0
# 拼接第一行
for img in first_line_img:
# past()方法进行粘贴,第一个参数是被粘对象,第二个是粘贴位置
image.paste(img, (offset, 0))
# 偏移量对应增加移动到下一个图片位置,size[0]表示图片宽度
offset += img.size[0]
# 偏移量重置为0
x_offset = 0
# 拼接第二行
for img in second_line_img:
# past()方法进行粘贴,第一个参数是被粘对象,第二个是粘贴位置
image.paste(img, (x_offset, 58))
# 偏移量对应增加移动到下一个图片位置,size[0]表示图片宽度
x_offset += img.size[0]
# 保存图片
image.save(img_name)
# 返回图片对象
return image
此时,我们得到的就是位置正确的图片了
以上,第一大步 获取验证图片 就算是完成了
第二大步:获取缺口位置
1.计算缺口位置(即滑块需要滑动的距离)
# 计算滑块移动距离
def get_distance(bg_image, fullbg_image):
'''
:param bg_image: (Image)缺口图片
:param fullbg_image: (Image)完整图片
:return: (Int)缺口离滑块的距离
'''
# 滑块的初始位置
distance = 57
# 遍历像素点横坐标
for i in range(distance, fullbg_image.size[0]):
# 遍历像素点纵坐标
for j in range(fullbg_image.size[1]):
# 如果不是相同像素
if not is_pixel_equal(fullbg_image, bg_image, i, j):
# 返回此时横轴坐标就是滑块需要移动的距离
return i
这其中有个is_pixel_equal()方法用于判断是否为相同像素,从而判断是不是缺口位置
其代码如下:
# 判断像素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):
"""
:param bg_image: (Image)缺口图片
:param fullbg_image: (Image)完整图片
:param x: (Int)位置x
:param y: (Int)位置y
:return: (Boolean)像素是否相同
"""
# 获取缺口图片的像素点(按照RGB格式)
bg_pixel = bg_image.load()[x, y]
# 获取完整图片的像素点(按照RGB格式)
fullbg_pixel = fullbg_image.load()[x, y]
# 设置一个判定值,像素值之差超过判定值则认为该像素不相同
threshold = 60
# 判断像素的各个颜色之差,abs()用于取绝对值
if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):
# 如果差值在判断值之内,返回是相同像素
return True
else:
# 如果差值在判断值之外,返回不是相同像素
return False
第三大步:模拟拖动
1.构造模拟人类的滑块移动轨迹:先快后慢,有对准时间
# 构造滑动轨迹
def get_trace(distance):
'''
:param distance: (Int)缺口离滑块的距离
:return: (List)移动轨迹
'''
# 创建存放轨迹信息的列表
trace = []
# 设置加速的距离
faster_distance = distance*(4/5)
# 设置初始位置、初始速度、时间间隔
start, v0, t = 0, 0, 0.2
# 当尚未移动到终点时
while start < distance:
# 如果处于加速阶段
if start < faster_distance:
# 设置加速度为2
a = 1.5
# 如果处于减速阶段
else:
# 设置加速度为-3
a = -3
# 移动的距离公式
move = v0 * t + 1 / 2 * a * t * t
# 此刻速度
v = v0 + a * t
# 重置初速度
v0 = v
# 重置起点
start += move
# 将移动的距离加入轨迹列表
trace.append(round(move))
# 返回轨迹信息
return trace
2.利用selenium拖拽滑块
# 模拟拖动
def move_to_gap(trace):
# 得到滑块标签
slider = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'gt_slider_knob')))
# 使用click_and_hold()方法悬停在滑块上,perform()方法用于执行
ActionChains(browser).click_and_hold(slider).perform()
for x in trace:
# 使用move_by_offset()方法拖动滑块,perform()方法用于执行
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
# 模拟人类对准时间
sleep(0.5)
# 释放滑块
ActionChains(browser).release().perform()
结果展示:
程序结构图
完整代码
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from urllib.request import urlretrieve
from bs4 import BeautifulSoup
import re
from PIL import Image
from time import sleep
# 初始化
def init():
# 定义为全局变量,方便其他模块使用
global url, browser, username, password, wait
# 登录界面的url
url = 'https://passport.bilibili.com/login'
# 实例化一个chrome浏览器
browser = webdriver.Chrome()
# 用户名
username = '***********'
# 密码
password = '***********'
# 设置等待超时
wait = WebDriverWait(browser, 20)
# 登录
def login():
# 打开登录页面
browser.get(url)
# 获取用户名输入框
user = wait.until(EC.presence_of_element_located((By.ID, 'login-username')))
# 获取密码输入框
passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))
# 输入用户名
user.send_keys(username)
# 输入密码
passwd.send_keys(password)
# 获取图片信息
def get_image_info(img):
'''
:param img: (Str)想要获取的图片类型:带缺口、原始
:return: 该图片(Image)、位置信息(List)
'''
# 将网页源码转化为能被解析的lxml格式
soup = BeautifulSoup(browser.page_source, 'lxml')
# 获取验证图片的所有组成片标签
imgs = soup.find_all('div', {'class': 'gt_cut_'+img+'_slice'})
# 用正则提取缺口的小图片的url,并替换后缀
img_url = re.findall('url\(\"(.*)\"\);', imgs[0].get('style'))[0].replace('webp', 'jpg')
# 使用urlretrieve()方法根据url下载缺口图片对象
urlretrieve(url=img_url, filename=img+'.jpg')
# 生成缺口图片对象
image = Image.open(img+'.jpg')
# 获取组成他们的小图片的位置信息
position = get_position(imgs)
# 返回图片对象及其位置信息
return image, position
# 获取小图片位置
def get_position(img):
'''
:param img: (List)存放多个小图片的标签
:return: (List)每个小图片的位置信息
'''
img_position = []
for small_img in img:
position = {}
# 获取每个小图片的横坐标
position['x'] = int(re.findall('background-position: (.*)px (.*)px;', small_img.get('style'))[0][0])
# 获取每个小图片的纵坐标
position['y'] = int(re.findall('background-position: (.*)px (.*)px;', small_img.get('style'))[0][1])
img_position.append(position)
return img_position
# 裁剪图片
def Corp(image, position):
'''
:param image:(Image)被裁剪的图片
:param position: (List)该图片的位置信息
:return: (List)存放裁剪后的每个图片信息
'''
# 第一行图片信息
first_line_img = []
# 第二行图片信息
second_line_img = []
for pos in position:
if pos['y'] == -58:
first_line_img.append(image.crop((abs(pos['x']), 58, abs(pos['x']) + 10, 116)))
if pos['y'] == 0:
second_line_img.append(image.crop((abs(pos['x']), 0, abs(pos['x']) + 10, 58)))
return first_line_img, second_line_img
# 拼接大图
def put_imgs_together(first_line_img, second_line_img, img_name):
'''
:param first_line_img: (List)第一行图片位置信息
:param second_line_img: (List)第二行图片信息
:return: (Image)拼接后的正确顺序的图片
'''
# 新建一个图片,new()第一个参数是颜色模式,第二个是图片尺寸
image = Image.new('RGB', (260,116))
# 初始化偏移量为0
offset = 0
# 拼接第一行
for img in first_line_img:
# past()方法进行粘贴,第一个参数是被粘对象,第二个是粘贴位置
image.paste(img, (offset, 0))
# 偏移量对应增加移动到下一个图片位置,size[0]表示图片宽度
offset += img.size[0]
# 偏移量重置为0
x_offset = 0
# 拼接第二行
for img in second_line_img:
# past()方法进行粘贴,第一个参数是被粘对象,第二个是粘贴位置
image.paste(img, (x_offset, 58))
# 偏移量对应增加移动到下一个图片位置,size[0]表示图片宽度
x_offset += img.size[0]
# 保存图片
image.save(img_name)
# 返回图片对象
return image
# 判断像素是否相同
def is_pixel_equal(bg_image, fullbg_image, x, y):
"""
:param bg_image: (Image)缺口图片
:param fullbg_image: (Image)完整图片
:param x: (Int)位置x
:param y: (Int)位置y
:return: (Boolean)像素是否相同
"""
# 获取缺口图片的像素点(按照RGB格式)
bg_pixel = bg_image.load()[x, y]
# 获取完整图片的像素点(按照RGB格式)
fullbg_pixel = fullbg_image.load()[x, y]
# 设置一个判定值,像素值之差超过判定值则认为该像素不相同
threshold = 60
# 判断像素的各个颜色之差,abs()用于取绝对值
if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):
# 如果差值在判断值之内,返回是相同像素
return True
else:
# 如果差值在判断值之外,返回不是相同像素
return False
# 计算滑块移动距离
def get_distance(bg_image, fullbg_image):
'''
:param bg_image: (Image)缺口图片
:param fullbg_image: (Image)完整图片
:return: (Int)缺口离滑块的距离
'''
# 滑块的初始位置
distance = 57
# 遍历像素点横坐标
for i in range(distance, fullbg_image.size[0]):
# 遍历像素点纵坐标
for j in range(fullbg_image.size[1]):
# 如果不是相同像素
if not is_pixel_equal(fullbg_image, bg_image, i, j):
# 返回此时横轴坐标就是滑块需要移动的距离
return i
# 构造滑动轨迹
def get_trace(distance):
'''
:param distance: (Int)缺口离滑块的距离
:return: (List)移动轨迹
'''
# 创建存放轨迹信息的列表
trace = []
# 设置加速的距离
faster_distance = distance*(4/5)
# 设置初始位置、初始速度、时间间隔
start, v0, t = 0, 0, 0.2
# 当尚未移动到终点时
while start < distance:
# 如果处于加速阶段
if start < faster_distance:
# 设置加速度为2
a = 1.5
# 如果处于减速阶段
else:
# 设置加速度为-3
a = -3
# 移动的距离公式
move = v0 * t + 1 / 2 * a * t * t
# 此刻速度
v = v0 + a * t
# 重置初速度
v0 = v
# 重置起点
start += move
# 将移动的距离加入轨迹列表
trace.append(round(move))
# 返回轨迹信息
return trace
# 模拟拖动
def move_to_gap(trace):
# 得到滑块标签
slider = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'gt_slider_knob')))
# 使用click_and_hold()方法悬停在滑块上,perform()方法用于执行
ActionChains(browser).click_and_hold(slider).perform()
for x in trace:
# 使用move_by_offset()方法拖动滑块,perform()方法用于执行
ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
# 模拟人类对准时间
sleep(0.5)
# 释放滑块
ActionChains(browser).release().perform()
# 主程序
def main():
# 初始化
init()
# 登录
login()
# 获取缺口图片及其位置信息
bg, bg_position = get_image_info('bg')
# 获取完整图片及其位置信息
fullbg, fullbg_position = get_image_info('fullbg')
# 将混乱的缺口图片裁剪成小图,获取两行的位置信息
bg_first_line_img, bg_second_line_img = Corp(bg, bg_position)
# 将混乱的完整图片裁剪成小图,获取两行的位置信息
fullbg_first_line_img, fullbg_second_line_img = Corp(fullbg, fullbg_position)
# 根据两行图片信息拼接出缺口图片正确排列的图片
bg_image = put_imgs_together(bg_first_line_img, bg_second_line_img, 'bg.jpg')
# 根据两行图片信息拼接出完整图片正确排列的图片
fullbg_image = put_imgs_together(fullbg_first_line_img, fullbg_second_line_img, 'fullbg.jpg')
# 计算滑块移动距离
distance = get_distance(bg_image, fullbg_image)
# 计算移动轨迹
trace = get_trace(distance-10)
# 移动滑块
move_to_gap(trace)
sleep(5)
# 程序入口
if __name__ == '__main__':
main()
github: https://github.com/JeesonZhang/pythonspider/blob/master/bilibili_crack
python爬虫处理滑块验证_python爬虫基础(9:验证识别之滑块验证)相关推荐
- python通过ip池爬_python 爬虫 代理ip池(适合初学者)
初次学习python爬虫的朋友在频繁访问被爬取页面网站时都会被拦截,也就是限制ip.这里教教大家建立代理ip池. #!/usr/bin/env python3# -*- coding: utf-8 - ...
- python运势预测程序_Python 爬虫系列之一——每日星座运势
开个新坑--Python 爬虫系列,最近脑子里有很多非常有趣的想法,但实现起来都需要一些简单的爬虫知识,如果放在趣学 Python 系列,会显得文章太长,干脆拿出来开个新坑.这个系列本质上是为趣学 P ...
- python爬虫微博评论图片_python爬虫爬取微博评论
原标题:python爬虫爬取微博评论 python爬虫是程序员们一定会掌握的知识,练习python爬虫时,很多人会选择爬取微博练手.python爬虫微博根据微博存在于不同媒介上,所爬取的难度有差异,无 ...
- python爬取收费素材_Python爬虫练习:爬取素材网站数据
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 在工作中的电子文案.ppt,生活中的新闻.广告,都离不开大量的素材,而素材 ...
- python爬虫抓取房产_Python爬虫实战(3):安居客房产经纪人信息采集
1, 引言 Python开源网络爬虫项目启动之初,我们就把网络爬虫分成两类:即时爬虫和收割式网络爬虫.为了使用各种应用场景,该项目的整个网络爬虫产品线包含了四类产品,如下图所示: 本实战是上图中的&q ...
- python爬虫爬取图片代码_Python爬虫入门:批量爬取网上图片的两种简单实现方式——基于urllib与requests...
Python到底多强大,绝对超乎菜鸟们(当然也包括我了)的想象.近期我接触到了爬虫,被小小地震撼一下.总体的感觉就两个词--"强大"和"有趣".今天就跟大家分享 ...
- list @size 验证_python爬虫21 | 对于b站这样的滑动验证码,不好意思,照样自动识别...
今天 要来说说滑动验证码了 大家应该都很熟悉 点击滑块然后移动到图片缺口进行验证 现在越来越多的网站使用这样的验证方式 为的是增加验证码识别的难度 那么 对于这种验证码 应该怎么破呢 接下来就是 学习 ...
- 基于python爬虫的论文标题_python爬虫——简单论文标题检索-Go语言中文社区
有趣的爬虫,独有的意义召唤着我去学习,去尝试.最近有感于每天对于论文的收集,感觉自己的收集速度赶不上论文的更新速度,同时对于自己想找到的论文的收集比较麻烦.因此,学习用python写一个很简单的爬虫, ...
- python打开网页被禁止_Python爬虫被禁?看看是不是这几个问题
Python爬虫在网上完成网站的信息采集时,常常出现无缘无故的ip被禁的情况,正爬取呢就没法继续了,造成日常业务也没办法正常进行了,整个人都不好了呢.一部分人完全不清楚被禁的原因,这么简单的就给禁掉了 ...
- python爬虫快速下载图片_Python爬虫入门:批量爬取网上图片的两种简单实现方式——基于urllib与requests...
Python到底多强大,绝对超乎菜鸟们(当然也包括我了)的想象.近期我接触到了爬虫,被小小地震撼一下.总体的感觉就两个词--"强大"和"有趣".今天就跟大家分享 ...
最新文章
- opengl正方形绕点旋转_一题十五种解法够不够? 旋转,构造,四点共圆乐不停...
- swing下拉框从mysql_Java Swing JComboBox下拉列表框的示例代码
- python实现登录网站下载文件-Python爬虫 登录网页后下载图片,怎么保持登录状态?...
- win8计算机安全模式,Win8.1怎么进入安全模式 两种进入Win8.1安全模式方法介绍
- python shape函数_Perlin噪声和Python的ctypes
- Spring Boot——一种包含分页和排序参数的接收方法DEMO
- Storm任务提交过程及目录树介绍
- sas java 虚拟机异常_深入理解JAVA虚拟机之异常诊断
- 菜鸟的学习之路(10) — LinkedList类
- Rust语言开发基础(二)开发环境安装
- PHP审核接口基本判断
- 迎接基础软件创新的春天
- python的sysfont_pygame.font.SysFont游戏文字交互
- 赴美工作常识(Part 5 - 绿卡优先级)
- CentOS 6.x 使用包管理器安装配置Apache+PHP+MySql
- 一个创业者的自白:假如重回华为怎么做?
- 一个人怎么写大型软件
- tm项目和fp项目_TM软件项目介绍
- 定时任务-动态定时任务(springboot+org.quartz)
- C#字符串转换为ASCII码