Python黑马程序员(Spark实战)笔记

这篇具有很好参考价值的文章主要介绍了Python黑马程序员(Spark实战)笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、基础准备

 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

# 导包
from pyspark import SparkConf,SparkContext
#创建SparkConf类对象
conf=SparkConf().setMaster("local[*]").setAppName("test_spark_app")
#基于SparkXConf类对象创建SparkContext对象
sc=SparkContext(conf=conf)
#打印PySpark的运行版本
print(sc.version)
#停止SparkContext对象的运行(停止pySpark程序)
sc.stop()

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 2、数据输入

from pyspark import SparkConf,SparkContext

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# 通过parallelize方法将Python对象加载到Spark内,成为RDD对象
rdd1=sc.parallelize([1,2,3,4,5])
rdd2=sc.parallelize((1,2,3,4,5))
rdd3=sc.parallelize("abcdefg")
rdd4=sc.parallelize({1,2,3,4,5})
rdd5=sc.parallelize({"key1":"value1","key2":"value2"})

#如果要查看RDD里面有什么内容,需要用collect()方法
print(rdd1.collect())
print(rdd2.collect())
print(rdd3.collect())
print(rdd4.collect())
print(rdd5.collect())


sc.stop()

注意:字符串返回的是['a','b','c','d','e','f','g']   字典返回的是['key1','key2']  

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

读取hello.txt的内容:

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

from pyspark import SparkConf,SparkContext

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# # 通过parallelize方法将Python对象加载到Spark内,成为RDD对象
# rdd1=sc.parallelize([1,2,3,4,5])
# rdd2=sc.parallelize((1,2,3,4,5))
# rdd3=sc.parallelize("abcdefg")
# rdd4=sc.parallelize({1,2,3,4,5})
# rdd5=sc.parallelize({"key1":"value1","key2":"value2"})
#
# #如果要查看RDD里面有什么内容,需要用collect()方法
# print(rdd1.collect())
# print(rdd2.collect())
# print(rdd3.collect())
# print(rdd4.collect())
# print(rdd5.collect())


#用textFile方法,读取文件数据加载到Spark内,成为RDD对象
rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/hello.txt")
print(rdd.collect())
sc.stop()

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

3、数据计算-map方法

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# 准备一个RDD
rdd=sc.parallelize([1,2,3,4,5])

#通过map方法将全部数据都乘以10
def func(data):
    return data*10

rdd2=rdd.map(func) #(T) -> U
                   #(T) -> T
print(rdd2.collect())
#链式调用

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

注意:

import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

如果没有添加上行代码程序会报出错误!

Caused by: org.apache.spark.SparkException: Python worker failed to connect back.

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 解释器的位置: (是在电脑中安装的位置)

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

代码中:

 def func(data):
     return data*10

可以替换成lambda

rdd2=rdd.map(lambda x:x*10)

完整代码:

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# 准备一个RDD
rdd=sc.parallelize([1,2,3,4,5])

#通过map方法将全部数据都乘以10
# def func(data):
#     return data*10

rdd2=rdd.map(lambda x:x*10) #(T) -> U
                            #(T) -> T
print(rdd2.collect())
#链式调用

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 链式调用 可以直接使用.的方式

rdd2=rdd.map(lambda x:x*10).map(lambda x:x+5)

完整代码:

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# 准备一个RDD
rdd=sc.parallelize([1,2,3,4,5])

#通过map方法将全部数据都乘以10
# def func(data):
#     return data*10

rdd2=rdd.map(lambda x:x*10).map(lambda x:x+5) #(T) -> U
                            #(T) -> T
print(rdd2.collect())
#链式调用

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

5、flatMap方法

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# 准备一个RDD
rdd=sc.parallelize(["itheima itcast 666","itheima itheima it cast","python itheima"])
#需求,将RDD数据里面的一个个单词提取出来
rdd2=rdd.map(lambda x:x.split(" "))
rdd1=rdd.flatMap(lambda x:x.split(" "))
print(rdd1.collect())
print(rdd2.collect())

flatMap算子

计算逻辑和map一样

可以比map多出,接触一层嵌套的功能

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

6、 reduceByKey算子

reduceBeKey中的聚合逻辑是:

[1,2,3,4,5] 然后聚合函数:lambda a,b:a+b

a  b

1+2=3

    3+3=6

      6+4=10

          10+5=15

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置

conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

#准备一个RDD
rdd =sc.parallelize([('男',99),('男',88),('女',99),('女',66)])
# 求男生和女生两组的成绩之和
rdd2=rdd.reduceByKey(lambda a,b:a+b)
print(rdd2.collect())

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

