Python 与 数据存储

Python 与 数据存储

本文为<Python开发基础>系列文章中的一篇,系列目录:

  1. 系列目录
  2. Python 与 http请求
  3. Python 与 html解析
  4. Python 与 数据存储
  5. Python 与 网络爬虫
  6. Python 与 Web服务(待完成)

本文由 CDFMLR 原创,收录于个人主页 https://clownote.github.io,并同时发布到 CSDN。本人不保证 CSDN 排版正确,敬请访问 clownote 以获得良好的阅读体验。

CSV

CSV 简介

CSV(Comma-Separated Values),逗号分隔值,或字符分隔值,以纯文本形式储存表格数据。一个CSV文件的内容如下:

id,name,age
0,foo,10
1,bar,11
2,foobar,100

Python 写 CSV

列表写入 CSV
import csv

d = [['0', 'foo', '10'], ['1', 'bar', '11'], ['2', 'foobar', '100']]
with open('data.csv', 'w') as csvfile:
    writer = csv.writer(csvfile)    # 要改分隔符,可以传入`delimiter='分隔符'`
    
    writer.writerow(['id', 'name', 'age'])  # 写一行,传入list
    writer.writerows(d)     # 写入多行,传入二维list

⚠️【注意】写中文的时候,要在open的时候传入 encoding='utf-8',防止乱码。

字典写入 CSV
import csv

with open('data.csv', 'w') as csvfile:
    fieldnames = ['id', 'name', 'age']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({'id': '9', 'name': 'fuzz', 'age': '666'})

Python 读 CSV

用 CSV库 读取
import csv

with open('data.csv', 'r', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        print(type(row), row)

从结果中可以看到,读取出来的是各行的list。

用 Pandas库 读取
import pandas as pd

df = pd.read_csv('data.csv')
print(df)
print(type(df))

MySQL

连接数据库

我们现在来连接位于本地的MySQL数据库,在这之前要先确定本地的MYSQL正在运行,且处于可访问的状态。

import pymysql  # 导入库

db = pymysql.connect(host='localhost', user='*', password='***') # 连接数据库,还可以传入 port = 整数值 来指定端口,这里使用默认值(3306)。
cursor = db.cursor()    # 获取 MySQL 操作游标,利用操作游标才能执行SQL语句
cursor.execute('SELECT VERSION()')  # 执行SQL语句
data = cursor.fetchone()    # 获取第一条数据
print("Database Version:", data)
cursor.execute("CREATE DATABASE spiders DEFAULT CHARACTER SET utf8")    # 新建一个用来爬虫的数据库
db.close()

创建表

import pymysql

db = pymysql.connect(host="localhost", user="*", password="***", port=3306, db='spiders')    # 直接连接到刚才新建的数据库
cursor = db.cursor()
sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age INT NOT NULL, PRIMARY KEY (id))'
cursor.execute(sql)
db.close()

插入数据

使用一系列变量插入
import pymysql

id = '2099133201'
name = 'Foo.Bar'
age = 101

db = pymysql.connect(host='localhost', user='*', password='***', db='spiders')
cursor = db.cursor()

sql = 'INSERT INTO students (id, name, age) VALUES (%s, %s, %s)'

try:
    cursor.execute(sql, (id, name, age))
    db.commit()
except:
    db.rollback()

db.close()

⚠️【注意】:

  1. 我们在execute的时候并不必使用python的方法把字符串提前构造好,可以在需要插入的地方使用 %s,然后运行execute的时候把参数列表用一个元组传入即可,然而,这样做的重点是:避免引号的问题!
  2. 注意MySQL的事务机制,对于数据库的 插入、更新、删除 操作的标准模版是:
try:
    cursor.execute(‘更改操作语句’)
    db.commit()
except:
    db.rollback()
使用字典插入
import pymysql

db = pymysql.connect(host='localhost', user='*', password='***', db='spiders')
cursor = db.cursor()

data = {
        'id': '2099133202',
        'name': 'Fuzz.Buzz',
        'age': '104'
        }

table = 'students'

keys = ', '.join(data.keys())
values = ', '.join(['%s'] * len(data))

