python常用模块

模块:本质上就是一个.py文件

模块分为三部分:内置模块、第三方模块、自定义模块

一、time模块

  1.时间表示形式

  在python中,通常有三种方式来表示时间:

  (1)时间戳(timestamp):时间戳表示的是从1970年1月1日00:00:00开始按秒计算的,是folat类型。

  (2)格式化的时间字符串(Format String): ‘2017-06-21

  (3)结构化时间:共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)

  2.时间形式的转换

 

复制代码
import time
# time.time()    #仅仅显示当前时间的时间戳

# time.localtime()    #结构化时间对象     将时间戳转化为结构化时间

# time.gmtime()     #世界标准时间(UTC时间)

# time.mktime(time.localtime())   #将结构化时间转换成时间戳

# time.strftime('%Y-%m-%d',time.localtime())    #将结构化时间转换成字符串时间

# time.strptime('1990-03-12','%Y-%m-%d')         #将字符串时间转换为结构化时间

# time.asctime()      #将结构化时间----->字符串时间
# time.ctime()        #将时间戳----->字符串时间
复制代码

 二、random模块

复制代码
import random
print(random.random()) #(0,1)float类型

print(random.randint(1,3))     #[1,3]  int类型

print(random.randrange(1,3))      #[1,3) int类型,顾头不顾尾

print(random.choice([12,23,'hello']))       #随机取列表中的内容

print(random.sample([12,23,'hello',134],2))     #随机取列表中某两个内容

print(random.uniform(1,3))  #float 取1-3中的浮点整型

l=[11,22,33,44]
random.shuffle(l)       #打乱次序
print(l)

1. 生成一个6位数字+字母的验证码 (面试题)

import random

l = []
for i in range(6):
alpha = chr(random.randint(65,90))
alpha_lower = chr(random.randint(97,122))
num = str(random.randint(0,9))
ret = random.choice([alpha,num,alpha_lower])
l.append(ret)
print(''.join(l))

三、hashlib

  1、算法介绍

  python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

  摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为为一个长度固定的数据串(通常用16进制的字符串表示)。

  摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

  摘要算法制作一能指出数据是否被篡改过,就是因为摘要甘薯是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同

  以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

import hashlib

md5_obj=hashlib.md5()

md5_obj.update(b'hellowold')    #摘要内容

# digest返回内容hex返回形式(16进制)
print(md5_obj.hexdigest())      
#得到结果950a06c97a36396c573849ad7061ffcd

  如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的

md5 = hashlib.md5()
md5.update('how to use md5 in ')
md5.update('python hashlib?')
print md5.hexdigest()

  2.摘要算法的应用

  摘要算法应用:文件一致性校验,登录

  任何允许用户登录的网站都会存储用户登录的用户名和口令。如何存储用户名和口令呢?方法是存到数据库表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008

  如果以明文保存用户口令,如果数据库泄露,所有用户的口令就落入黑客的手里。此外,网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

  考虑这么个情况,很多用户喜欢用123456,888888,password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:

'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'

  这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。

  对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护呢?

  由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

hashlib.md5("salt".encode("utf8"))

  经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

  但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

  如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

  摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

四、os模块

  os模块是与操作系统交互的一个接口

  

复制代码
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.makedirs('dirname1/dirname2')    可生成多层递归目录
os.removedirs('dirname1')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname')    生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat('path/filename')  获取文件/目录信息
os.path.abspath(path)  返回path规范化的绝对路径
os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path)  返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
复制代码

 五、sys模块

复制代码
sys.argv           命令行参数List,第一个元素是程序本身路径,之后可以添加元素,python会以列表的方式返回
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
复制代码

六、logging模块

1.函数式简单配置

复制代码
import logging  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')  
复制代码

默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。

灵活配置日志级别,日志格式,输出位置:

 
import logging  
logging.basicConfig(level=logging.DEBUG,  
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
                    datefmt='%a, %d %b %Y %H:%M:%S',  
                    filename='/tmp/test.log',  
                    filemode='w')  
  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message')