出现: UserWarning: Please install psutil to have better support with spilling  

在cmd中 pip install psutil 即可

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 文章来源地址https://www.toymoban.com/news/detail-745160.html

 7、数据计算练习案例

要求:

# 完成单词计数统计
# 1.构建执行环境入口对象
# 2.读取数据文件
# 3.取出全部单词
# 4.将所有单词都转换为二元元组,单词为Key value 设置为1
# 5.分组并求和
# 6.打印输出
# 完成单词计数统计
# 1.构建执行环境入口对象
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)
#2.读取数据文件
rdd=sc.textFile("C:/Users/GYH\Desktop/data/pyspark_heima/hello.txt")
#3.取出全部单词
word_rdd=rdd.flatMap(lambda x:x.split(" "))
# print(word_rdd.collect())

#4.将所有单词都转换为二元元组,单词为Key value 设置为1
word_with_one_rdd=word_rdd.map(lambda word:(word,1))
# print(word_with_one_rdd.collect())
#5.分组并求和
result=word_with_one_rdd.reduceByKey(lambda a,b:a+b)
#6打印输出
print(result.collect())

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

8、filter方法 

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

#准备一个RDD
rdd=sc.parallelize([1,2,3,4,5])
# 对RDD的数据进行过滤
rdd2=rdd.filter(lambda num:num%2==0)

print(rdd2.collect())

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

9、distinct方法

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

#准备一个RDD
rdd=sc.parallelize([1,1,3,3,5,5,7,8,8,9,10])
#对RDD的数据进行去重
rdd2=rdd.distinct()
print(rdd2.collect())

 Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

10、sortBy方法

rdd.sortBy(func,ascending=Flase,numPartition=1)

#func(T)-->U:告知按照rdd中的哪一个数据进行排序,比如lambda x:x[1]表示按照rdd中的第二列元素进行排序

#ascending True升序 Flase降序

#numPartitions:用多少分区排序

from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

#1.读取文件
rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/hello.txt")
#2.取出全部单词
word_rdd=rdd.flatMap(lambda x:x.split(" "))
#3.将所有单词都转换为二元元组,单词为Key,value设置为1
word_with_one_rdd=word_rdd.map(lambda word:(word,1))
#4.分组并求和
ressult_rdd=word_with_one_rdd.reduceByKey(lambda a,b:a+b)
print(ressult_rdd.collect())
#5.对结果进行排序
final_rdd=ressult_rdd.sortBy(lambda x:x[1],ascending=False,numPartitions=1)
print(final_rdd.collect())

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

11、数据计算-练习案例2

from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

# TODO 需求1 城市销售额排名
# 1.1 读取文件到RDD
file_rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/orders.txt")
# 1.2 取出一个JSON字符串
json_str_rdd=file_rdd.flatMap(lambda x:x.split("|"))
# print(json_str_rdd)
# 1.3 将一个个JSON字符串转换为字典
dict_rdd=json_str_rdd.map(lambda x:json.loads(x))
print(dict_rdd.collect())
# 1.4 取出城市和销售数据
# (城市,销售额)
city_with_money_rdd=dict_rdd.map(lambda x:(x['areaName'],int(x['money'])))
# 1.5 按城市分组按销售聚合
city_result_edd=city_with_money_rdd.reduceByKey(lambda a,b:a+b)
# 1.6 按销售额聚合结果进行排序
result1_rdd=city_result_edd.sortBy(lambda x:x[1],ascending=False,numPartitions=1)
print(f"需求1的结果是{result1_rdd.collect()}")

# TODO 需求2:取出城市有哪些商品类别在销售
# 2.1 取出全部的商品类别
category_rdd=dict_rdd.map(lambda x:x['category']).distinct()
print(f"需求2的结果{category_rdd.collect()}")
#2.2 对全部商品类别进行去重
# TODO 需求3
# 3.1过滤北京市的数据
beijing_data_rdd=dict_rdd.filter(lambda x:x['areaName']=='北京')
# 3.2 取出全部商品类别
result3_rdd=beijing_data_rdd.map(lambda x:x['category']).distinct()
print(f"需求3的结果:{result3_rdd.collect()}")
# 3.3 进行商品类别去重

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

#12、输出为Python对象

数据输出的方法

collect 将RDD内容转换为list

reduce 对RDD内容进行自定义聚合

take 取出RDD的前N个元素组成list

count 统计RDD元素个数

from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
sc=SparkContext(conf=conf)

