第01章 网络数据采集入门

 

 

序言

1. 内容介绍

本章详细介绍了网络数据采集基本流程,selenium 基本操作方法等内容。

2. 理论目标

  • 了解网络数据采集基本流程
  • 了解 selenium 基本操作方法

3. 实践目标

  • 能掌握 urllib 的基本使用方法,完成网络数据采集
  • 能掌握 selenium 的基本使用方法,完成网页登陆并采集数据

4. 实践案例

5. 内容目录

  • 1.网络数据采集概述
  • 2.网络数据采集实施
  • 3.selenium 入门
  • 4.登陆模拟

第1节 网络数据采集概述

1. 网络数据采集定义

网络数据采集器(俗称爬虫、网页蜘蛛)就是一个探测机器,它的基本操作就是模拟人的行为去各个网站溜达,点点按钮,查查数据,或者把看到的信息背回来。就像一只虫子在一幢楼里不知疲倦地爬来爬去。
采集到的数据将会以文本文件、数据库等形式存储起来。

6cf87f07f8f120ac0748dc582386a33e.png

2. 网络数据采集应用场景

  1. 搜索数据更新
  2. 行业数据采集
  3. 网络舆情监测
  4. 电商价格监测

3. 网络数据采集合规性

爬虫目前还处于早期的蛮荒阶段,法律还没明确定义:“允许哪些行为”、“不允许哪些行为”。

bb497eb049d18f81050da0b8c9daf541.png

《中华人民共和国刑法》第286条:违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。

4. 网络数据采集社会案例

近日,新华社报道称,从北京市公安局网安总队获悉,按照公安部“净网2019”专项行动部署,北京警方近期破获备受关注的巧达科技非法获取计算机信息系统数据案,这家企业非法爬取用户数据,数量之大、牟利之巨,令人咋舌,公司法人王某某等36人被检察机关依法批准逮捕。

张某等非法获取计算机信息系统数据案(上海市杨浦区人民法院(2015)杨刑初字第232号)本案中,同享公司人员因通过技术手段非法获取掌门公司服务器存储的大量WIFI热点密码数据,被判非法获取计算机信息系统数据罪。

鲁某、吴某非法侵入计算机信息系统案(眉山市东坡区人民法院(2018)川1402刑初56号)鲁某和吴某因获取国家事务网站数据信息,最终被判非法侵入计算机信息系统罪。

无论如何,当你抓取某个网站的数据时,请记住自己是该网站的访客,应当约束自己的抓取行为,否则他们可能会封禁你的IP,甚至采取更进一步的法律行动。


第2节 网络数据采集实施

1. 目标网页分析

本章目标是京东笔记本商品列表页:https://list.jd.com/list.html?cat=670,671,672

 

2. 目标网页源代码分析

爬虫需要获取的核心内容信息是:网页源代码
网页源代码,是指未编译的文本代码或一个网站的全部源码文件,是一系列人类可读的计算机语言指令;通过浏览器或服务器翻译后才是用户最终看到的效果。
键盘按 F12 键可以查看开网页源代码

 

3. 目标网页元素与源代码对照

选择源代码左上角“箭头”图标,在页面元素上移动 或 在源代码上移动时,会呈现对应关系

 

盒子模型:网页所有HTML元素可以看作盒子

5f9f6afd3d02d68e541d6386a03dbb7a.png

 

4. 目标网页采集实施

目标网页采集大致分为以下六个步骤:

  1. 准备网页信息及浏览器信息
  2. 获取网页源代码
  3. 读取网页源代码并重新编码
  4. 确定关键信息
  5. 提取关键信息(正则表达式)
  6. 存储关键信息(保存至文本文档)

step1.准备网页信息及浏览器信息

 

## 确定网页网址url url = r"https://list.jd.com/list.html?cat=670,671,672" ## 模拟浏览器头部信息 user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36' header = {'User-Agent': user_agent}

step2.获取网页源代码

 

import urllib.request as ur ## 为避免冲突,直接导入 urllib.request ## 请求获取网页源代码 request_info = ur.Request(url, headers=header) ##类似打开浏览器,写上网址 response_info = ur.urlopen(request_info) ##类似按下回车键,网页展示相关信息

step3.读取网页源代码并重新编码

 

# 读取网页源代码并重新编码 response_read = response_info.read().decode('utf-8') ##因为只关心数据,一般把换行符、回车符、制表符替换掉 response_read = response_read.replace("\n","").replace("\r","").replace("\t","") print(response_read)

f50271f0adc0a4c2f9268e3b5a04fc28.png

step4.确定关键信息

uploading.4e448015.gif正在上传…重新上传取消

