前言
自我感觉唯一亮点就是滑块验证出现各种异常处理情况,完全实现自动化!
转载本文,请注明本文链接,谢谢!
直接上代码
写明了注释:
from selenium import webdriver
import time
import selenium.webdriver.support.ui as ui
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import base64
import random
from PIL import Image
from io import BytesIO
from loguru import logger
class BilibiliLogin(object):
def __init__(self,proxy):
"""
:param proxy: 添加代理代理
"""
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('''--no-sandbox''')
chrome_options.add_argument('''--disable-gpu''')
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
chrome_options.add_experimental_option('useAutomationExtension', False)
if proxy != "":
chrome_options.add_argument('--proxy-server={0}'.format(proxy))
self.driver = webdriver.Chrome(options=chrome_options,executable_path='chromedriver')
self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
def sms_login(self,phone):
logger.debug("开始手机验证码登陆(B站):" + phone)
self.driver.set_page_load_timeout(10)
try:
self.driver.set_page_load_timeout(10)
self.driver.get('https://passport.bilibili.com/login')
except Exception as e:
self.driver.execute_script("window.stop()")
# 等待手机验证按钮出现点击
wait = ui.WebDriverWait(self.driver,5)
wait.until(lambda driver:self.driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[1]/span[2]'))
self.driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[1]/span[2]').click()
time.sleep(0.5)
sms_input = self.driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[3]/div[1]/div/input')
sms_input.send_keys(phone)
time.sleep(0.5)
sms_send_btn = self.driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[3]/div[3]/button')
sms_send_btn.click()
time.sleep(0.5)
try:
WebDriverWait(self.driver, 3, 0.2).until(
EC.presence_of_element_located((By.CLASS_NAME, 'geetest_wrap')))
logger.debug("开始手机验证码登陆(B站):" + phone + " 找到验证框")
except Exception as e:
logger.debug("开始手机验证码登陆(B站):" + phone + " 没有找到验证框")
logger.debug('开始进行验证码验证(B站)')
result = self.captcha_verify()
if result:
pass
input('===')
def qr_login(self):
logger.debug('开始二维码登陆(B站)')
self.driver.set_page_load_timeout(10)
try:
self.driver.set_page_load_timeout(10)
self.driver.get('https://passport.bilibili.com/login')
except Exception as e:
self.driver.execute_script("window.stop()")
wait = ui.WebDriverWait(self.driver, 5)
wait.until(lambda driver: self.driver.find_element_by_xpath('//*[@id="login-app"]/div/div[2]/div[3]/div[1]/div/div[1]/div/div[2]/img'))
img_data = self.driver.find_element_by_xpath('//*[@id="login-app"]/div/div[2]/div[3]/div[1]/div/div[1]/div/div[2]/img').get_attribute('src')
print(img_data)
input('等待刷新验证码')
self.get_cookies("二维码")
self.close()
def pwd_login(self,name,pwd):
logger.debug("开始帐户密码模拟登陆(B站):"+name)
self.driver.set_page_load_timeout(10)
try:
self.driver.set_page_load_timeout(10)
self.driver.get('https://passport.bilibili.com/login')
except Exception as e:
self.driver.execute_script("window.stop()")
wait = ui.WebDriverWait(self.driver, 5)
wait.until(lambda driver: self.driver.find_element_by_xpath(
'//*[@id="login-username"]'))
self.driver.find_element_by_xpath('//*[@id="login-username"]').send_keys(name)
time.sleep(0.3)
self.driver.find_element_by_xpath('//*[@id="login-passwd"]').send_keys(pwd)
time.sleep(0.3)
self.driver.find_element_by_xpath('//*[@id="geetest-wrap"]/div/div[5]/a[1]').click()
try:
WebDriverWait(self.driver, 3, 0.2).until(
EC.presence_of_element_located((By.CLASS_NAME, 'geetest_wrap')))
logger.debug("开始手机验证码登陆(B站):" + name + " 找到验证框")
except Exception as e:
logger.debug("开始手机验证码登陆(B站):" + name + " 未找到验证框")
return
result = self.captcha_verify()
if result:
self.get_cookies(name)
self.close()
def captcha_verify(self):
"""
滑块验证
这里是一个死循环,直到滑块过了这个方法才会结束,出现异常都会点击刷新重新验证滑块
:return:
"""
time.sleep(1)
c_image, ic_image = self.get_geetest_image()
instance = self.get_slice_gap(c_image, ic_image) - 6 # 根据自己的浏览器的实际情况减掉一些距离
print(f'缺口的偏移量为:{instance}')
time.sleep(1)
button = self.driver.find_element_by_class_name('geetest_slider_button')
ActionChains(self.driver).click_and_hold(button).perform()
tracks = self.get_track(instance)
for x in tracks:
ActionChains(self.driver).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.3)
ActionChains(self.driver).release().perform()
time.sleep(2)
try:
# 这是用于账号慢慢登录,这里就直接登录成功,没有这个元素了,直接异常返回
error = self.driver.find_element_by_class_name('geetest_panel_error').value_of_css_property('display')
except:
return True
if str(error) == 'block':
# 说明出现滑块后出现网络不给力,点击重试
self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[4]/div[3]').click()
time.sleep(5)
error_wait = ui.WebDriverWait(self.driver, 5)
error_wait.until(lambda driver: self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[4]/div[3]'))
return self.captcha_verify()
is_captcha = self.driver.find_element_by_xpath('/html/body/div[2]').value_of_css_property('display')
print(is_captcha)
if str(is_captcha) == "block":
# 说明滑块没有过,点击刷新,重新获取滑块
self.driver.find_element_by_xpath('/html/body/div[2]/div[2]/div[6]/div/div[2]/div/a[2]').click()
time.sleep(0.5)
return self.captcha_verify()
return True
def get_track(self,instance):
'''
模拟人工运动
:param x:
:return:
'''
tracks = []
x = int(instance / 2)
# 先滑动一半(很关键,不滑不给过)
tracks.append(x)
# 滑动x距离
instance -= x
# 减去已划过的距离,重新赋值剩下的x偏移量
while instance == 10:
x = random.randint(5, 8)
# 当剩下的一半x偏移量=10的时候,每次滑动的距离(x偏移量)取5-8之间的随机数
tracks.append(x)
instance -= x
for i in range(int(instance)):
tracks.append(1)
# 当x偏移量10的时候,最后几步慢慢滑(每次滑动的偏移量为1),以确保滑动的精准
return tracks
def isElementExist(self,element):
"""
判断元素是否存在
:return:
"""
try:
self.driver.find_element_by_class_name(element)
return True
except:
return False
def get_geetest_image(self):
"""
获取极验验证码图片
:return: c_image(完整验证图) ic_image(有缺失的验证图)
"""
"""
完整的验证图
"""
# 执行js 拿到canvas画布里面的图片数据
js = 'return document.getElementsByClassName("geetest_canvas_fullbg")[0].toDataURL("image/png");'
# 图片数据
complete_img_data = self.driver.execute_script(js)
# base64 编码的图片信息
complete_img_base64 = complete_img_data.split(',')[1]
# 转成bytes类型
complete_img = base64.b64decode(complete_img_base64)
# 加载图片 return 回去对比
c_image = Image.open(BytesIO(complete_img))
"""
有缺失的验证码图
"""
# 执行js 拿到canvas画布里的图片数据
js = 'return document.getElementsByClassName("geetest_canvas_bg")[0].toDataURL("image/png");'
# 图片数据
incomplete_img_data = self.driver.execute_script(js)
# base64 编码的图片信息
incomplete_img_base64 = incomplete_img_data.split(',')[1]
# 转为bytes类型
incomplete_img = base64.b64decode(incomplete_img_base64)
# 直接加载图片 return 回去对比
ic_image = Image.open(BytesIO(incomplete_img))
return c_image, ic_image
def is_pixel_similar(self, c_image, ic_image, x, y):
"""
比较两张图片的像素点
注意: 像素点比较是有偏差的, 需要允许一定范围的误差,
我们可以设置一个阈值
:param ic_image:
:param c_image:
:param x:
:param y:
:return: 当像素点不相同时, 返回 False
"""
# 获取两张图片执行位置的像素点
c_pixel = c_image.load()[x, y]
ic_pixel = ic_image.load()[x, y]
# 阈值 允许误差
threshold = 10
# 对比
if abs(c_pixel[0] - ic_pixel[0]) < threshold and \
abs(c_pixel[1] - ic_pixel[1]) < threshold and \
abs(c_pixel[2] - ic_pixel[2]) < threshold:
return True
return False
def get_slice_gap(self, c_image, ic_image):
"""
获取缺口的偏移量
通过比较两张图片的所有像素点, 获取两张图片是从哪里开始不同
从而得到 移动块 要在 x 方向移动的距离
:param c_image: 完整的图片
:param ic_image: 有缺失的图片
:return: 缺口的偏移量
"""
for x in range(c_image.size[0]):
for y in range(c_image.size[1]):
if not self.is_pixel_similar(c_image, ic_image, x, y):
return x
def close(self):
self.driver.close()
def get_cookies(self, name):
"""
:param name: 手机/账号
:return:
"""
rel = ''
cookies = self.driver.get_cookies()
print(cookies)
for ck in cookies:
rel = rel + ck['name'] + "=" + ck['value'] + ";"
print(rel)
if __name__ == '__main__':
dl = BilibiliLogin("")
dl.sms_login("你的手机号")
dl.qr_login()
dl.pwd_login('你的手机号','你的密码')