思路

  1. 输入用户名密码点击登陆
  2. 获取验证码的原始图片与有缺口的图片
  3. 找出两张图片的缺口起始处
  4. 拖动碎片

功能代码段

# 使用到的库
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
import time
import base64
username = '用户名'
password = '密码'
# 放在外面的原因是如果再类的内部初始化,则程序结束后浏览器会自动退出
driver = webdriver.Chrome()

初始化相关参数

	# 初始化相关参数
def __init__(self):
self.url = 'https://passport.bilibili.com/login'
self.browser = driver
self.wait = WebDriverWait(self.browser, 20)
self.name = username
self.pw = password

获取按钮、输入框、碎片拖动按钮对象

	def get_login_button(self):
"""
获取初始登录按钮
:return: 按钮对象
"""
button = self.wait.until(
EC.presence_of_element_located((By.XPATH, "//a[contains(@class,'btn') and contains(@class, 'btn-login')]")))
return button def get_slider_button(self):
"""
获取拖动碎片的地方
:return: 拖动对象
"""
sliderbutton = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='geetest_slider_button']")))
return sliderbutton def get_login_input(self):
"""
获取登陆输入框(用户名/密码)
:return: 输入框对象
"""
user_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-username']")))
pw_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-passwd']")))
return user_login, pw_login

获取带有碎片的图片和完整图片

    def save_pic(self, data, filename):
"""
解码获取到的base64再写入到文件中,保存图片
:return:
"""
data = data.split(',')[1]
data = base64.b64decode(data)
with open(filename, 'wb') as f:
f.write(data) def get_pic(self):
"""
获取无缺口图片和有缺口图片
:return: 图片对象
"""
picName = ['full.png', 'slice.png']
# 图片对象的class
className = ['geetest_canvas_fullbg', 'geetest_canvas_bg']
# canvas标签中的图片通过js代码获取base64编码,然后再通过解码,将其写入文件才能获取到
for i in range(len(className)):
js = "var change = document.getElementsByClassName('"+className[i]\
+ "'); return change[0].toDataURL('image/png');"
im_info = self.browser.execute_script(js)
self.save_pic(im_info, picName[i])

判断像素点是否相同

    def is_pixel_equal(self, image1, image2, x, y):
"""
判断两个像素点是否是相同
:param image1: 不带缺口图片
:param image2: 带缺口图片
:param x: 像素点的x坐标
:param y: 像素点的y坐标
:return:
"""
pixel1 = image1.load()[x, y]
pixel2 = image2.load()[x, y]
threshold = 40
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):
"""
获取缺口偏移量
:param image1: 不带缺口图片
:param image2: 带缺口图片
:return:
"""
# 这个可以自行操作一下,如果发现碎片对不准,可以调整
left = 10
for i in range(left, image1.size[0]):
for j in range(image1.size[1]):
if not self.is_pixel_equal(image1, image2, i, j):
left = i
return left
return left

变速运动拖动碎片,否则容易被看出来是机器执行

    def get_track(self, distance):
"""
根据偏移量获取移动轨迹
:param self:
:param distance: 偏移量
:return: 移动轨迹
"""
# 移动轨迹
track = []
# 当前位移
current = 0
# 对的不一定很准确,所以自行调整一下distance
distance = distance - 9
# 减速阈值 -> 也就是加速到什么位置的时候开始减速
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0 while current < distance:
if current < mid:
# 加速度为正2
a = 2
else:
# 加速度为负3
a = -3
v0 = v
v = v0 + a * t
move = v0 * t + 1 / 2 * a * t * t
current += move
track.append(round(move))
return track

模拟拖动碎片

    def move_to_gap(self, slider, tracks, browser):
"""
拖动滑块到缺口处
:param self:
:param slider: 滑块
:param tracks: 轨迹
:return:
"""
# click_and_hold()点击鼠标左键,不松开
ActionChains(self.browser).click_and_hold(slider).perform()
for x in tracks:
# move_by_offset()鼠标从当前位置移动到某个坐标
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
# release()在某个元素位置松开鼠标左键
ActionChains(self.browser).release().perform()

配置执行

    def test(self):
# 输入用户名和密码
self.browser.get(self.url)
user_login, pw_login = self.get_login_input()
user_login.send_keys(self.name)
pw_login.send_keys(self.pw)
# 点击按钮对象
button = self.get_login_button()
button.click()
# 这里设置等待是为了使得滑动验证码能出现,之后才能通过toDataURL获取
time.sleep(3)
self.get_pic()
image1 = Image.open('full.png')
image2 = Image.open('slice.png')
left = self.get_gap(image1, image2)
track = self.get_track(left)
slider = self.get_slider_button()
self.move_to_gap(slider, track, self.browser)