step5.提取关键信息(正则表达式)

 

## 正则表达式提取信息,需导入re 库 import re ## 选择所有商品列表 GoodlistPattern = re.compile(r'.*<ul class="gl-warp clearfix" data-tpl="1">(.*?)</ul>.*') GoodlistResult = re.search(GoodlistPattern, response_read) GoodlistGroups = GoodlistResult.groups() GoodlistStr = GoodlistGroups[0] # print(GoodlistStr) ## 选择每个商品 GoodlistsPattern = re.compile(r'(<li data-sku=.*?</li>)') GoodlistsResult = re.findall(GoodlistsPattern, GoodlistStr) print(GoodlistsResult)

f5e9e9d10460a56612600dbfc236de80.png

step6.存储关键信息(保存至文本文档)

 

with open("computer_goodlist.txt","w",encoding="utf-8") as f: for i in GoodlistsResult: f.write(str(i)) f.write("\n") f.flush()


第3节 selenium 入门

1. selenium简介

selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera,Edge等

案例演示

 

from selenium import webdriver url = r"https://www.baidu.com/" driver = webdriver.Chrome() driver.implicitly_wait(20) driver.get(url)

2. selenium环境准备

下载浏览器驱动:http://chromedriver.storage.googleapis.com/index.html?path=91.0.4472.19/
下载完之后,驱动文件需和爬虫脚本放在同一个目录下

安装selenium语法:

 

pip install selenium

485e65f64737666c56abe2500f95d71f.png

3. selenium实施步骤

  1. 导入相关库
  2. 确定网页网址url
  3. 启动浏览器
  4. 模拟人工操作动态加载数据
  5. 读取网页源代码并重新编码
  6. 提取目标信息(商品数据)
  7. 目标信息写入文本文件(商品数据)
  8. 关闭并退出浏览器

step1.导入相关库

 

import re #re库,正则表达式获取信息 import time #time库,任务暂时睡眠,给页面下载保留缓冲时间 from selenium import webdriver #selenium库,启动自动测试模拟

step2.确定网页网址url

 

url = r"https://list.jd.com/list.html?cat=670,671,672"

step3.启动浏览器

 

driver = webdriver.Chrome() driver.implicitly_wait(20) # 这里设置智能等待20s,即浏览器等待加载时间 driver.get(url)

step4.模拟人工操作动态加载数据

 

Target = driver.find_element_by_css_selector("span[class='p-num']") driver.execute_script("arguments[0].scrollIntoView();", Target) # 拖动到可见的元素:页码栏 time.sleep(10) #等待10S,浏览器数据加载

step5.读取网页源代码并重新编码

 

response_info = driver.page_source response_read = response_info.replace("\n","").replace("\r","").replace("\t","") ##因为只关心数据,一般把换行符、回车符、制表符替换掉

step6.提取目标信息(商品数据)

 

## 选择所有商品列表 GoodlistPattern = re.compile(r'.*<ul class="gl-warp clearfix" data-tpl="1">(.*?)</ul>.*') GoodlistResult = re.search(GoodlistPattern, response_read) GoodlistGroups = GoodlistResult.groups() GoodlistStr = GoodlistGroups[0] ## 选择每个商品 GoodlistsPattern = re.compile(r'(<li data-sku=.*?</li>)') GoodlistsResult = re.findall(GoodlistsPattern, GoodlistStr)

step7.目标信息写入文本文件(商品数据)

 

with open("computer_goodlist_full.txt","w",encoding="utf-8") as f: for i in GoodlistsResult: f.write(str(i)) f.write("\n") f.flush()

step8.关闭并退出浏览器

 

driver.close() driver.quit()

4. 浏览器操作介绍

 

from selenium import webdriver url = r"https://www.baidu.com/" driver = webdriver.Chrome() driver.implicitly_wait(20) driver.get(url)

定位元素方法

 

#<a href="http://news.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">新闻</a> driverNews = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[3]/a[1]') print(driverNews)

[<selenium.webdriver.remote.webelement.WebElement (session="aa5156da378e40ff4500d85fa67df88f", element="0.7628609986660331-1")>]

查找符合要求的第一个元素:

查找元素方法作用说明
.find_element_by_id()通过ID进行匹配查找,只返回匹配到的一个元素
.find_element_by_name()通过name进行匹配查找,只返回匹配到的一个元素
.find_element_by_xpath()通过xpath进行匹配查找,只返回匹配到的一个元素
.find_element_by_link_text()通过链接内容进行匹配查找,只返回匹配到的一个元素
.find_element_by_partial_link_text()通过部分链接内容进行匹配查找,只返回匹配到的一个元素
.find_element_by_tag_name()通过标签名称进行匹配查找,只返回匹配到的一个元素
.find_element_by_class_name()通过class名称进行匹配查找,只返回匹配到的一个元素
.find_element_by_css_selector()通过CSS选择器进行匹配查找,只返回匹配到的一个元素

