python:selenium模拟B站模拟手机号,账号密码,二维码登录!

文章目录


前言

自我感觉唯一亮点就是滑块验证出现各种异常处理情况,完全实现自动化!


转载本文,请注明本文链接,谢谢!

直接上代码

写明了注释:

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('你的手机号','你的密码')
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值