您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

Python破解BiliBili滑块验证码,完美避开人机识别

时间:2020-06-18 17:26:38  来源:  作者:

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

作者:Mingyueyixi

PS:如有需要Python学习资料的小伙伴可以私信小编获取

Python破解BiliBili滑块验证码,完美避开人机识别

 

准备工作

  • B站登录页 https://passport.bilibili.com/login
  • python3
  • pip install selenium (webdriver框架
  • pip install PIL (图片处理)
  • chrome driver:http://chromedriver.storage.googleapis.com/index.html
  • firefox driver:https://github.com/mozilla/geckodriver/releases

B站的滑块验证码如上。

这类验证码可以使用 selenium 操作浏览器拖拽滑块来进行破解,难点两个,一个如何确定拖拽到的位置,另一个是避开人机识别(反爬虫)。

Python破解BiliBili滑块验证码,完美避开人机识别

 

确定滑块验证码需要拖拽的位移距离

有三种方式

  • 人工智能机器学习,确定滑块位置
  • 通过完整图片与缺失滑块的图片进行像素对比,确定滑块位置
  • 边缘检测算法,确定位置

各有优缺点。人工智能机器学习,确定滑块位置,需要进行训练,比较麻烦,也可以看是否存在在线api可以调用。以下介绍其他两种方式。

对比完整图片与缺失滑块的图片

仅介绍,本文不进行实现。对于B站来说,是准确率最高的方式(100%),但不能保证未来B站的滑块验证升级,导致不可用。

B站的滑块验证模块,一共有三张图片:完整图、缺失滑块图、滑块图,都是由画布绘制出的。类似于:

完整图:

Python破解BiliBili滑块验证码,完美避开人机识别

 

缺失滑块图:

Python破解BiliBili滑块验证码,完美避开人机识别

 

滑块图:

Python破解BiliBili滑块验证码,完美避开人机识别

 

HTML代码类似于:

<div class="geetest_canvas_img geetest_absolute" style="display: block;">
<div class="geetest_slicebg geetest_absolute">
	<canvas class="geetest_canvas_bg geetest_absolute" height="160" width="260"></canvas>
	<canvas class="geetest_canvas_slice geetest_absolute" width="260" height="160"></canvas>
</div>
<canvas class="geetest_canvas_fullbg geetest_fade geetest_absolute" height="160" width="260" style="display: none;"></canvas>
</div>

只需要通过selenium获取画布元素,执行js拿到画布像素,遍历完整图和缺失滑块图的像素,一旦获取到差异(需要允许少许像素误差),像素矩阵x轴方向即是滑块位置。

另外由于滑块图距离画布坐标原点有距离,还需要减去这部分距离。

最后使用 selenium 拖拽即可。

边缘检测算法,确定位置

滑块基本上是个方形,通过算法确定方形起始位置即可。

Python破解BiliBili滑块验证码,完美避开人机识别

 

介绍两种方式

  • 滑块是方形的,存在垂直与水平的边,该边在缺失滑块图中基本都是灰黑的。遍历像素找到基本都是灰黑的边即可。
  • 缺失滑块图中滑块位置是灰黑封闭的。通过算法可以找到封闭区域,大小与滑块相近,即是滑块需要拖拽到的位置。

第二种实现起来有些复杂,不进行实现了。

下面是第一种实现方式(只实现了垂直边的检测,水平边检测原理一致),会存在检测不出或错误的情况,使用时需要换一张验证码。也可能存在检测出的边是另一条(因为B站的滑块不是长方形,存在弧形边),那么需要减去滑块宽度

class VeriImageUtil():

    def __init__(self):
        self.defaultConfig = {
            "grayOffset": 20,
            "opaque": 1,
            "minVerticalLineCount": 30
        }
        self.config = copy.deepcopy(self.defaultConfig)

    def updateConfig(self, config):
        # temp = copy.deepcopy(config)
        for k in self.config:
            if k in config.keys():
                self.config[k] = config[k]

    def getMaxOffset(self, *args):
        # 计算偏移平均值最大的数
        av = sum(args) / len(args)

        maxOffset = 0
        for a in args:
            offset = abs(av - a)
            if offset > maxOffset:
                maxOffset = offset
        return maxOffset

    def isGrayPx(self, r, g, b):
        # 是否是灰度像素点,允许波动offset
        return self.getMaxOffset(r, g, b) < self.config["grayOffset"]

    def isDarkStyle(self, r, g, b):
        # 灰暗风格
        return r < 128 and g < 128 and b < 128

    def isOpaque(self, px):
        # 不透明
        return px[3] >= 255 * self.config["opaque"]

    def getVerticalLineOffsetX(self, bgImage):
        # bgImage = Image.open("./image/bg.png")
        # bgImage.im.mode = 'RGBA'
        bgBytes = bgImage.load()

        x = 0
        while x < bgImage.size[0]:
            y = 0
            # 点》》线,灰度线条数量
            verticalLineCount = 0

            while y < bgImage.size[1]:
                px = bgBytes[x, y]
                r = px[0]
                g = px[1]
                b = px[2]
                # alph = px[3]
                # print(px)
                if self.isDarkStyle(r, g, b) and self.isGrayPx(r, g, b) and self.isOpaque(px):
                    verticalLineCount += 1
                else:
                    verticalLineCount = 0
                    y += 1
                    continue

                if verticalLineCount >= self.config["minVerticalLineCount"]:
                    # 连续多个像素都是灰度像素,直线
                    # print(x, y)
                    return x

                y += 1

            x += 1
        pass


if __name__ == '__main__':
    bgImage = Image.open("./image/bg.png")
    veriImageUtil = VeriImageUtil()

    # veriImageUtil.updateConfig({
    #     "grayOffset": 20,
    #     "opaque": 0.6,
    #     "minVerticalLineCount": 10
    # })
        bgOffsetX = veriImageUtil.getVerticalLineOffsetX(bgImage)
    print("bgOffsetX:{} ".format(bgOffsetX))

使用selenium进行滑动验证(会失败)

首先,我们需要从html中获取滑块验证的图片,通过执行js,将画布像素转为base64,然后python即可获取,进行拖拽处理:

from selenium import webdriver
import time
import base64
from PIL import Image
from io import BytesIO
from selenium.webdriver.support.ui import WebDriverWait

def checkVeriImage(driver):    
    WebDriverWait(driver, 5).until(
        lambda driver: driver.find_element_by_css_selector('.geetest_canvas_bg.geetest_absolute'))
    time.sleep(1)
    im_info = driver.execute_script(
        'return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png");')
    # 拿到base64编码的图片信息
    im_base64 = im_info.split(',')[1]
    # 转为bytes类型
    im_bytes = base64.b64decode(im_base64)
    with open('./temp_bg.png', 'wb') as f:
        # 保存图片到本地,方便查看预览
        f.write(im_bytes)
        
    image_data = BytesIO(im_bytes)
    bgImage = Image.open(image_data)
    # 滑块距离左边有 5~10 像素左右误差
    offsetX = VeriImageUtil().getVerticalLineOffsetX(bgImage)
    eleDrag = driver.find_element_by_css_selector(".geetest_slider_button")
    action_chains = webdriver.ActionChains(driver)
    action_chains.drag_and_drop_by_offset(eleDrag,offsetX-10,0).perform()

貌似可以了,但实际上,验证时会遇到“拼图被怪物吃掉了,请重试”,导致失败。这是因为被检测到机器人(爬虫)操作了。

避开人机识别

B站滑块验证码的人机识别,其实不咋滴,主要靠是否存在停留间隔来判断。一开始被网上文章误导,弄了什么距离=初速度乘以时间t + 1/2加速度乘以(时间平方)模拟拖拽,实际上是完全不对路的。

Python破解BiliBili滑块验证码,完美避开人机识别

 

webdriver.ActionChains(driver).drag_and_drop_by_offset(eleDrag,offsetX-10,0).perform() 拖动滑块会导致验证失败。在B站中,这是因为这个动作太快了的缘故。有的同学就打算直接加 time.sleep(1) 了,这么做是不会成功的,会提示拼图被怪物吃掉了,请重试

实际上人做滑块验证的过程可以归为:手指快速拖拽验证码到指定位置,修正误差,停留一会儿,释放滑块。

简单实现

代码可以简单实现,都不需要模拟人修正拖拽误差的过程,普通网站不会去统计这个,至少B站不会。

    def simpleSimulateDragX(self, source, targetOffsetX):
        """
        简单拖拽模仿人的拖拽:快速沿着X轴拖动,直接一步到达正确位置,再暂停一会儿,然后释放拖拽动作
        B站是依据是否有暂停时间来分辨人机的,这个方法适用。
        :param source: 
        :param targetOffsetX: 
        :return: None
        """
		#参考`drag_and_drop_by_offset(eleDrag,offsetX-10,0)`的实现,使用move方法
        action_chains = webdriver.ActionChains(self.driver)
        # 点击,准备拖拽
        action_chains.click_and_hold(source)
        action_chains.pause(0.2)
        action_chains.move_by_offset(targetOffsetX,0)
        action_chains.pause(0.6)
        action_chains.release()
        action_chains.perform()

添加修正过程的实现

其实也就最后一段多出了fix的过程,
action_chains.move_by_offset(10,0)

    def fixedSimulateDragX(self, source, targetOffsetX):
		#参考`drag_and_drop_by_offset(eleDrag,offsetX-10,0)`的实现,使用move方法
        action_chains = webdriver.ActionChains(self.driver)
        # 点击,准备拖拽
        action_chains.click_and_hold(source)
        action_chains.pause(0.2)
        action_chains.move_by_offset(targetOffsetX-10,0)
        action_chains.pause(0.6)
        action_chains.move_by_offset(10,0)
        action_chains.pause(0.6)
        action_chains.release()
        action_chains.perform()

终极版实现

为了更像人类操作,可以进行拖拽间隔时间和拖拽次数、距离的随机化。虽然这对B站没什么用,还可能会导致验证时间变久一些。

拖拽多次,可以使用循环遍历,不过代码可能不好理解,直接判断就行,最多也就两到3次就完成修正误差的过程。

    def __getRadomPauseScondes(self):
        """
        :return:随机的拖动暂停时间
        """
        return random.uniform(0.6, 0.9)

    def simulateDragX(self, source, targetOffsetX):
        """
        模仿人的拖拽动作:快速沿着X轴拖动(存在误差),再暂停,然后修正误差
        防止被检测为机器人,出现“图片被怪物吃掉了”等验证失败的情况
        :param source:要拖拽的html元素
        :param targetOffsetX: 拖拽目标x轴距离
        :return: None
        """
        action_chains = webdriver.ActionChains(self.driver)
        # 点击,准备拖拽
        action_chains.click_and_hold(source)
        # 拖动次数,二到三次
        dragCount = random.randint(2, 3)
        if dragCount == 2:
            # 总误差值
            sumOffsetx = random.randint(-15, 15)
            action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
            # 暂停一会
            action_chains.pause(self.__getRadomPauseScondes())
            # 修正误差,防止被检测为机器人,出现图片被怪物吃掉了等验证失败的情况
            action_chains.move_by_offset(-sumOffsetx, 0)
        elif dragCount == 3:
            # 总误差值
            sumOffsetx = random.randint(-15, 15)
            action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
            # 暂停一会
            action_chains.pause(self.__getRadomPauseScondes())

            # 已修正误差的和
            fixedOffsetX = 0
            # 第一次修正误差
            if sumOffsetx < 0:
                offsetx = random.randint(sumOffsetx, 0)
            else:
                offsetx = random.randint(0, sumOffsetx)

            fixedOffsetX = fixedOffsetX + offsetx
            action_chains.move_by_offset(-offsetx, 0)
            action_chains.pause(self.__getRadomPauseScondes())

            # 最后一次修正误差
            action_chains.move_by_offset(-sumOffsetx + fixedOffsetX, 0)
            action_chains.pause(self.__getRadomPauseScondes())

        else:
            raise Exception("莫不是系统出现了问题?!")

        # 参考action_chains.drag_and_drop_by_offset()
        action_chains.release()
        action_chains.perform()

终章(完整代码)

Python破解BiliBili滑块验证码,完美避开人机识别

 

本文完整示例代码如下

# -*- coding: utf-8 -*-
# @Date:2020/2/15 2:09
# @Author: Lu
# @Description bilibili滑块验证码识别。B站有反爬限制,过快地拖拽会提示“怪物吃了拼图,请重试”。
# 目前B站有三张图片,只要对比完整图和缺失滑块背景图的像素,就可以得到偏移图片y轴距离,减去滑块空白距离=需要滑动的像素距离
# 这里采用边缘检测,检测缺失滑块的底图是否存在一条灰色竖线,即认为是滑块目标位置,存在失败的概率,适用范围应该更大些。


from selenium import webdriver
import time
import base64
from PIL import Image
from io import BytesIO
from selenium.webdriver.support.ui import WebDriverWait
import random
import copy


class VeriImageUtil():

    def __init__(self):
        self.defaultConfig = {
            "grayOffset": 20,
            "opaque": 1,
            "minVerticalLineCount": 30
        }
        self.config = copy.deepcopy(self.defaultConfig)

    def updateConfig(self, config):
        # temp = copy.deepcopy(config)
        for k in self.config:
            if k in config.keys():
                self.config[k] = config[k]

    def getMaxOffset(self, *args):
        # 计算偏移平均值最大的数
        av = sum(args) / len(args)

        maxOffset = 0
        for a in args:
            offset = abs(av - a)
            if offset > maxOffset:
                maxOffset = offset
        return maxOffset

    def isGrayPx(self, r, g, b):
        # 是否是灰度像素点,允许波动offset
        return self.getMaxOffset(r, g, b) < self.config["grayOffset"]

    def isDarkStyle(self, r, g, b):
        # 灰暗风格
        return r < 128 and g < 128 and b < 128

    def isOpaque(self, px):
        # 不透明
        return px[3] >= 255 * self.config["opaque"]

    def getVerticalLineOffsetX(self, bgImage):
        # bgImage = Image.open("./image/bg.png")
        # bgImage.im.mode = 'RGBA'
        bgBytes = bgImage.load()

        x = 0
        while x < bgImage.size[0]:
            y = 0
            # 点》》线,灰度线条数量
            verticalLineCount = 0

            while y < bgImage.size[1]:
                px = bgBytes[x, y]
                r = px[0]
                g = px[1]
                b = px[2]
                # alph = px[3]
                # print(px)
                if self.isDarkStyle(r, g, b) and self.isGrayPx(r, g, b) and self.isOpaque(px):
                    verticalLineCount += 1
                else:
                    verticalLineCount = 0
                    y += 1
                    continue

                if verticalLineCount >= self.config["minVerticalLineCount"]:
                    # 连续多个像素都是灰度像素,直线,认为需要滑动这么多
                    # print(x, y)
                    return x

                y += 1

            x += 1
        pass


class DragUtil():
    def __init__(self, driver):
        self.driver = driver

    def __getRadomPauseScondes(self):
        """
        :return:随机的拖动暂停时间
        """
        return random.uniform(0.6, 0.9)

    def simulateDragX(self, source, targetOffsetX):
        """
        模仿人的拖拽动作:快速沿着X轴拖动(存在误差),再暂停,然后修正误差
        防止被检测为机器人,出现“图片被怪物吃掉了”等验证失败的情况
        :param source:要拖拽的html元素
        :param targetOffsetX: 拖拽目标x轴距离
        :return: None
        """
        action_chains = webdriver.ActionChains(self.driver)
        # 点击,准备拖拽
        action_chains.click_and_hold(source)
        # 拖动次数,二到三次
        dragCount = random.randint(2, 3)
        if dragCount == 2:
            # 总误差值
            sumOffsetx = random.randint(-15, 15)
            action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
            # 暂停一会
            action_chains.pause(self.__getRadomPauseScondes())
            # 修正误差,防止被检测为机器人,出现图片被怪物吃掉了等验证失败的情况
            action_chains.move_by_offset(-sumOffsetx, 0)
        elif dragCount == 3:
            # 总误差值
            sumOffsetx = random.randint(-15, 15)
            action_chains.move_by_offset(targetOffsetX + sumOffsetx, 0)
            # 暂停一会
            action_chains.pause(self.__getRadomPauseScondes())

            # 已修正误差的和
            fixedOffsetX = 0
            # 第一次修正误差
            if sumOffsetx < 0:
                offsetx = random.randint(sumOffsetx, 0)
            else:
                offsetx = random.randint(0, sumOffsetx)

            fixedOffsetX = fixedOffsetX + offsetx
            action_chains.move_by_offset(-offsetx, 0)
            action_chains.pause(self.__getRadomPauseScondes())

            # 最后一次修正误差
            action_chains.move_by_offset(-sumOffsetx + fixedOffsetX, 0)
            action_chains.pause(self.__getRadomPauseScondes())


        else:
            raise Exception("莫不是系统出现了问题?!")

        # 参考action_chains.drag_and_drop_by_offset()
        action_chains.release()
        action_chains.perform()

    def simpleSimulateDragX(self, source, targetOffsetX):
        """
        简单拖拽模仿人的拖拽:快速沿着X轴拖动,直接一步到达正确位置,再暂停一会儿,然后释放拖拽动作
        B站是依据是否有暂停时间来分辨人机的,这个方法适用。
        :param source: 
        :param targetOffsetX: 
        :return: None
        """

        action_chains = webdriver.ActionChains(self.driver)
        # 点击,准备拖拽
        action_chains.click_and_hold(source)
        action_chains.pause(0.2)
        action_chains.move_by_offset(targetOffsetX, 0)
        action_chains.pause(0.6)
        action_chains.release()
        action_chains.perform()

def checkVeriImage(driver):
    WebDriverWait(driver, 5).until(
        lambda driver: driver.find_element_by_css_selector('.geetest_canvas_bg.geetest_absolute'))
    time.sleep(1)
    im_info = driver.execute_script(
        'return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png");')
    # 拿到base64编码的图片信息
    im_base64 = im_info.split(',')[1]
    # 转为bytes类型
    im_bytes = base64.b64decode(im_base64)
    with open('./temp_bg.png', 'wb') as f:
        # 保存图片到本地
        f.write(im_bytes)

    image_data = BytesIO(im_bytes)
    bgImage = Image.open(image_data)
    # 滑块距离左边有 5 像素左右误差
    offsetX = VeriImageUtil().getVerticalLineOffsetX(bgImage)
    print("offsetX: {}".format(offsetX))
    if not type(offsetX) == int:
        # 计算不出,重新加载
        driver.find_element_by_css_selector(".geetest_refresh_1").click()
        checkVeriImage(driver)
        return
    elif offsetX == 0:
        # 计算不出,重新加载
        driver.find_element_by_css_selector(".geetest_refresh_1").click()
        checkVeriImage(driver)
        return
    else:
        dragVeriImage(driver, offsetX)


def dragVeriImage(driver, offsetX):
    # 可能产生检测到右边缘的情况
    # 拖拽
    eleDrag = driver.find_element_by_css_selector(".geetest_slider_button")
    dragUtil = DragUtil(driver)
    dragUtil.simulateDragX(eleDrag, offsetX - 10)
    time.sleep(2.5)

    if isNeedCheckVeriImage(driver):
        checkVeriImage(driver)
        return
    dragUtil.simulateDragX(eleDrag, offsetX - 6)

    time.sleep(2.5)
    if isNeedCheckVeriImage(driver):
        checkVeriImage(driver)
        return
    # 滑块宽度40左右
    dragUtil.simulateDragX(eleDrag, offsetX - 56)

    time.sleep(2.5)
    if isNeedCheckVeriImage(driver):
        checkVeriImage(driver)
        return
    dragUtil.simulateDragX(eleDrag, offsetX - 52)

    if isNeedCheckVeriImage(driver):
        checkVeriImage(driver)
        return


def isNeedCheckVeriImage(driver):
    if driver.find_element_by_css_selector(".geetest_panel_error").is_displayed():
        driver.find_element_by_css_selector(".geetest_panel_error_content").click();
        return True
    return False


def task():
    # 此步骤很重要,设置chrome为开发者模式,防止被各大网站识别出来使用了Selenium
    # options = webdriver.ChromeOptions()
    # options.add_experimental_option('excludeSwitches', ['enable-automation'])

    options = webdriver.FirefoxOptions()

    # driver = webdriver.Firefox(executable_path=r"../../../res/webdriver/geckodriver_x64_0.26.0.exe",options=options)
    driver = webdriver.Firefox(executable_path=r"../../../res/webdriver/geckodriver_x64_0.26.0.exe",options=options)

    driver.get('https://passport.bilibili.com/login')
    time.sleep(3)

    driver.find_element_by_css_selector("#login-username").send_keys("1234567")
    driver.find_element_by_css_selector("#login-passwd").send_keys("abcdefg")
    driver.find_element_by_css_selector(".btn.btn-login").click()
    time.sleep(2)
    checkVeriImage(driver)

    pass


#   该方法用来确认元素是否存在,如果存在返回flag=true,否则返回false
def isElementExist(driver, css):
    try:
        driver.find_element_by_css_selector(css)
        return True
    except:
        return False


if __name__ == '__main__':
    task()


Tags:Python 滑块验证码   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
开头语: 除非是很简单的参数能够解决滑块问题或者追求效率的业务,否则的话,我还是会selenium来解决,并且接下来的验证码模式的话不需要使用到原图进行比较!!我发现很多网站都是基...【详细内容】
2020-09-25  Tags: Python 滑块验证码  点击:(167)  评论:(0)  加入收藏
前言本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。作者:MingyueyixiPS:如有需要Python学习资料的...【详细内容】
2020-06-18  Tags: Python 滑块验证码  点击:(34)  评论:(0)  加入收藏
▌简易百科推荐
大家好,我是菜鸟哥,今天跟大家一起聊一下Python4的话题! 从2020年的1月1号开始,Python官方正式的停止了对于Python2的维护。Python也正式的进入了Python3的时代。而随着时间的...【详细内容】
2021-12-28  菜鸟学python    Tags:Python4   点击:(1)  评论:(0)  加入收藏
学习Python的初衷是因为它的实践的便捷性,几乎计算机上能完成的各种操作都能在Python上找到解决途径。平时工作需要在线学习。而在线学习的复杂性经常让人抓狂。费时费力且效...【详细内容】
2021-12-28  风度翩翩的Python    Tags:Python   点击:(1)  评论:(0)  加入收藏
Python 是一个很棒的语言。它是世界上发展最快的编程语言之一。它一次又一次地证明了在开发人员职位中和跨行业的数据科学职位中的实用性。整个 Python 及其库的生态系统使...【详细内容】
2021-12-27  IT资料库    Tags:Python 库   点击:(2)  评论:(0)  加入收藏
菜单驱动程序简介菜单驱动程序是通过显示选项列表从用户那里获取输入并允许用户从选项列表中选择输入的程序。菜单驱动程序的一个简单示例是 ATM(自动取款机)。在交易的情况下...【详细内容】
2021-12-27  子冉爱python    Tags:Python   点击:(4)  评论:(0)  加入收藏
有不少同学学完Python后仍然很难将其灵活运用。我整理15个Python入门的小程序。在实践中应用Python会有事半功倍的效果。01 实现二元二次函数实现数学里的二元二次函数:f(x,...【详细内容】
2021-12-22  程序汪小成    Tags:Python入门   点击:(32)  评论:(0)  加入收藏
Verilog是由一个个module组成的,下面是其中一个module在网表中的样子,我只需要提取module名字、实例化关系。module rst_filter ( ...); 端口声明... wire定义......【详细内容】
2021-12-22  编程啊青    Tags:Verilog   点击:(8)  评论:(0)  加入收藏
运行环境 如何从 MP4 视频中提取帧 将帧变成 GIF 创建 MP4 到 GIF GUI ...【详细内容】
2021-12-22  修道猿    Tags:Python   点击:(6)  评论:(0)  加入收藏
面向对象:Object Oriented Programming,简称OOP,即面向对象程序设计。类(Class)和对象(Object)类是用来描述具有相同属性和方法对象的集合。对象是类的具体实例。比如,学生都有...【详细内容】
2021-12-22  我头秃了    Tags:python   点击:(9)  评论:(0)  加入收藏
所谓内置函数,就是Python提供的, 可以直接拿来直接用的函数,比如大家熟悉的print,range、input等,也有不是很熟,但是很重要的,如enumerate、zip、join等,Python内置的这些函数非常...【详细内容】
2021-12-21  程序员小新ds    Tags:python初   点击:(5)  评论:(0)  加入收藏
Hi,大家好。我们在接口自动化测试项目中,有时候需要一些加密。今天给大伙介绍Python实现各种 加密 ,接口加解密再也不愁。目录一、项目加解密需求分析六、Python加密库PyCrypto...【详细内容】
2021-12-21  Python可乐    Tags:Python   点击:(8)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条