查找符合要求的多个元素(返回一个列表):

查找元素方法作用说明
.find_elements_by_id()通过ID进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_name()通过name进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_xpath()通过xpath进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_link_text()通过链接内容进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_partial_link_text()通过部分链接内容进行匹配查找,只返回匹配到的多个元素(列表形式)
find_elements_by_tag_name()通过标签名称进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_class_name()通过class名称进行匹配查找,只返回匹配到的多个元素(列表形式)
.find_elements_by_css_selector()通过CSS选择器进行匹配查找,只返回匹配到的多个元素(列表形式)

常用方法是通过xpath相对路径进行定位,同时CSS也是比较好的方法。

操作元素方法

所有的操作与页面交互都将通过WebElement接口,常见的操作元素方法如下:

 

from selenium import webdriver from selenium.webdriver.common.keys import Keys #<a href="http://news.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">新闻</a> driverNews = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[3]/a[1]') driverNews[0].click()

操作元素方法作用说明
.clear()清除元素的内容
.send_keys()模拟按键输入
.click()点击元素
.submit()提交表单

获取元素值方法

通过WebElement接口可以获取常用的值,这些值同样非常重要。

 

from selenium import webdriver from selenium.webdriver.common.keys import Keys #<a href="http://news.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">新闻</a> driverNews = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[3]/a[1]') print(driverNews[0].text)

获取元素值方法作用说明
.size获取元素的尺寸
.text获取元素的文本
.get_attribute()获取属性值
.location获取元素坐标,先找到要获取的元素,再调用该方法
.page_source返回页面源码
.driver.title返回页面标题
.current_url获取当前页面的URL
.is_displayed()设置该元素是否可见
.is_enabled()判断元素是否被使用
.is_selected()判断元素是否被选中
.tag_name返回元素的tagName

鼠标操作方法

鼠标的操作不仅仅是click()单击操作,还有很多包含在ActionChains类中的操作。

 

from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains #<a href="http://news.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">新闻</a> driverNews = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[3]/a[1]') ActionClick = ActionChains(driver).context_click(driverNews[0]) ActionClick.perform()

鼠标操作方法作用说明
.context_click(elem)右击鼠标点击元素elem,另存为等行为
.double_click(elem)双击鼠标点击元素elem,地图web可实现放大功能
.drag_and_drop(source,target)拖动鼠标,源元素按下左键移动至目标元素释放
.move_to_element(elem)鼠标移动到一个元素上
.click_and_hold(elem)按下鼠标左键在一个元素上
.perform()在通过调用该函数执行ActionChains中存储行为

键盘操作方法

webdriver的Keys类中提供了键盘所有的按键操作

 

from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.action_chains import ActionChains # <input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off"> driverInput = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input') driverEnter = driver.find_elements_by_xpath(r'/html/body/div[1]/div[1]/div[5]/div/div/form/span[2]/input') driverInput[0].send_keys("新闻") driverEnter[0].send_keys(Keys.ENTER)

键盘输入方法作用说明
.send_keys(Keys.ENTER)按下回车键
.send_keys(Keys.TAB)按下Tab制表键
.send_keys(Keys.SPACE)按下空格键space
.send_keys(Kyes.ESCAPE)按下回退键Esc
.send_keys(Keys.BACK_SPACE)按下删除键BackSpace
.send_keys(Keys.SHIFT)按下shift键
.send_keys(Keys.CONTROL)按下Ctrl键
.send_keys(Keys.ARROW_DOWN)按下鼠标光标向下按键
.send_keys(Keys.CONTROL,‘a’)组合键全选Ctrl+A
.send_keys(Keys.CONTROL,‘c’)组合键复制Ctrl+C
.send_keys(Keys.CONTROL,‘x’)组合键剪切Ctrl+X
.send_keys(Keys.CONTROL,‘v’)组合键粘贴Ctrl+V

第4节 登陆模拟

登陆模拟,指的是通过python 代码模拟用户登陆系统,然后访问那些只能登陆状态才能访问的页面。常用模拟登陆的方法有:

  1. 直接使用已知的cookie访问;
  2. 模拟登录后再携带得到的cookie访问;
  3. 模拟登录后用session保持登录状态;
  4. 使用无头浏览器访问

本文将基于“方法4 使用无头浏览器访问”展开讨论。

1. 环境工具准备