配置参数 

logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:

filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息

配置参数

 

2.logger对象配置

import logging

logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log')

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

fh.setFormatter(formatter)        #为输出流设置格式
ch.setFormatter(formatter)

logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
logger.addHandler(ch)

logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')

logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别。

七、序列化模块

  1.什么是序列化

  我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

  2.json模块

  如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

  JSON表示的对象就是标准的JavaScript语言的对象一个子集,JSON和Python内置的数据类型对应如下:

  

import json

json.dumps()    #序列化
json.loads()    #反序列化
#=====================序列化============
dic={'name':'sam',"age":18}

f=open('json_data.txt','w')

data=json.dumps(dic)
print(data)
print(type(data))
f.write(data)
f.close()
#====================================== json.dump(dic,f) #1.data=json.dumps(dic) 2.f.write(data) #==================反序列化============= f=open('json_data.txt','r') data=f.read() dic=json.loads(data) print(dic) print(type(dic))
#========================================= json.load(f) #1.data=f.read() 2.dic=json.loads(data) #=================反序列化练习======= import json f=open("test",'r') data=f.read() dic=json.loads(data) print(dic) print(type(dic))

 八、re模块

  就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

  字符匹配(普通字符,元字符):

  1 普通字符:大多数字符和字母都会和自身匹配
              >>> re.findall('alvin','yuanaleSxalexwupeiqi')
                      ['alvin'] 

  2 元字符:. ^ $ * + ? { } [ ] | ( ) \

  一、元字符

  1.表示开头结尾的元字符
  ^---------从字符串开始位置匹配,加在元素前面
  $---------从字符串结尾位置匹配,加在元素后面
  .(点)-------通配符,匹配除换行符(/n)以外的任何字符
  如果想让点匹配换行符,可以使用re.S切换模式来实现
 
  2.表示重复的元字符
  *(星) [0,无穷]------将前面的符号匹配0到无穷次
  +     [1,无穷]------将前面的符号匹配1到无穷次
  ?    [0,1]---------将前面的符号匹配0到1次
  {}     [n,m]---------将前面的符号匹配n到m次    也可指定一个数字表示匹配n次
  {} 可以模拟上面的*+?
import re

ret1=re.findall('李.*','李杰\nalex\n李莲英\negon\nalvin\n李二棍子')
ret2=re.findall('李.+','李杰\nalex\n李莲英\negon\nalvin\n李二棍子')

ret3=re.findall('(李.{1,2})\n','李杰\nalex\n李莲英\negon\nalvin\n李二棍子') # 设定优先级的原因

# 匹配一个数字包括整型和浮点型
ret4=re.findall('\d+\.?\d*','12.45,34,0.05,109')

print(ret4)
  贪婪匹配:有多少匹配多少
  非贪婪匹配:\d+?把所有的数字当做一个数字去匹配
  3.转义符:\
  (1)反斜杠后边跟元字符去除特殊功能,比如\.
  (2)反斜杠后边跟普通字符实现特殊功能,比如\d
\d  匹配任何十进制数;      它相当于类 [0-9]。
\D  匹配任何非数字字符;    它相当于类 [^0-9]。
\s  匹配任何空白字符;      它相当于类 [ \t\n\r\f\v]。
\S  匹配任何非空白字符;    它相当于类 [^ \t\n\r\f\v]。
\w  匹配任何字母数字字符;   它相当于类 [a-zA-Z0-9_]。
\W  匹配任何非字母数字字符; 它相当于类 [^a-zA-Z0-9_]
\b  匹配一个特殊字符边界,比如空格 ,&,#等

  \b的应用:

ret=re.findall(r'I\b','I am LIST')
print(ret)#['I']
  二、分组:()
  ():可以把字符分组,匹配完成之后只显示分组内的内容
  如果想要显示全部匹配到的内容(?:)要在括号里加?:,就是取消优先级
  命名分组:(?P<分组名字>分组内容)