sql = 'INSERT  INTO {table} ({keys}) VALUES ({values})'.format(table=table, keys=keys, values=values)

try:
    if cursor.execute(sql, tuple(data.values())):
        print("Success!")
        db.commit()
except Exception as e:
    print('Failed:\n', e)
    db.rollback()

db.close()

在这个例子中如果不使用 %s + 传入 tuple 的那个方法动态构造语句,而直接用 values = ', '.join(data.values()) 然后 cursor.execute(sql) 就会 Failed 了。

更新数据

简单的数据更新
sql = 'UPDATE students SET age = %s WHERE name = %s'

try:
    cursor.execute(sql, (999, 'Foo.Bar'))
    db.commit()
except:
    db.rollback()

db.close()
更实用的字典更新

在实际数据抓取过程中,如果出现了重复数据,我们更希望的做法是更新数据:
如果重复则更新数据,如果数据不存在则插入数据,另外支持灵活的字典传值。

import pymysql

db = pymysql.connect(host='localhost', user='*', password='***', db='spiders')
cursor = db.cursor()

data = {
        'id': '2099133203',
        'name': 'SomeOne',
        'age': '0'
        }

table = 'students'

keys = ', '.join(data.keys())
values = ', '.join(['%s'] * len(data))

sql = 'INSERT INTO {table} ({keys}) VALUES ({values}) ON DUPLICATE KEY UPDATE'.format(table=table, keys=keys, values=values)
update = ', '.join([' {key} = %s'.format(key=key) for key in data])

sql += update

try:
    if cursor.execute(sql, tuple(data.values()) * 2):
        print("Success!")
        db.commit()
except Exception as e:
    print('Failed:\n', e)
    db.rollback()

db.close()

删除数据

import pymysql

db = pymysql.connect(host='localhost', user='*', password='***', db='spiders')
cursor = db.cursor()

table = 'students'
condition = 'age > 200'

sql = 'DELETE FROM {table} WHERE {condition}'.format(table=table, condition=condition)

try:
    cursor.execute(sql)
    db.commit()
except:
    db.rollback()

db.close()

查询数据

sql = 'SELECT * FROM students'

try:
    cursor.execute(sql)
    print('Count:', cursor.rowcount)
    one = cursor.fetchone()
    print('One:', one)
    results = cursor.fetchall()
    print('Results:', results)
    print('Results Type:', type(results))
    for row in results:
        print(row)
except:
    print('Error')

MongoDB

MongoDB 简介

MongoDB 是由 C++ 语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似 Json 对象,它的字段值可以包含其他文档,数组及文档数组,非常灵活。

在使用前要先确定 MongoDB 安装好,并已经开启了服务:

$ brew services start mongodb
$ sudo mongod
$ mongo

以上三条命令开启了 MongoDB 的服务,并打开客户端。

连接数据库

import pymongo
client = pymongo.MongoClient(host='localhost', port=27017)
# client = pymongo.MongoClient('mongodb://localhost:27017/')    效果是一样的

指定数据库

db = client.test
# db = client['test']   等价

指定集合

collection = db.students    # 还是可以用`['']`来取

插入数据

插入数据有3种方法:

  • collection.insert():插入一个或多个数据,传入一个对象则插入一个,传入一个对象的list可以插入多个(官方不推荐使用);
  • collection.insert_one():插入一个数据,传入一个dict;
  • collection.insert_many():插入多个数据,传入一个dict的list;
import pymongo

client = pymongo.MongoClient(host='localhost', port=27017)
db = client.test
collection = db.students

student1 = {
        'id': '2099133201',
        'name': 'Foo',
        'age': 10,
        'gender': 'male'
        }

student2 = {
        'id': '2099133202',
        'name': 'Bar',
        'age': 11,
        'gender': 'male'
        }

student3 = {
        'id': '2099133203',
        'name': 'Moo',
        'age': 12,
        'gender': 'female'
        }

result0 = collection.insert_one(student1)
result1 = collection.insert_many([student2, student3])
print(result0, result0.inserted_id, sep='\n')
print(result1, result1.inserted_ids, sep='\n')

查询

查询使用如下方法:

  • find_one():查得单个结果
  • find():返回一个结果的生成器