主要包括两个工具 Tesseract-OCR 和 pytesseract,用于验证码识别

OCR介绍

OCR(Optical Character Recognition,光学字符识别),也就是使用字符识别方法将形状翻译成计算机文字的过程,Tesseract-OCR是Google支持的开源ocr项目。

下载地址:https://digi.bib.uni-mannheim.de/tesseract/

安装步骤:

34a77da1cdbe6a392b317396f43ea870.png

812181f96579b969351523a6910ad69b.png

选择“使用用户”

a991af803307039d339018c99f687888.png

语言包选择,可以独自下载,但注意版本必须一致,否则容易报错,具体链接:https://github.com/tesseract-ocr/tessdata

dd8bf7123242010f9e6a69b6d010a1cb.png

8f7d210eb17a8723b797a6b7dc9869dc.png

3d51530e997254b0fabba6eb7dc8c4c6.png

6e9a32560863b6d7d382b64ec06b9e1f.png

3635c1e970c74cf7ef94fd8dc56bd3e4.png

a139e35853197795ecf3ead04d6dc3df.png

配置环境路径

6f4ce3810ec4bccf9f792d6165dad15c.png

5133d55edc45a6084ce1dca09ca29317.png

检查版本,确认是否正确安装

8b0d1b2ed6285bfc6e368f8d599b552a.png

pytesseract

为了使用Python将图像识别为字母和数字,我们需要用到Tesseract库,它是Google支持的开源ocr项目。
安装语句:pip install pytesseract

8f55ee8edcc6a7c82370ada51a5bca7e.png

2. 模拟登陆实施

本次实验网站:https://so.gushiwen.cn/user/login.aspx

模拟登录实施步骤

  1. 启动浏览器,进入登陆页面
  2. F12 开启“开发者工具”,勾选“Preserve log”
  3. 登录信息框内输入账号密码、验证码,登录网站
  4. 从“开发者工具”中,逐个查看“Name”栏目,找到headers - General - Requset Method为POST的
  5. headers 拉到最下面,找到“Form Data”,按里面的内容组建登录信息
  6. 再按后面“代码实现逻辑”执行

 

 

代码实现逻辑

  1. 启动浏览器,打开网页
  2. 网页输入验证码、账号密码,登录网页(此处为死循环,直至验证码正确才退出)
  • 2.1).截取网页验证码图片
  • 2.2).处理图片(灰度化、二值化)、识别验证码
  • 2.3).网页内填写信息:账号、密码、验证码
  • 2.4).登录验证正确时直接第3步,否则第2.1)步
  1. 进入目标页面“我的背诵”,读取网页源代码
  2. 利用正则表达式解析源代码,提取关键信息
  3. 写入文档

主体代码

 

## 导入库 import re import time from selenium import webdriver from selenium.webdriver.common.keys import Keys from PIL import Image import pytesseract ## 确定网页网址url、登陆账号和密码 url = r"https://so.gushiwen.cn/user/login.aspx" username = "kingdragon_lun@126.com" password = "ABC123!!" ## 1.启动浏览器,打开网页 driver = webdriver.Chrome() driver.implicitly_wait(20) # 这里设置智能等待20s,即浏览器等待加载时间 driver.get(url) ## 2.网页输入验证码、账号密码,登录网页 driver = login(driver, username, password) ## 3.进入目标页面“我的背诵”,读取网页源代码 response_read = getresource(driver) ## 4.利用正则表达式解析源代码,提取关键信息 TotalList = getinfo(response_read) ## 5.写入文档 writefile(TotalList) driver.close() ## 退出浏览器 driver.quit()

各函数详细代码

 

## 2.网页输入验证码、账号密码,登录网页(此处为死循环,直至登录验证正确才退出) def login(driver, username, password): n = 1 while True: filename = "Code.png" ## 2.1.截取网页验证码图片 driver = getcode(driver, filename) ## 2.2.处理图片(灰度化、二值化)、识别验证码 Code = picocr(filename) ## 2.3.网页内填写信息:账号、密码、验证码 driver = trylogin(driver, username, password, Code) ## 2.4.登录验证正确时直接第3步,否则第2.1)步 if isalert(driver): driver.switch_to.alert.accept() driver.refresh() # 刷新方法 refresh print("正在重试第{}遍".format(str(n))) n = n + 1 else: break return driver

 