m = re.findall(r'(ad)+', 'add')
print(m)
 
ret=re.search('(?P<id>\d{2})/(?P<name>\w{3})','23/com')
print(ret.group())#23/com
print(ret.group('id'))#23
 
  三、字符集
  [ ]:字符集:用来表示或的意思,在字符集里可以有多个字符
 
  #注意:*,+.等元字符在字符集里都是普通符号,
 
  还有特殊意义的有:
  -(杠)----------可以代表范围:如[1-9]
  ^ ----------在字符集里代表取反的意思
  \  ----------转义
#--------------------------------------------字符集[]
ret=re.findall('a[bc]d','acd')
print(ret)#['acd']
 
ret=re.findall('[a-z]','acd')
print(ret)#['a', 'c', 'd']
 
ret=re.findall('[.*+]','a.cd+')
print(ret)#['.', '+']
 
#在字符集里有功能的符号: - ^ \
 
ret=re.findall('[1-9]','45dha3')
print(ret)#['4', '5', '3']
 
ret=re.findall('[^ab]','45bdha3')
print(ret)#['4', '5', 'd', 'h', '3']
 
ret=re.findall('[\d]','45bdha3')
print(ret)#['4', '5', '3']

  四、贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配
string pattern1 = @"a.*c";   // greedy match 
Regex regex = new Regex(pattern1);
regex.Match("abcabc"); // return "abcabc"
非贪婪匹配:在满足匹配时,匹配尽可能短的字符串,使用?来表示非贪婪匹配

string pattern1 = @"a.*?c";   // non-greedy match 
Regex regex = new Regex(pattern1);
regex.Match("abcabc"); // return "abc"
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法:
--------------------------------

. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x

就是取前面任意长度的字符,直到一个x出现
  五、re模块下的常用方法
import re

re.findall('a','alvin yuan')    #返回所有满足匹配条件的结果,放在列表里

re.search('a','alvin yuan').group()  

#函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以
# 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
 


re.match('a','abc').group()     #同search,不过尽在字符串开始处进行匹配
 

ret=re.split('[ab]','abcd')     #先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割

print(ret)#['', '', 'cd']
 

ret=re.sub('\d','abc','alvin5yuan6',1)

ret=re.subn('\d','abc','alvin5yuan6')



obj=re.compile('\d{3}')
ret=obj.search('abc123eeee')
print(ret.group())#123


import re
ret=re.finditer('\d','ds3sy4784a')
print(ret)        #<callable_iterator object at 0x10195f940>
 
print(next(ret).group())
print(next(ret).group())

注意:

1 findall的优先级查询:

import re
 
ret=re.findall('www.(baidu|oldboy).com','www.oldboy.com')
print(ret)#['oldboy']     这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
 
ret=re.findall('www.(?:baidu|oldboy).com','www.oldboy.com')
print(ret)#['www.oldboy.com']

2 split的优先级查询

ret=re.split("\d+","yuan2egon56alex")
print(ret)

ret=re.split("(\d+)","yuan2egon56alex")
print(ret)



爬虫

import requests

import re
import json

def getPage(url):

    response=requests.get(url)
    return response.text

def parsePage(s):
    
    com=re.compile('<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
                   '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',re.S)

    ret=com.finditer(s)
    for i in ret:
        yield {
            "id":i.group("id"),
            "title":i.group("title"),
            "rating_num":i.group("rating_num"),
            "comment_num":i.group("comment_num"),
        }

def main(num):

    url='https://movie.douban.com/top250?start=%s&filter='%num
    response_html=getPage(url)
    ret=parsePage(response_html)
    print(ret)
    f=open("move_info7","a",encoding="utf8")

    for obj in ret:
        print(obj)
        data=json.dumps(obj,ensure_ascii=False)
        f.write(data+"\n")