#准备RDD
rdd=sc.parallelize([1,2,3,4,5])
# collect算子,输出RDD为list的对象
rdd_list:list=rdd.collect()
print(rdd_list)
print(type(rdd_list))
# reduce 算子,对RDD进行两两聚合
num=rdd.reduce(lambda a,b:a+b)
print(num)
# take算子,取出RDD前N个元素,组成list返回
take_list=rdd.take(3)
print(take_list)
# count,统计rdd内有多少条数据,返回值为数字
number_count=rdd.count()
print(f"rdd内有{number_count}个元素")
sc.stop()

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 #13、数据输出到文件中

1、下载hadoop3.3.0压缩包

      百度网盘:链接:https://pan.baidu.com/s/1y4a2w4D8zCzYKEDY9aPWtw 
                  提取码:1234

      hadoop3.3.0解压到任意位置即可

2、将haoop3.3.0的bin文件夹下的 hadoop.dll  复制到C:\Windows\System32中Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

在pycharm中添加如下代码

os.environ['HADOOP_HOME']="E:/spark/hadoop-3.3.0"

 运行后成功写入:

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 14、综合案例

读取文件转换成RDD,并完成:

打印输出:热门搜索时间段(小时精度)Top3

打印输出:统计黑马程序员关键字在哪个时段被搜索最多

将数据转换为JSON格式,写出文件

1、热门搜索时间段(小时精度)Top3

from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON']="C:/Users/GYH/AppData/Local/Programs/Python/Python310/python.exe" #python解释器的位置
os.environ['HADOOP_HOME']="E:/spark/hadoop-3.3.0"
conf=SparkConf().setMaster("local[*]").setAppName("test_spark")
conf.set("spark.default.parallelism","1")
sc=SparkContext(conf=conf)

# 读取文件转换成RDD
# TODO 需求1:热门城市时间段TOP3(小时精度)
file_rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/SogouQ.txt")
#1.1取出全部时间并转换为小时
#1.2转换为(小时,1)的二元元组
#1.3Key分组聚合Value
#1.4排序(降序)
#1.5取前3
result1=file_rdd.map(lambda x:x.split("\t")).\
    map(lambda x:x[0][:2]).\
    map(lambda x:(x,1)).\
    reduceByKey(lambda a,b:a+b).\
    sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
    take(3)
print(f"需求1的结果:{result1}")

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 # TODO 需求2:热门搜索词TOP3

# TODO 需求2:热门搜索词TOP3
# 2.1 取出全部搜索词
# 2.2 (词,1) 二元元组
# 2.3 分组聚合
# 2.4 排序
# 2.5 TOP3
file_rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/SogouQ.txt")
result2=file_rdd.map(lambda x:x.split('\t')).\
    map(lambda x:x[2]).\
    map(lambda x:(x,1)).\
    reduceByKey(lambda a,b:a+b).\
    sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
    take(3)
print(f"需求2的结果:{result2}")

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 # TODO 需求3:统计黑马程序员关键字在什么时段被搜索的最多

# TODO 需求3:统计黑马程序员关键字在什么时段被搜索的最多
# 3.1 过滤内容 只能保留黑马程序员关键字
# 3.2 转换为(小时,1)的二元元组
# 3.3 Key分组聚合Value
# 3.4 排序(降序)
# 3.5 取前1
file_rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/SogouQ.txt")
result3=file_rdd.map(lambda x:x.split("\t")).\
    filter(lambda x:x[2]=='黑马程序员').\
    map(lambda x:(x[0][:2],1)).\
    reduceByKey(lambda a,b:a+b).\
    sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
    take(1)
print(f"需求3的结果{result3}")

 # TODO 需求4:将数据转换为JSON格式,写到文件中

# TODO 需求4:将数据转换为JSON格式,写到文件中
file_rdd=sc.textFile("C:/Users/GYH/Desktop/data/pyspark_heima/SogouQ.txt")
# 4.1 转换为JSON格式的RDD
# 4.2 写出为文件
file_rdd.map(lambda x:x.split("\t")).\
    map(lambda x:{"time":x[0],"user_id":x[1],"key_word":x[2],"rank1":x[3],"rank2":x[4],"url":x[5]}).\
    saveAsTextFile("C:/Users/GYH/Desktop/data/pyspark_heima/output1_JSON")

打开output1_JSON文件夹下的part_00000 

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 成功写入:

Python黑马程序员(Spark实战)笔记,pyspark,spark,大数据,分布式

 