# 2.1 截取网页验证码图片 def getcode(driver, filename): # 获取验证码图片在网页中的位置 element = driver.find_element_by_id('imgCode') # 定位验证码图片 p = 1.25 # 网页缩放比例,观察而定 left = int(element.location['x'] * p) # 获取图片左上角坐标x top = int(element.location['y'] * p) # 获取图片左上角y right = int(element.location['x'] * p + element.size['width']) # 获取图片右下角x bottom = int(element.location['y'] * p + element.size['height']) # 获取图片右下角y # 通截屏后通过Image裁剪出验证码图片保存 driver.save_screenshot(filename) # 截取当前窗口并保存图片 img = Image.open(filename) # 打开图片 img = img.crop((left, top, right, bottom)) # 截图验证码 img.save(filename) # 保存验证码图片 return driver

 

## 2.2 处理图片(灰度化、二值化)、识别验证码 def picocr(filename): ## 灰度化 ## 把彩色图像转化为灰度图像。通过灰度处理可以把色彩空间由RGB转化为HIS img = Image.open(filename) imggray = img.convert('L') imggray.save('gray.png') ## 二值化 ## 把大于某个临界灰度值的像素灰度设为灰度极大值,把小于这个值的像素灰度设为灰度极小值 threshold = 150 table = [] for i in range(256): if i <threshold: table.append(0) else: table.append(1) imggraybn = imggray.point(table,'1') imggraybn.save('bn.png') ## 打开验证码图片,输出识别信息 imggraybn = Image.open('bn.png') code = pytesseract.image_to_string(imggraybn) return code

 

# 2.3 网页内填写信息:账号、密码、验证码 def trylogin(driver, username, password, Code): element_username = driver.find_element_by_css_selector("input[id='email']") # 找到用户名的框框 element_username.send_keys(username) # 填写用户名 element_password = driver.find_element_by_css_selector("input[id='pwd']") # 找到输入密码的框框 element_password.send_keys(password) # 填写密码 element_code = driver.find_element_by_css_selector("input[id='code']") # 找到输入验证码的框框 element_code.send_keys(str(Code)[0:4]) # 输入验证码,以防超过4个 element_submit = driver.find_element_by_css_selector("input[id='denglu']") # 找到登录按钮 element_submit.click() # 点击登录按钮 return driver # 返回浏览器

 

## 2.4 输入验证码后,判断是否报错(警告) def isalert(driver): try: alert = driver.switch_to.alert alert.text return alert except: return False

 

## 3.进入目标页面“我的背诵”,读取网页源代码 def getresource(driver): # 选择我的背诵 element_recite = driver.find_element_by_link_text("我的背诵") element_recite.click() # 读取网页源代码并重新编码 response_info = driver.page_source response_read = response_info ##因为只关心数据,一般把换行符、回车符、制表符替换掉 response_read = response_read.replace("\n", "").replace("\r", "").replace("\t", "") return response_read

 

## 4.利用正则表达式解析源代码,提取关键信息(粗略) def getinfo(response_read): ## 选取背诵部分 CecitePattern = re.compile(r'<div id="mainSearch" class="mainSearch">(.*?)<div class="right">') CeciteResult = re.findall(CecitePattern, response_read) return CeciteResult

 

## 4.利用正则表达式解析源代码,提取关键信息(精细) def getinfo(response_read): ## 选取背诵部分 CecitePattern = re.compile(r'<div id="mainSearch" class="mainSearch">(.*?)<div class="right">') CeciteResult = re.findall(CecitePattern, response_read) ## 选取背诵部分-每篇文章段落 ListPattern = re.compile(r'<div class="sons"(.*?)</span></div> </div>') ListResult = re.findall(ListPattern, CeciteResult[0]) TotalList = [] # 空列表,存储文章的信息 for ListResulti in ListResult: ## 选取背诵部分-每篇文章信息 DetailPattern = re.compile( r'<b>(.*?)</b></a></p> <p class="source"><a href=.*?>(.*?)</a><a href=.*?>(.*?)</a></p> <div class="contson" id=.*?>(.*?)<p></p>背诵成绩:(.*?)分<p></p><p></p> </div>.*<span>背诵于:(.*)') DetailResult = re.findall(DetailPattern, ListResulti) DetailList = [] # 空列表,存储一篇文章的信息 for DetailResulti in DetailResult[0]: DetailStr = str(DetailResulti) DetailStr = DetailStr.replace("<p>", "").replace("</p>", "").replace("<br />", "").replace(" ", "").replace("\xa0", "") DetailList.append(DetailStr) TotalList.append(DetailList) return TotalList

 

## 5.写入文档 def writefile(TotalList): with open("gushiwen_info_raw.txt", "w+", encoding="utf-8") as f: for TotalListi in TotalList: TotalListStr = "".join(TotalListi) ## TotalListStr = "^".join(TotalListi) f.write(TotalListStr + "\n") f.flush()


 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

撸码的xiao摩羯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值