完整代码

TIP

如果出现碎片移动存在一定对不准的情况,可以自行调整一下left和distance的值。

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image
import time
import base64
username = '用户名'
password = '密码'
driver = webdriver.Chrome() class Start:
def __init__(self):
self.url = 'https://passport.bilibili.com/login'
self.browser = driver
self.wait = WebDriverWait(self.browser, 20)
self.name = username
self.pw = password def get_login_button(self):
"""
获取初始登录按钮
:return: 按钮对象
"""
button = self.wait.until(
EC.presence_of_element_located((By.XPATH, "//a[contains(@class,'btn') and contains(@class, 'btn-login')]")))
return button def get_slider_button(self):
"""
获取拖动碎片的地方
:return: 拖动对象
"""
sliderbutton = self.wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='geetest_slider_button']")))
return sliderbutton def get_login_input(self):
"""
获取登陆输入框(用户名/密码)
:return: 输入框对象
"""
user_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-username']")))
pw_login = self.wait.until(EC.presence_of_element_located((By.XPATH, "//input[@id='login-passwd']")))
return user_login, pw_login def save_pic(self, data, filename):
"""
解码获取到的base64再写入到文件中,保存图片
:return:
"""
data = data.split(',')[1]
data = base64.b64decode(data)
with open(filename, 'wb') as f:
f.write(data) def get_pic(self):
"""
获取无缺口图片和有缺口图片
:return: 图片对象
"""
# 图片对象的类名
# 首先需要这个东西已经出现了,我们才能去执行相关的js代码
picName = ['full.png', 'slice.png']
className = ['geetest_canvas_fullbg', 'geetest_canvas_bg']
# canvas标签中的图片通过js代码获取base64编码
for i in range(len(className)):
js = "var change = document.getElementsByClassName('"+className[i]\
+ "'); return change[0].toDataURL('image/png');"
im_info = self.browser.execute_script(js)
self.save_pic(im_info, picName[i]) def is_pixel_equal(self, image1, image2, x, y):
"""
判断两个像素点是否是相同
:param image1: 不带缺口图片
:param image2: 带缺口图片
:param x: 像素点的x坐标
:param y: 像素点的y坐标
:return:
"""
pixel1 = image1.load()[x, y]
pixel2 = image2.load()[x, y]
threshold = 40
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):
"""
获取缺口偏移量
:param image1: 不带缺口图片
:param image2: 带缺口图片
:return:
"""
# 这个可以自行操作一下,如果发现碎片对不准,可以调整
left = 10
for i in range(left, image1.size[0]):
for j in range(image1.size[1]):
if not self.is_pixel_equal(image1, image2, i, j):
left = i
return left
return left def get_track(self, distance):
"""
根据偏移量获取移动轨迹
:param self:
:param distance: 偏移量
:return: 移动轨迹
"""
# 移动轨迹
track = []
# 当前位移
current = 0
# 因为老对不的不准确,所以自行调整一下distance
distance = distance - 9
# 减速阈值 -> 也就是加速到什么位置的时候开始减速
mid = distance * 4 / 5
# 计算间隔
t = 0.2
# 初速度
v = 0 while current < distance:
if current < mid:
# 加速度为正2
a = 2
else:
# 加速度为负3
a = -3
v0 = v
v = v0 + a * t
move = v0 * t + 1 / 2 * a * t * t
current += move
track.append(round(move))
return track def test(self):
# 输入用户名和密码
self.browser.get(self.url)
user_login, pw_login = self.get_login_input()
user_login.send_keys(self.name)
pw_login.send_keys(self.pw)
# 点击按钮对象
button = self.get_login_button()
button.click()
# 保存图片
time.sleep(3)
self.get_pic()
image1 = Image.open('full.png')
image2 = Image.open('slice.png')
left = self.get_gap(image1, image2)
track = self.get_track(left)
slider = self.get_slider_button()
self.move_to_gap(slider, track, self.browser) def move_to_gap(self, slider, tracks, browser):
"""
拖动滑块到缺口处
:param self:
:param slider: 滑块
:param tracks: 轨迹
:return:
"""
# click_and_hold()点击鼠标左键,不松开
ActionChains(self.browser).click_and_hold(slider).perform()
for x in tracks:
# move_by_offset()鼠标从当前位置移动到某个坐标
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
# release()在某个元素位置松开鼠标左键
ActionChains(self.browser).release().perform() Start().test()

