Python爬虫:Bilibili模拟登陆(滑动验证码)项目
Posted 黑马程序员官方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python爬虫:Bilibili模拟登陆(滑动验证码)项目相关的知识,希望对你有一定的参考价值。
文章目录
- 1. 为什么要处理滑动验证码
- 2. 目标网站
- 3. 项目的开发环境
- 4. 项目流程介绍
- 5. bilibili模拟登陆-初始化
- 6. bilibili模拟登陆-请求bilibili的登录页面&模拟输入账号密码
- 7. bilibili模拟登陆-获取验证码图片
- 8. bilibili模拟登陆-比较两个验证码图片获取验证码滑块的偏移量
- 9. bilibili模拟登陆-使用偏移值计算移动操作
- 10.bilibili模拟登陆-拖动滑块
1. 为什么要处理滑动验证码
在很多时候我们在做模拟登陆的时候会遇到滑动验证码,这个时候就必须要处理
2. 目标网站
bilibili视频网站的滑动验证码
经过分析我们知道bilibili使用的是极验的拖动验证码
3. 项目的开发环境
本项目需要用到 selenium,io,PIL,time,random,请提前安装
请安装对应版本的库如下,其他库均为标准库,无需安装 pip3 install Pillow4.2.1 pip3 install selenium3.12.0
4. 项目流程介绍
- 初始化
- 请求bilibili的登录页面&模拟输入账号密码
- 获取验证码图片&有阴影拼图的验证码图片
- 比较两个验证码图片获取验证码滑块的偏移量
- 使用偏移值计算移动操作
- 操作滑块按钮,模拟拖动滑块做验证登录
5. bilibili模拟登陆-初始化
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
def __init__(self):
# 设置登录页面url
self.url = 'https://passport.bilibili.com/login'
# 创建浏览器对象
self.driver = webdriver.Chrome()
# 设置页面的隐式等待
self.driver.implicitly_wait(3)
# 设置账号密码
self.user = '18668928175'
self.pwd = '461324morganna'
def login(self):
"""
实现主要的登录逻辑
"""
pass
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
6. bilibili模拟登陆-请求bilibili的登录页面&模拟输入账号密码
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
#......
def input_user_pwd(self):
"""
请求登录页面输入账号密码
"""
# 请求登录页面
self.driver.get(self.url)
# 输入账号
el_user = self.driver.find_element_by_id('login-username')
el_user.send_keys(self.user)
# 输入密码
el_user = self.driver.find_element_by_id('login-passwd')
el_user.send_keys(self.pwd)
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
7. bilibili模拟登陆-获取验证码图片
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
#......
def get_screenshot(self):
"""
获取屏幕截图,截图结果使用Image打开,打开之后的对象可以用来从中截取出验证码图片
"""
# 截屏
screenshot = self.driver.get_screenshot_as_png()
# 打开图片
screenshot = Image.open(BytesIO(screenshot))
return screenshot
def get_position(self):
"""
获取图片验证码在屏幕截图上的位置
"""
# 点击锁按钮才能查看没有阴影的验证码
el_lock = self.driver.find_element_by_xpath('//div[@class="gt_ajax_tip gt_ready"]')
el_lock.click()
# 获取图片验证码元素
img = self.driver.find_element_by_xpath('//div[@class="gt_cut_fullbg gt_show"]')
time.sleep(3)
# 获取验证码在屏幕截图上的坐标
location = img.location
# 获取验证码图片的的尺寸
size = img.size
# 使用图片坐标和图片尺寸计算出整个图片的区域,这里所有的计算都乘以2倍,需要注意的是,在我机器上因为分辨率的问题才这样做的,请根据自身机器的分辨率决定是否
left, top, right, button = 2 * location['x'], 2 * location['y'], 2 * (location['x'] + size['width']), 2 * (location['y'] + size['height'])
return left, top, right, button
def get_image(self):
# 获取截图用的坐标元组
position = self.get_position()
# 屏幕截图
screenshot1 = self.get_screenshot()
# 将不带阴影的验证码图片抠出
captcha1 = screenshot1.crop(position)
with open('captcha1.png', 'wb')as f:
captcha1.save(f)
# 点击拖动按钮,出现验证码图片用于截图
button = self.get_button()
button.click()
# 点击之后会出现错误提示信息,等待一定时间之后提示信息消失再截图
time.sleep(4)
# 屏幕截图
screenshot2 = self.get_screenshot()
# 将验证码图片抠出
captcha2 = screenshot2.crop(position)
with open('captcha2.png', 'wb')as f:
captcha2.save(f)
return captcha1, captcha2
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
# 获取两个图片对象
img1, img2 = self.get_image()
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
8. bilibili模拟登陆-比较两个验证码图片获取验证码滑块的偏移量
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
#......
def is_pixel_equal(self, image1, image2, x, y):
"""
比较两张图片相同位置的像素的是否相似
"""
# 获取两个验证码图片在x,y这个点的rgb的颜色
pixel1 = image1.load()[x, y]
pixel2 = image2.load()[x, y]
# 设置比较值
threshold = 60
print(pixel1, pixel2)
# 分别获取两个验证码同一像素差值的绝对值,然后再和比较值
if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:
return True
else:
return False
def get_gap(self, image1, image2):
# 该值用于跳过验证码滑块,因为验证码滑块有阴影会影响结果,所以直接设置跳过验证码滑块
left = 120
# 遍历验证码的x轴
for i in range(left, image1.size[0]):
# 确定x之后遍历y,获取一个坐标
for j in range(image1.size[1]):
# 判断相似度,超过设定的相似度则表示找到拼图阴影
if not self.is_pixel_equal(image1, image2, i, j):
left = i
# left为滑块移动的像素,因为分辨率的问题,我的机器上需要除2才能得到真实拖动距离,-6是阴影区域的修正值,也可根据自身环境决定是否使用
return round(left/2 - 6)
return left
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
# 获取两个图片对象
img1, img2 = self.get_image()
# 获取拼图阴影位置
offset = self.get_gap(img1, img2)
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
9. bilibili模拟登陆-使用偏移值计算移动操作
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
#......
def get_track(self, offset):
"""
直接将滑块瞬间拖动到阴影会被极验的后台识别为机器操作,所以这里仿照人类的拖动验证码的情形模拟操作,一般来说人类拖动验证码的时候是先加速后减速,最后可能做微调
"""
track = []
# 当前距离
current = 0
# 设置加减速的中间值
mid = offset * 9/10
# 设置时间渐渐个
t = 0.3
# 设置速度
v = 0
# 当滑动到偏移值的时候退出生成
while current < offset:
根据是否到达临界值决定使用加速的加速度还是减速的加速度
if current < round(mid):
a = 2
else:
a = -4
v0 = v
v = v0 + a*t
move = v0 * t + 1/2 * a * t * t
current += move
track.append(round(move))
# print(track)
# 设置混淆位移,成对编写,添加到track的结尾,模拟微调
data = [1, 5, 3, -5, -3, -1]
random.shuffle(data)
track.extend(data)
return track
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
# 获取两个图片对象
img1, img2 = self.get_image()
# 获取拼图阴影位置
offset = self.get_gap(img1, img2)
# 计算移动轨迹
track = self.get_track(offset)
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
11.bilibili模拟登陆-拖动滑块
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
# ......
def operate_button(self, track):
print("***")
# 点击拖动验证码的按钮
ActionChains(self.driver).click_and_hold(self.get_button()).perform()
# 拖动验证码
for i in track:
ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
# 松开按钮验证
ActionChains(self.driver).release().perform()
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
# 获取两个图片对象
img1, img2 = self.get_image()
# 获取拼图阴影位置
offset = self.get_gap(img1, img2)
# 计算移动轨迹
track = self.get_track(offset)
# 操作拖动条完成拼图
self.operate_button(track)
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
10.bilibili模拟登陆-拖动滑块
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from io import BytesIO
from PIL import Image
import time
import random
class Bilibili(object):
# ......
def operate_button(self, track):
print("***")
# 点击拖动验证码的按钮
ActionChains(self.driver).click_and_hold(self.get_button()).perform()
# 拖动验证码
for i in track:
ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform()
# 松开按钮验证
ActionChains(self.driver).release().perform()
def login(self):
"""
实现主要的登录逻辑
"""
# 调用输入账号密码的方法
self.input_user_pwd()
# 获取两个图片对象
img1, img2 = self.get_image()
# 获取拼图阴影位置
offset = self.get_gap(img1, img2)
# 计算移动轨迹
track = self.get_track(offset)
# 操作拖动条完成拼图
self.operate_button(track)
def run(self):
self.login()
if __name__ == '__main__':
bili = Bilibili()
bili.run()
以上是关于Python爬虫:Bilibili模拟登陆(滑动验证码)项目的主要内容,如果未能解决你的问题,请参考以下文章