if __name__ == '__main__':
    count=0
    for i in range(10):
        main(count)
        count+=25

爬虫练习

  

 

 

 

 

 

 

 

 

九、configparser模块

  该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)。

  1.创建文件

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
  
[bitbucket.org]
User = hg
  
[topsecret.server.com]
Port = 50022
ForwardX11 = no

如果想用python生成一个这样的文档怎么做呢?

import configparser

config = configparser.ConfigParser()

config["DEFAULT"] = {'ServerAliveInterval': '45',
                      'Compression': 'yes',
                     'CompressionLevel': '9',
                     'ForwardX11':'yes'
                     }

config['bitbucket.org'] = {'User':'hg'}

config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}

with open('example.ini', 'w') as configfile:

   config.write(configfile)

  2.查找文件

import configparser

config = configparser.ConfigParser()

#---------------------------查找文件内容,基于字典的形式

print(config.sections())        #  []

config.read('example.ini')

print(config.sections())        #   ['bitbucket.org', 'topsecret.server.com']

print('bytebong.com' in config) # False
print('bitbucket.org' in config) # True


print(config['bitbucket.org']["user"])  # hg

print(config['DEFAULT']['Compression']) #yes

print(config['topsecret.server.com']['ForwardX11'])  #no


print(config['bitbucket.org'])          #<Section: bitbucket.org>

for key in config['bitbucket.org']:     # 注意,有default会默认default的键
    print(key)

print(config.options('bitbucket.org'))  # 同for循环,找到'bitbucket.org'下所有键

print(config.items('bitbucket.org'))    #找到'bitbucket.org'下所有键值对

print(config.get('bitbucket.org','compression')) # yes       get方法取深层嵌套的值

  3.增删改操作

import configparser

config = configparser.ConfigParser()

config.read('example.ini')

config.add_section('yuan')



config.remove_section('bitbucket.org')
config.remove_option('topsecret.server.com',"forwardx11")


config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')

config.write(open('new2.ini', "w"))

 

十、subprocess模块

  subprocess模块允许一个进程创建一个新的子进程,通过管道连接到子进程的stdin/stdout/stderr,获取子进程的返回值等操作。 

  1.简单命令

 
import subprocess

#  创建一个新的进程,与主进程不同步  if in win: s=subprocess.Popen('dir',shell=True)
s=subprocess.Popen('ls')
s.wait()                  # s是Popen的一个实例对象

print('ending...')     
 

  2.命令带参数

  linux:

import subprocess

subprocess.Popen('ls -l',shell=True)

#subprocess.Popen(['ls','-l'])

  3.控制子进程

  当我们想要更个性化我们的需求的时候,就要转向Popen类,该类生成的对象用来代表子进程。刚才我们使用到了一个wait方法

  此外,你还可以在父进程中对子进程进行其它操作:

 
s.poll() # 检查子进程状态
s.kill() # 终止子进程
s.send_signal() # 向子进程发送信号
s.terminate() # 终止子进程

s.pid:子进程号
 

  4.子进程文本流控制

  可以在Popen()建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用    subprocess.PIPE将多个子进程的输入和输出连接在一起,构成管道(pipe):

import subprocess

# s1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
# print(s1.stdout.read())



#s2.communicate()

s1 = subprocess.Popen(["cat","/etc/passwd"], stdout=subprocess.PIPE)
s2 = subprocess.Popen(["grep","0:0"],stdin=s1.stdout, stdout=subprocess.PIPE)
out = s2.communicate()

print(out)

  ubprocess.PIPE实际上为文本流提供一个缓存区。s1的stdout将文本输出到缓存区,随后s2的stdin从该PIPE中将文本读取走。s2的输出文本也被存放在PIPE中,直到communicate()方法从PIPE中读取出PIPE中的文本。
  注意:communicate()是Popen对象的一个方法,该方法会阻塞父进程,直到子进程完成

转载于:https://www.cnblogs.com/gpd-Amos/p/7872724.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值