如果查询的结果不存在,会返回 None

res_of_find = collection.find()     # 得到所有数据
print('find:\n', res_of_find)
for i in res_of_find:
    print(i)

res_of_findone = collection.find_one({'name': 'Foo'})   # 通过传入一组字典键值来查询
print('find_one:\n', res_of_findone)

如果要通过 MongoDB 为每条数据添加的 _id 属性来查询,需要这样做:

res_of_find_by_id = collection.find_one({'_id': ObjectId('5c78e0c6b92a4e5f17d70cfa')})
print('find by id:\n', res_of_find_by_id)

如果要查询 “年龄大于10” 的数据:

collection.find({'age': {'$gt': 10}})

这里查询条件的value变成了一个符号加运算值的字典,可用的操作如下:

符号含义示例
$lt小于{'age': {'$lt': 20}}
$gt大于{'age': {'$gt': 20}}
$lte小于等于{'age': {'$lte': 20}}
$gte大于等于{'age': {'$gte': 20}}
$ne不等于{'age': {'$ne': 20}}
$in在范围内{'age': {'$in': [20, 23]}}
$nin不在范围内{'age': {'$nin': [20, 23]}}

还有多种查询方式:

collection.find({'name': {'$regex': '^M.*'}})

常用操作如下:

符号含义示例示例含义
$regex匹配正则{'name': {'$regex': '^M.*'}}name 以 M开头
$exists属性是否存在{'name': {'$exists': True}}name 属性存在
$type类型判断{'age': {'$type': 'int'}}age 的类型为 int
$mod数字模操作{'age': {'$mod': [5, 0]}}年龄模 5 余 0
$text文本查询{'$text': {'$search': 'Mike'}}text 类型的属性中包含 Mike 字符串
$where高级条件查询{'$where': 'obj.fans_count == obj.follows_count'}自身粉丝数等于关注数

去掉 _id:

Mongo 返回的数据中会有一项 _id (Mongo自动加入的用来识别对象的字段),我们常不想看它 ,可以在查询时加入对结果的过滤:

collection.find(projection={'_id': False})

计数

要统计查询到的结果条数,可以直接调用查询结果的 count() 方法。

collection.find({'age': {'$gt': 0}}).count()

P.S. 一点题外话:

写到这里,又是一个熄灯后的美丽夜晚。调暗屏幕背光,小心翼翼地捧起那蠢蠢欲动的白色樱桃机械键盘,把它安置到光明之外,避开红绿蓝的纷争。趁着这五彩缤纷的黑暗,把下一行让人迷醉的代码,作为虔诚的礼物,送给饥饿良久的编译器。静待她施展动人的魔法,变出一串没有间断点的二进制位向量,在 0 和 1 的排列组合中,光影流转,似现星河鹭起,似有高山流水。在心驰神往的一瞬,不妨再分个神,欣赏开和关之间,一扇回到未来的古朴大门。


排序

我们可以通过调用 sort() 方法,传入用来排序的字段、升降序的标志即可对查询的结果排序。

  • pymongo.ASCENDING:指定升序;
  • pymongo.DESCENDING:指定降序;
r = collection.find().sort('name', pymongo.ASCENDING)
for i in r:
    print(i['name'])

偏移

偏移:偏移x个元素,即忽略(跳过)前面的x个元素,得到第x+1开始个元素。
这个操作调用查询结果的 skip() 方法。

r = collection.find().skip(2)
print([i for i in r])

更新

更新与插入类似,主要有三种方法:

  • update():更新一条或多条数据,不推荐使用
  • update_one():更新一条数据
  • update_many():更新多条数据
condition = {'name': 'Foo'}
student = collection.find_one(condition)
student['age'] = 25
result = collection.update(condition, {'$set': student})
print(result)

这里,我们先查询出了目标条目,然后把它修改成了新的数据,调用update,传入查询条件和一个代表操作的字典完成更新。
常用的操作有:

  • $set: {'$set': value}: 把value中有的属性更新成新的
  • $inc: {'$inc': {'age': 1}}: 把age加1

删除

也是有三个:remove()delete_one()delete_many()

