1. 前言
最近在写一个爬虫项目,需要模拟哔哩哔哩的登入。但是,哔哩哔哩使用了滑块登入验证,传统的表单提交加上账号密码的方式已经不能满足需求。本文将介绍如何使用 Python 模拟哔哩哔哩的滑块登入验证。
2. 环境准备
2.1 安装 Python
首先,要确保你已经安装了 Python。如果你还没有安装 Python,请到 Python官网 下载对应版本。
2.2 安装 selenium
在本文中,我们将使用 selenium 来模拟登入。selenium 是一个自动化测试工具,可以模拟用户在浏览器中的操作。
使用 pip 安装最新版本的 selenium:
pip install selenium
2.3 下载 Chromedriver
selenium 需要一个浏览器驱动程序才能模拟浏览器。在本文中,我们将使用 Chrome 浏览器。请到 Chromedriver 官网 下载对应版本的 Chromedriver。下载后,将 Chromedriver.exe 文件放在任意一个文件夹中。
3. 实现
3.1 打开 Chrome 浏览器
使用 Python 中的 selenium webdriver,打开 Chrome 浏览器:
from selenium import webdriver
# 初始化浏览器
browser = webdriver.Chrome(r"C:\path\to\chromedriver.exe")
这里需要把 r"C:\path\to\chromedriver.exe"
替换成 Chromedriver 的实际路径。
3.2 进入哔哩哔哩登入页面
使用 get()
方法进入哔哩哔哩登入页面:
# 进入登入页面
browser.get('https://passport.bilibili.com/login')
3.3 切换到滑动验证码的 iframe
哔哩哔哩的滑块登入验证是在一个 iframe 中进行的。因此,需要使用 switch_to.frame()
方法切换到滑动验证码的 iframe 中:
# 切换到滑动验证码的 iframe
iframe = browser.find_element_by_xpath('//iframe[@id="gc-box"]')
browser.switch_to.frame(iframe)
3.4 获取滑块和背景图片的 URL
哔哩哔哩的滑动验证码需要使用背景图片和滑块图片进行验证。使用 get_attribute()
方法获取背景图片和滑块图片的 URL:
# 获取背景图片
background_img = browser.find_element_by_xpath('//div[@class="gt_cut_bg gt_show"]/div[@class="gt_cut_bg_slice"]')
bg_url = background_img.get_attribute('style').split("'")[1]
# 获取滑块图片
slider_img = browser.find_element_by_xpath('//div[@class="gt_cut_fullbg gt_show"]/div[@class="gt_cut_fullbg_slice"]')
slider_url = slider_img.get_attribute('style').split("'")[1]
这里需要注意一下,获取到的背景图片和滑块图片的 URL 包含了一些参数。这些参数是每次请求时生成的,需要在每次请求时重新获取。
3.5 下载背景图片和滑块图片并还原
哔哩哔哩的滑块验证码还原需要将背景图片和滑块图片拼接在一起,并根据图片的不同特点还原出完整的滑动验证码。这个过程可以使用 PIL 库来实现。
首先,使用 requests 库下载图片:
import requests
# 下载背景图片和滑块图片
bg = requests.get(bg_url).content
slider = requests.get(slider_url).content
然后,使用 PIL 库将图片转换为 Image 对象:
from PIL import Image
from io import BytesIO
# 将图片转换为 Image 对象
bg = Image.open(BytesIO(bg))
slider = Image.open(BytesIO(slider))
最后,根据图片的不同特点还原出完整的滑动验证码:
import re
import time
import random
# 还原滑动验证码
def get_offset(bg, slider):
# 阈值
threshold = 50
# 获取要滑动的距离
for x in range(bg.size[0] - slider.size[0]):
for y in range(bg.size[1] - slider.size[1]):
# 计算每个像素点的 RGB 值之和
bg_rgb = sum(bg.getpixel((x, y)))
slider_rgb = sum(slider.getpixel((x, y)))
# 如果 RGB 值之和之差超过了阈值,说明找到了要滑动的距离
if abs(bg_rgb - slider_rgb) > threshold:
return x
# 根据 RGB 值还原滑动验证码
def restore_image(im_path, bg_path, u, x):
im = Image.open(im_path)
bg = Image.open(bg_path)
# 将滑块放到正确的位置
bg.paste(im, (x, 0))
# 获取阴影部分
img1 = bg.crop((u, 0, u + im.size[0], im.size[1]))
# 获取拼图部分
img2 = im.crop((0, 0, im.size[0], im.size[1]))
# 创建一张新的图片
new_img = Image.new('RGB', (bg.size[0], bg.size[1]))
# 将阴影部分放到新图片中
new_img.paste(img1, (0, 0))
# 将拼图部分放到新图片中
new_img.paste(img2, (x, 0))
# 返回还原后的图片
return new_img
这个过程看起来比较复杂,但实际上就是比较简单的图像处理。如果你对图像处理的原理感兴趣,可以自行查阅相关资料。
3.6 拖动滑块完成验证
最后,使用 ActionChains 类拖动滑块完成验证:
from selenium.webdriver.common.action_chains import ActionChains
# 拖动滑块完成验证
slider_btn = browser.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
ActionChains(browser).click_and_hold(slider_btn).perform()
# 还原图片并获取要滑动的距离
bg_path = 'bg.png'
im_path = 'slider.png'
bg.save(bg_path)
slider.save(im_path)
u = get_offset(bg, slider)
# 根据 RGB 值还原滑动验证码
new_img = restore_image(im_path, bg_path, u, 0)
new_img.save('new.png')
# 拖动滑块完成验证
ActionChains(browser).move_by_offset(u, 0).perform()
ActionChains(browser).release().perform()
这里需要注意一下,move_by_offset(u, 0)
中的 u
就是之前还原图片时计算得到的要滑动的距离。
4. 完整代码
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
from io import BytesIO
import requests
browser = webdriver.Chrome(r"C:\path\to\chromedriver.exe")
browser.get('https://passport.bilibili.com/login')
# 切换到滑动验证码的 iframe
iframe = browser.find_element_by_xpath('//iframe[@id="gc-box"]')
browser.switch_to.frame(iframe)
# 获取背景图片
background_img = browser.find_element_by_xpath('//div[@class="gt_cut_bg gt_show"]/div[@class="gt_cut_bg_slice"]')
bg_url = background_img.get_attribute('style').split("'")[1]
# 获取滑块图片
slider_img = browser.find_element_by_xpath('//div[@class="gt_cut_fullbg gt_show"]/div[@class="gt_cut_fullbg_slice"]')
slider_url = slider_img.get_attribute('style').split("'")[1]
# 下载背景图片和滑块图片
bg = requests.get(bg_url).content
slider = requests.get(slider_url).content
# 将图片转换为 Image 对象
bg = Image.open(BytesIO(bg))
slider = Image.open(BytesIO(slider))
# 还原滑动验证码
def get_offset(bg, slider):
# 阈值
threshold = 50
# 获取要滑动的距离
for x in range(bg.size[0] - slider.size[0]):
for y in range(bg.size[1] - slider.size[1]):
# 计算每个像素点的 RGB 值之和
bg_rgb = sum(bg.getpixel((x, y)))
slider_rgb = sum(slider.getpixel((x, y)))
# 如果 RGB 值之和之差超过了阈值,说明找到了要滑动的距离
if abs(bg_rgb - slider_rgb) > threshold:
return x
def restore_image(im_path, bg_path, u, x):
im = Image.open(im_path)
bg = Image.open(bg_path)
# 将滑块放到正确的位置
bg.paste(im, (x, 0))
# 获取阴影部分
img1 = bg.crop((u, 0, u + im.size[0], im.size[1]))
# 获取拼图部分
img2 = im.crop((0, 0, im.size[0], im.size[1]))
# 创建一张新的图片
new_img = Image.new('RGB', (bg.size[0], bg.size[1]))
# 将阴影部分放到新图片中
new_img.paste(img1, (0, 0))
# 将拼图部分放到新图片中
new_img.paste(img2, (x, 0))
# 返回还原后的图片
return new_img
slider_btn = browser.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
ActionChains(browser).click_and_hold(slider_btn).perform()
bg_path = 'bg.png'
im_path = 'slider.png'
bg.save(bg_path)
slider.save(im_path)
u = get_offset(bg, slider)
new_img = restore_image(im_path, bg_path, u, 0)
new_img.save('new.png')
ActionChains(browser).move_by_offset(u, 0).perform()
ActionChains(browser).release().perform()
5. 总结
本文介绍了如何使用 Python 模拟哔哩哔哩的滑块登入验证。具体来说,我们使用 selenium 打开 Chrome 浏览器,然后进入哔哩哔哩的登入页面。接下来,我们使用 selenium 切换到滑动验证码的 iframe,并获取滑块和背景图片的 URL。然后,我们使用 requests 库下载背景图片和滑块图片,并使用 PIL 库还原出完整的滑动验证码。最后,我们使用 ActionChains 类拖动滑块完成验证。
这里仅仅是一个简单的例子,实际上滑动验证码常常被应用于诸如网站爬虫、反爬虫、登录验证等场景。理解滑动验证码的原理以及如何模拟它的行为,对爬虫优化或反爬虫工作是有很大帮助的。