使用python - selenium模拟登陆b站的更多相关文章

  1. python selenium模拟登陆163邮箱。

    selenium是可以模拟浏览器操作. 有些爬虫是异步加载的,通过爬取网页源码是得不到需要的内容.所以可以模拟浏览器去登陆该网站进行爬取操作. 需要安装selenium通过pip install xx ...

  2. python selenium模拟登陆qq空间

    不多说.直接上代码 from selenium import webdriver driver = webdriver.Chrome() driver.get('http://qzone.qq.com ...

  3. python selenium 模拟登陆百度账号

    代码: from selenium import webdriver url = 'https://passport.baidu.com/v2/?login' username = 'your_use ...

  4. python+selenium 模拟登陆,自动下单

    目前写的实在太粗糙,留着,以后来写上

  5. Python实现模拟登陆

    大家经常会用Python进行数据挖掘的说,但是有些网站是需要登陆才能看到内容的,那怎么用Python实现模拟登陆呢?其实网路上关于这方面的描述很多,不过前些日子遇到了一个需要cookie才能登陆的网站 ...

  6. 【小白学爬虫连载(10)】–如何用Python实现模拟登陆网站

    Python如何实现模拟登陆爬取Python实现模拟登陆的方式简单来说有三种:一.采用post请求提交表单的方式实现.二.利用浏览器登陆网站记录登陆成功后的cookies,采用get的请求方式,传入c ...

  7. python爬虫模拟登陆

    python爬虫模拟登陆 学习了:https://www.cnblogs.com/chenxiaohan/p/7654667.html  用的这个 学习了:https://www.cnblogs.co ...

  8. Selenium模拟登陆百度贴吧

    Selenium模拟登陆百度贴吧 from selenium import webdriver from time import sleep from selenium.webdriver.commo ...

  9. 使用Python+Selenium模拟登录QQ空间

    使用Python+Selenium模拟登录QQ空间爬QQ空间之类的页面时大多需要进行登录,研究QQ登录规则的话,得分析大量Javascript的加密解密,这绝对能掉好几斤头发.而现在有了seleniu ...

随机推荐

  1. Webpack基础知识总结

    本文将从多个方面回顾下自己了解的Webpack知识,包括常见的配置项,前端搭建的一些方法和项目实际优化方法,有错误的地方还请指出并多多包涵. 一.关于Webpack 1.概念 本质上,webpack ...

  2. ==和equals的深度分析

    ==的分析 1.对于基本数据类型,比较的是他们的具体内容是不是一样,跟他们的内存地址无关.举个栗子: public class Test{ public static void main(String ...

  3. nginx代理grafana

    希望通过Nginx为服务器上的grafana进行代理,实现通过在当前域名后加/grafana在公网进行访问,开始按照百度的方法弄了几个小时都不行,后面仔细看了官方的文档才弄好,Mark一下. Ngin ...

  4. ES6 promise 使用示例

    new Promise(function (resolve, reject) { $.ajax({ type : 'post', data : formData, dataType : 'json', ...

  5. 安装完PyCharm,启动时弹出Failed to load JVM DLLbinserverjvm

    安装完PyCharm,启动时弹出"Failed to load JVM DLL\bin\server\jvm.dll"解决方案 问题描述:打开PyCharm时,弹出"Fa ...

  6. Caffe源码-Solver类

    Solver类简介 Net类中实现了网络的前向/反向计算和参数更新,而Solver类中则是对此进行进一步封装,包含可用于逐次训练网络的Step()函数,和用于求解网络的优化解的Solve()函数,同时 ...

  7. Java 从入门到进阶之路(十四)

    在之前的文章我们介绍了一下 Java 中的抽象类和抽象方法,本章我们来看一下 Java 中的接口. 在日常生活中,我们会接触到很多类似接口的问题,比如 USB 接口,我们在电脑上插鼠标,键盘,U盘的时 ...

  8. Android JSON解析插件

    JSON是一种轻量级的数据格式,用于数据的交互. Android交互数据主要两种方式:JSON和 XML.XML格式比JSON格式数量略大,所以大多都使用Json数据格式. 在Android开发的过程 ...

  9. Django的Form验证

    Django的Form验证 Form验证:Form提交Form表单数据验证 针对Form提交的数据进行验证 创建模板 class loginForm() 请求提交给模板,创建对象 obj=loginF ...

  10. Spring注解之@Autowired

    前言 说起Spring的@Autowired注解,想必大家已经熟悉的不能再熟悉了.本文就针对此最常用的注解,梳理一下它的功能和原理,争取从源码的角度将此注解讲通,如有写的不准确的地方,欢迎各位园友拍砖 ...