collection.remove({'name': 'Kevin'})

更多

更完善的MongoDB还在官网等待我们去学习:官方文档🔗链接

Redis

Redis 简介

Redis 是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构。

RedisPy 库提供两个类 Redis 和 StrictRedis 用于实现Redis 的命令操作。
推荐使用的是 StrictRedis,它实现了绝大部分Redis的命令,参数也一一对应。
Redis 主要用来兼容旧版本。

连接 Redis

首先需要确定正确在本地安装了Redis,并开启了服务。

使用 StrictRedis 连接
from redis import StrictRedis

redis = StrictRedis(host='localhost', port=6379, db=0, password=None)
# 这里使用的这几个参数和默认值是等效的,即可以直接使用 `StrictRedis()` 来代替。

redis.set('name', 'Foo')    # 设置了一个键值对
print(redis.get('name'))    # 获取一个数据
使用 ConnectionPool 连接
from redis import StrictRedis, ConnectionPool

pool = ConnectionPool(host='localhost', port=6379, db=0, password=None)
redis = StrictRedis(connection_pool=pool)

redis.set('age', 100)
print(redis.get('age'))

使用 ConnectionPool 还可以用 URL 来构建:

有三种可用的 URL 格式:

  • Redis TCP 连接:redis://[:password]@host:port/db
  • Redis TCP+SSL 连接:rediss://[:password]@host:port/db
  • Redis Unix Socket连接:unix://[:password]@/path/to/socket.sock?db=db

使用的姿势如下:

from redis import StrictRedis, ConnectionPool
# 注意多导入了一个ConnectionPool。

url = 'redis://@localhost:6379/0'
pool = ConnectionPool.from_url(url)
redis = StrictRedis(connection_pool=pool)

redis.set("test", 'test')
r = redis.get('test')
print(type(r), r, sep='\n')

Key 操作

这是一些常用的对 key 的操作,通过使用如 redis.exists('neme') 这样的语句来使用他们:

方法作用参数说明示例示例说明示例结果
exists(name)判断一个key是否存在name: key名redis.exists(‘name’)是否存在name这个key1
delete(name)删除一个keyname: key名redis.delete(‘name’)删除name这个key1
type(name)判断key类型name: key名redis.type(‘name’)判断name这个key类型b’string’
keys(pattern)获取所有符合规则的keypattern: 匹配规则redis.keys(‘n*’)获取所有以n开头的key[b’name’]
randomkey()获取随机的一个keyrandomkey()获取随机的一个keyb’name’
rename(src, dst)将key重命名src: 原key名 dst: 新key名redis.rename(‘name’, ‘nickname’)将name重命名为nicknameTrue
dbsize()获取当前数据库中key的数目dbsize()获取当前数据库中key的数目100
expire(name, time)设定key的过期时间,单位秒name: key名 time: 秒数redis.expire(‘name’, 2)将name这key的过期时间设置2秒True
ttl(name)获取key的过期时间,单位秒,-1为永久不过期name: key名redis.ttl(‘name’)获取name这key的过期时间-1
move(name, db)将key移动到其他数据库name: key名 db: 数据库代号move(‘name’, 2)将name移动到2号数据库True
flushdb()删除当前选择数据库中的所有keyflushdb()删除当前选择数据库中的所有keyTrue
flushall()删除所有数据库中的所有keyflushall()删除所有数据库中的所有keyTrue

String 操作

String 是 Redis 中最基本的Value储存方式,他的操作如下,