到了这里,关于Python黑马程序员(Spark实战)笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 黑马程序员3天带你玩转Python深度学习TensorFlow框架学习笔记

    这是黑马程序员3天带你玩转Python深度学习TensorFlow框架学习笔记 视频链接: 黑马程序员3天带你玩转Python深度学习TensorFlow框架 学习目标:知道深度学习与机器学习的区别 区别:深度学习没有特征提取 特征方面 机器学习的特征工程步骤是要靠手动完成的,而且需要大量领域专

    2024年02月01日
    浏览(51)
  • 【黑马程序员】C++核心功能项目实战

    20240221 本教程主要利用C++来实现一个基于多态的职工管理系统 构成 普通员工 经理 老板 员工显示 需要显示职工编号、职工姓名、职工岗位以及职责 不同员工职责 责普通员工职责:完成经理交给的任务 经理职责:完成老板交给的任务,并下发任务给员工 老板职责:管理公司所有

    2024年02月22日
    浏览(39)
  • 黑马程序员 Docker笔记

    本篇学习笔记文档对应B站视频: 同学们,在前两天我们学习了Linux操作系统的常见命令以及如何在Linux上部署一个单体项目。大家想一想自己最大的感受是什么? 我相信,除了个别天赋异禀的同学以外,大多数同学都会有相同的感受,那就是麻烦。核心体现在三点: 命令太

    2024年01月23日
    浏览(60)
  • 黑马程序员---微服务笔记【实用篇】

        微服务实现流程:  所有要学的技术:  分层次教学:  具体分层:  单体架构 将业务所有功能集中在一个项目中开发,打成一个包部署 优点:架构简单、部署成本低 缺点:耦合度高 分布式架构 根据业务功能对系统进行查分,每个业务模块作为独立项目开发,称为一

    2024年02月07日
    浏览(50)
  • 学习笔记-微服务高级(黑马程序员)

    测试软件 jmeter 雪崩问题 个微服务往往依赖于多个其它微服务,服务提供者I发生了故障,依赖于当前服务的其它服务随着时间的推移形成级联失败 超时处理 设定超时时间,请求超过一定时间没有响应就返回错误信息 仓壁模式 限定每个业务能使用的线程数,避免耗尽整个tom

    2024年04月25日
    浏览(47)
  • 学习笔记-微服务基础(黑马程序员)

    spring cloud spring cloud alibaba eureka-server 注册中心 eureka-client 客户端 每30s发送心跳 服务 服务消费者 服务提供者 依赖 启动类 添加注解 @EnableEurekaServer 配置文件 application.yml 依赖 配置文件 application.yml 添加注解 @LoadBlanced 修改url 自定义负载均衡策略 1、定义新的IRule,将轮询策略(

    2024年04月13日
    浏览(48)
  • SpringBoot-黑马程序员-学习笔记(三)

    目录 30.springboot整合MyBatis-plus 32.SSM整合 38.MP中的条件查询 小知识:许多放在类前面的注解,比如@Mapper,@Service都是将该类定义成一个Bean,交给spring管理 39.Service模块 1.创建普通springboot项目,勾选Mysql 框架 2.在pom包里面导入mybatis-plus的坐标 3.把数据层的类继承BaseMapper这个接口

    2024年02月07日
    浏览(42)
  • 黑马程序员-职工管理系统实战-附加源码Git

    职工管理系统可以用来管理公司内所有员工的信息 本教程主要利用C++来实现一个基于多态的职工管理系统 公司中职工分为三类:普通员工、经理、老板,显示信息时,需要显示职工编号、职工姓名、职工岗位、以及职责 普通员工职责:完成经理交给的任务 经理职责:完成老

    2024年02月03日
    浏览(55)
  • Linux命令基础,黑马程序员学习笔记

    command [-options] [parameter] command:命令本身 -options:[可选,非必填]命令的一些选项,可以通过选项控制命令的行为细节 parameter:[可选,非必填]命令的参数,多数用于命令的指向目标等 示例: ls -l /home/itheima ls是命令本身,-l是选项, /home/itheima是参数意思是以列表的形式,显示/home

    2024年02月19日
    浏览(89)
  • [学习笔记]黑马程序员-Hadoop入门视频教程

    黑马程序员大数据Hadoop入门视频教程,适合零基础自学的大数据Hadoop教程 学习目标 1.理解大数据基本概念 2.掌握数据分析基本步骤 3.理解分布式、集群概念 4.学会VMware虚拟机的导入与使用 5.掌握Linux常用操作命令使用 6.掌握vi/vim编辑器基础使用 1.1.1 企业数据分析方向 数据分

    2024年02月13日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包