Python实现Bilibili滑动验证码自动破解与登录
本文详细介绍使用Python结合Selenium和PIL库破解Bilibili滑动验证码的实现过程。通过获取完整与缺口验证码图片,利用图像分割与像素比对计算滑块位移,并模拟人类运动轨迹完成拖动。文中提供了从环境配置到代码实现的全流程解析,帮助开发者掌握自动化登录技术。
访问登录页面地址:https://passport.bilibili.com/login
左侧图片展示完整的验证码背景图,右侧图片展示带有缺口的验证码背景图。

具体实施步骤如下:
1. 准备有效的Bilibili账号用于测试登录。
2. 所需工具包括:PyCharm集成开发环境、Selenium自动化库、ChromeDriver浏览器驱动以及PIL图像处理库。
3. 核心破解思路如下:
首先获取完整验证码图片和带有缺口的验证码图片,通过图像算法计算缺口所在的坐标位置,随后利用Selenium控制浏览器将滑块拖动至指定位置,即可完成验证。
具体实现代码如下:
首先导入项目所需的各类包和库文件。
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
from lxml.html import etree
import re,requests
from PIL import Image
from time import sleep
from .config import username,password
class Jiyan_test:
def __init__(self):
self.url='https://passport.bilibili.com/login'
self.brower=webdriver.Chrome('chromedriver')
self.wait=WebDriverWait(self.brower,20)#设置等待
def login(self):
self.brower.get(self.url)
self.user=self.wait.until(EC.presence_of_element_located((By.ID,'login-username')))
self.passwd=self.wait.until(EC.presence_of_element_located((By.ID,'login-passwd')))
self.user.send_keys(username)
self.passwd.send_keys(password)
def get_images(self):#获取验证码图片
# print(self.brower.page_source)
full_position=[]#完整散图坐标
bg_position=[]#缺口散图坐标
html=etree.HTML(self.brower.page_source)
gt_cut_fullbg_slices=html.xpath('//div[@class="gt_cut_fullbg_slice"]/@style')
full_slice_url=re.findall('url\(\"(.*)\"\);',gt_cut_fullbg_slices[0])[0].replace('webp','jpg')
gt_cut_bg_slices = html.xpath('//div[@class="gt_cut_bg_slice"]/@style')
bg_slice_url = re.findall('url\(\"(.*)\"\);', gt_cut_bg_slices[0])[0].replace('webp', 'jpg')
print(gt_cut_fullbg_slices)
for i in gt_cut_fullbg_slices:
position=re.findall('background-position: (.*);',i)[0].replace('px','').split(' ')
position=[int(i) for i in position]
full_position.append(position)
for i in gt_cut_fullbg_slices:
position = re.findall('background-position: (.*);', i)[0].replace('px','').split(' ')
position=[int(i) for i in position]
bg_position.append(position)
print(full_position)
print(bg_position)
print(full_slice_url)
print(bg_slice_url)
full_pic_data=requests.get(full_slice_url).content
bg_pic_data=requests.get(bg_slice_url).content
with open('image/full_pic.jpg','wb') as f:
f.write(full_pic_data)
with open('image/bg_pic.jpg', 'wb') as f:
f.write(bg_pic_data)
full_image=Image.open('image/full_pic.jpg')
bg_image=Image.open('image/bg_pic.jpg')
return full_image,bg_image,full_position,bg_position
接下来处理图片分割逻辑。
def pic_cut(self,file,position):#分割图片
first_line_pic=[]
second_line_pic=[]
# full_image, bg_image, full_position, bg_position=self.get_images()
for p in position:
if p[1]==-58:
first_line_pic.append(file.crop((abs(p[0]),58,abs(p[0])+10,166)))
if p[1]==0:
second_line_pic.append(file.crop((abs(p[0]),0,abs(p[0])+10,58)))
print(first_line_pic)
print(second_line_pic)
return first_line_pic,second_line_pic
然后进行图片合并操作。
def merge_pics_new(self,first_line_pic,second_line_pic,file_name):
#新建图片
image=Image.new('RGB',(260,116))
offset=0#设置偏移量
#拼接第一行
for i in first_line_pic:
image.paste(i,(offset,0))
offset+=i.size[0]
offset_x=0
#拼接第二行
for j in second_line_pic:
image.paste(j,(offset_x,58))
offset_x+=j.size[0]
image.save('image/'+file_name)#合成完整图片
def merge_pics(self):#合并图片
#先割切乱码图片
full_image, bg_image, full_position, bg_position=self.get_images()
first_line_pic, second_line_pic=self.pic_cut(full_image,full_position)
self.merge_pics_new(first_line_pic, second_line_pic,'full_new_pic.jpg')
first_line_pic, second_line_pic = self.pic_cut(bg_image, bg_position)
self.merge_pics_new(first_line_pic, second_line_pic, 'bg_new_pic.jpg')
随后判断两张处理后的图片是否一致。
def check_pics_is_same(self,bg_image,full_image,x,y):#判断图片是否一样
bg_pixel=bg_image.getpixel((x,y))
full_pixel=full_image.getpixel((x,y))
for i in range(0,3):
if abs(bg_pixel[i]-full_pixel[i])>=50:
return False
else:
return True
接着计算滑块需要移动的具体距离。
def reckon_distance2(self):#计算滑块
try:
full_image = Image.open('image/full_new_pic.jpg')
bg_image = Image.open('image/bg_new_pic.jpg')
for i in range(0,full_image.size[0]):
for j in range(0,full_image.size[1]):
if not self.check_pics_is_same(bg_image,full_image,i,j):
return i
except Exception:
print('图片读取失败')
根据计算出的距离生成模拟人类操作的运动轨迹。
def reckon_trail(self):#计算运动轨迹
print('计算运动轨迹')
track=[]
distance=self.reckon_distance2()
distance=int(distance)-7#矫正值
print('缺口坐标',distance)
fast_distance=distance*(4/5)
start,v0,t=0,0,0.2
while start<distance:
if start<fast_distance:#加速状态
a=1.5#加速
else:
a=-3#减速
#数学公式 s=v0*t+1/2 v*t平方
move=v0*t+1/2*a*t*t
#当前速度
v=v0+a*t
#重置粗速度
v0=v
#重置起始位置
start+=move
track.append(round(move))
return track
最后执行模拟拖动操作。
def move_block(self):# 模拟拖动滑块
print('开始模拟')
track=self.reckon_trail()
#找寻滑块标签
slider=self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'gt_slider_knob')))
ActionChains(self.brower).click_and_hold(slider).perform()#执行
for x in track:
ActionChains(self.brower).move_by_offset(xoffset=x,yoffset=0).perform()
sleep(0.4)
ActionChains(self.brower).release().perform()#释放滑块
if __name__ == '__main__':
c=Jiyan_test()
c.login()
c.merge_pics()
c.move_block()
经测试运行基本正常,但偶尔会出现未能完全对齐的情况,此时需要微调distance参数值以优化效果。
总结
以上内容为Python破解Bilibili滑动验证码登录功能的完整介绍,希望能为读者提供帮助。如有任何疑问,欢迎留言交流,小编会及时回复。感谢大家对本站的支持! 如果觉得本文对您有帮助,欢迎转载,请注明出处,谢谢!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