方法作用参数说明示例示例说明示例结果
set(name, value)给数据库中key为name的string赋予值valuename: key名 value: 值redis.set(‘name’, ‘Bob’)给name这个key的value赋值为BobTrue
get(name)返回数据库中key为name的string的valuename: key名redis.get(‘name’)返回name这个key的valueb’Bob’
getset(name, value)给数据库中key为name的string赋予值value并返回上次的valuename: key名 value: 新值redis.getset(‘name’, ‘Mike’)赋值name为Mike并得到上次的valueb’Bob’
mget(keys, *args)返回多个key对应的valuekeys: key的列表redis.mget([‘name’, ‘nickname’])返回name和nickname的value[b’Mike’, b’Miker’]
setnx(name, value)如果key不存在才设置valuename: key名redis.setnx(‘newname’, ‘James’)如果newname这key不存在则设置值为James第一次运行True,第二次False
setex(name, time, value)设置可以对应的值为string类型的value,并指定此键值对应的有效期name: key名 time: 有效期 value: 值redis.setex(‘name’, 1, ‘James’)将name这key的值设为James,有效期1秒True
setrange(name, offset, value)设置指定key的value值的子字符串name: key名 offset: 偏移量 value: 值redis.set(‘name’, ‘Hello’) redis.setrange(‘name’, 6, ‘World’)设置name为Hello字符串,并在index为6的位置补World11,修改后的字符串长度
mset(mapping)批量赋值mapping: 字典redis.mset({‘name1’: ‘Durant’, ‘name2’: ‘James’})将name1设为Durant,name2设为JamesTrue
msetnx(mapping)key均不存在时才批量赋值mapping: 字典redis.msetnx({‘name3’: ‘Smith’, ‘name4’: ‘Curry’})在name3和name4均不存在的情况下才设置二者值True
incr(name, amount=1)key为name的value增值操作,默认1,key不存在则被创建并设为amountname: key名 amount:增长的值redis.incr(‘age’, 1)age对应的值增1,若不存在则会创建并设置为11,即修改后的值
decr(name, amount=1)key为name的value减值操作,默认1,key不存在则被创建并设置为-amountname: key名 amount:减少的值redis.decr(‘age’, 1)age对应的值减1,若不存在则会创建并设置为-1-1,即修改后的值
append(key, value)key为name的string的值附加valuekey: key名redis.append(‘nickname’, ‘OK’)向key为nickname的值后追加OK13,即修改后的字符串长度
substr(name, start, end=-1)返回key为name的string的value的子串name: key名 start: 起始索引 end: 终止索引,默认-1截取到末尾redis.substr(‘name’, 1, 4)返回key为name的值的字符串,截取索引为1-4的字符b’ello’
getrange(key, start, end)获取key的value值从start到end的子字符串key: key名 start: 起始索引 end: 终止索引redis.getrange(‘name’, 1, 4)返回key为name的值的字符串,截取索引为1-4的字符b’ello’

List 操作

List 是 Redis 中 value 为列表的一种数据。

方法作用参数说明示例示例说明示例结果
rpush(name, *values)在key为name的list尾添加值为value的元素,可以传多个name: key名 values: 值redis.rpush(‘list’, 1, 2, 3)给list这个key的list尾添加1、2、33,list大小
lpush(name, *values)在key为name的list头添加值为value的元素,可以传多个name: key名 values: 值redis.lpush(‘list’, 0)给list这个key的list头添加04,list大小
llen(name)返回key为name的list的长度name: key名redis.llen(‘list’)返回key为list的列表的长度4
lrange(name, start, end)返回key为name的list中start至end之间的元素name: key名 start: 起始索引 end: 终止索引redis.lrange(‘list’, 1, 3)返回起始为1终止为3的索引范围对应的list[b’3’, b’2’, b’1’]
ltrim(name, start, end)截取key为name的list,保留索引为start到end的内容name:key名 start: 起始索引 end: 终止索引ltrim(‘list’, 1, 3)保留key为list的索引为1到3的元素True
lindex(name, index)返回key为name的list中index位置的元素name: key名 index: 索引redis.lindex(‘list’, 1)返回key为list的列表index为1的元素b’2’
lset(name, index, value)给key为name的list中index位置的元素赋值,越界则报错name: key名 index: 索引位置 value: 值redis.lset(‘list’, 1, 5)将key为list的list索引1位置赋值为5True
lrem(name, count, value)删除count个key的list中值为value的元素name: key名 count: 删除个数 value: 值redis.lrem(‘list’, 2, 3)将key为list的列表删除2个31,即删除的个数
lpop(name)返回并删除key为name的list中的首元素name: key名redis.lpop(‘list’)返回并删除名为list的list第一个元素b’5’
rpop(name)返回并删除key为name的list中的尾元素name: key名redis.rpop(‘list’)返回并删除名为list的list最后一个元素b’2’
blpop(keys, timeout=0)返回并删除名称为在keys中的list中的首元素,如果list为空,则会一直阻塞等待keys: key列表 timeout: 超时等待时间,0为一直等待redis.blpop(‘list’)返回并删除名为list的list的第一个元素[b’5’]
brpop(keys, timeout=0)返回并删除key为name的list中的尾元素,如果list为空,则会一直阻塞等待keys: key列表 timeout: 超时等待时间,0为一直等待redis.brpop(‘list’)返回并删除名为list的list的最后一个元素[b’2’]
rpoplpush(src, dst)返回并删除名称为src的list的尾元素,并将该元素添加到名称为dst的list的头部src: 源list的key dst: 目标list的keyredis.rpoplpush(‘list’, ‘list2’)将key为list的list尾元素删除并返回并将其添加到key为list2的list头部b’2’

Set 、Sorted Set操作

详见 Redis教程

Hash 操作

Redis 中可以用name指定一个哈希表的名称,然后表内存储了各个键值对。

方法作用参数说明示例示例说明示例结果
hset(name, key, value)向key为name的hash中添加映射name: key名 key: 映射键名 value: 映射键值hset(‘price’, ‘cake’, 5)向key为price的hash中添加映射关系,cake的值为51,即添加的映射个数
hsetnx(name, key, value)向key为name的hash中添加映射,如果映射键名不存在name: key名 key: 映射键名 value: 映射键值hsetnx(‘price’, ‘book’, 6)向key为price的hash中添加映射关系,book的值为61,即添加的映射个数
hget(name, key)返回key为name的hash中field对应的valuename: key名 key: 映射键名redis.hget(‘price’, ‘cake’)获取key为price的hash中键名为cake的value5
hmget(name, keys, *args)返回key为name的hash中各个键对应的valuename: key名 keys: 映射键名列表redis.hmget(‘price’, [‘apple’, ‘orange’])获取key为price的hash中apple和orange的值[b’3’, b’7’]
hmset(name, mapping)向key为name的hash中批量添加映射name: key名 mapping: 映射字典redis.hmset(‘price’, {‘banana’: 2, ‘pear’: 6})向key为price的hash中批量添加映射True
hincrby(name, key, amount=1)将key为name的hash中映射的value增加amountname: key名 key: 映射键名 amount: 增长量redis.hincrby(‘price’, ‘apple’, 3)key为price的hash中apple的值增加36,修改后的值
hexists(name, key)key为namehash中是否存在键名为key的映射name: key名 key: 映射键名redis.hexists(‘price’, ‘banana’)key为price的hash中banana的值是否存在True
hdel(name, *keys)key为namehash中删除键名为key的映射name: key名 key: 映射键名redis.hdel(‘price’, ‘banana’)从key为price的hash中删除键名为banana的映射True
hlen(name)从key为name的hash中获取映射个数name: key名redis.hlen(‘price’)从key为price的hash中获取映射个数6
hkeys(name)从key为name的hash中获取所有映射键名name: key名redis.hkeys(‘price’)从key为price的hash中获取所有映射键名[b’cake’, b’book’, b’banana’, b’pear’]
hvals(name)从key为name的hash中获取所有映射键值name: key名redis.hvals(‘price’)从key为price的hash中获取所有映射键值[b’5’, b’6’, b’2’, b’6’]
hgetall(name)从key为name的hash中获取所有映射键值对name: key名redis.hgetall(‘price’)从key为price的hash中获取所有映射键值对{b’cake’: b’5’, b’book’: b’6’, b’orange’: b’7’, b’pear’: b’6’}

RedisDump

RedisDump 提供了 Redis 数据的导入和导出功能。

RedisDump 提供两个可执行命令:

  • redis-dump 用于 导出 数据;详见 redis-dump -h
    • e.g. $ redis-dump -u :foobared@localhost:6379 > ./redis_data.jl
  • redis-load 用于 导入 数据;详见 redis-load -h
    • e.g. $ redis-load -u :foobared@localhost:6379 < redis_data.json
    • 等价于$ cat redis_data.json | redis-load -u :foobared@localhost:6379
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值