爬虫解析模块(bs4,selenium)

bs4文档

from bs4 import BeautifulSoup
  1. Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
  2. Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。

解析器

解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, “html.parser”)Python的内置标准库、执行速度适中 、文档容错能力强Python 2.7.3 or 3.2.2)前的版本中文容错能力差
lxml HTML 解析器BeautifulSoup(markup, “lxml”)速度快、文档容错能力强需要安装C语言库
lxml XML 解析器BeautifulSoup(markup, “xml”)速度快、唯一支持XML的解析器需要安装C语言库
html5libBeautifulSoup(markup, “html5lib”)最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档速度慢、不依赖外部扩展

初始化

def __init__(self, markup="", features=None, builder=None,
             parse_only=None, from_encoding=None, exclude_encodings=None,
             element_classes=None, **kwargs):
    """Constructor.
    :param markup: html文本或file-like对象
    :param features: 解析器:("lxml","lxml-xml", "html.parser", or "html5lib")
    """


soup = BeautifulSoup("html","lxml")

获取标签和属性

print(soup.title)	#选择title标签。
print(soup.head)	#选择head标签
print(soup.p)		#选择p标签

print(soup.title.name)			#获取title标签名
print(soup.p.attrs['name'])		#获取p标签name属性值
print(soup.p['name'])			#获取p标签name属性值

print(soup.p.string)		#获取p标签内容,该方法只能获取第一个p标签内容

print(soup.head.title.string)		#嵌套选择,用点号分割,层层嵌套

print(soup.p.contents)		# 获取p标签的子节点及其内容,->list
print(soup.p.children) 		# 获取获取标签的子节点->iter
print(soup.p.descendants)	# 返回子孙节点(不仅仅是子节点,返回结果为迭代器)
print(soup.a.parent)		# 返回的是第一个a标签的父节点
print(soup.a.parents)		# 返回的是父节点以及祖先节点
print(list(enumerate(soup.a.next_siblings)))		#a标签的下一个兄弟节点
print(list(enumerate(soup.a.previous_siblings)))	#上一个兄弟

find()和find_all()方法

  1. find_all()方法返回值:所有符合条件的元素列表。
  2. find()方法 返回值:返回符合条件的第一个元素。
def find_all(self, name=None, attrs={}, recursive=True, string=None,
             limit=None, **kwargs):
    """
    :param name: 标签名称,可以为字符串,列表,正则
    :param attrs: 标签属性
    :param recursive: 递归
    :param limit: 限制数量
    :kwargs: key=val格式的标签属性,如果是class属性,需要写为class_=val
    
    :return: A ResultSet of PageElements.
    :rtype: bs4.element.ResultSet
    """
def find(self, name=None, attrs={}, recursive=True, string=None,
         **kwargs):
    r = None
    l = self.find_all(name, attrs, recursive, string, 1, **kwargs)
    if l:
        r = l[0]
    return r

对于返回的Element对象:

  1. tag.text 表示其中的文本内容
  2. tag.get(attr) 获取attr属性
  3. tag.name 表示标签的名字

其他find_*

find_parents()  返回所有祖先节点
find_parent()  返回直接父节点

find_next_siblings()  返回后面所有兄弟节点
find_next_sibling()	返回后面第一个兄弟节点

find_all_next()  返回节点后所有符合条件的节点
find_next() 返回第一个符合条件的节点

find_all_previous() 返回节点后所有符合条件的节点
find_previous() 返回第一个符合条件的节点

select()方法

select()主要是以CSS选择器(selector)的方式寻找元素。如果找到元素,则返回元素列表;否则返回空列表。

  1. 标签选择器:直接指定标签名
    1. Soup.select(‘p’):寻找所有<p>标签的元素
    2. Soup.select(‘img’):寻找所有<img>标签的元素
    3. Soup.select(‘input[name]’):寻找所有<input>标签且包含name属性的元素
  2. 类(class)选择器:.
    1. Soup.select('.name):寻找所有class属性为name的元素
  3. 选择器:#
    1. Soup.select('#name):寻找所有id属性为name的元素
  4. 层级选择器:
    1. 单层:>
      Soup.select(‘div.list > ul > li > a’):查找所有class为list的div中的ul里的li标签里面的a标签
    2. 多层:空格
      Soup.select(‘p #name’):寻找所有<p>标签且id为name的元素
      Soup.select(‘p .name’):寻找所有class为name的<p>标签

小结

大部分情况下,find和find_all方法就可以解决问题
在这里插入图片描述

selenium中文文档----------文档

Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作。

pip install selenium

Selenium 支持非常多的浏览器

from selenium import webdriver
# 常用Chrome和Edge
browser = webdriver.Chrome() 
browser = webdriver.Edge() 

browser = webdriver.Firefox() 
browser = webdriver.Safari()
browser = webdriver.Android()
browser = webdriver.Ie()
browser = webdriver.Opera()
browser = webdriver.PhantomJS()

浏览器驱动

  1. Chrome Chrome找到对应版本的下载即可。
  2. Edge Edge找到对应版本下载即可。

可以将浏览器驱动放到selenium库的目录下,这样在初始化browser对象时就可以不用传入参数了或者干脆丢个文件夹下,配置环境变量就行。

验证安装

from selenium import webdriver
from time import sleep

browser = webdriver.Chrome()
# 打开百度
browser.get('https://www.baidu.com')
sleep(5)
# 使用完关闭
browser.close()

定位方式

<button id="submit" name="submitBtn" class="text-center">提交</button>
  1. id:标签ID定位
find_element_by_id("submit")
  1. name:标签name属性
find_element_by_name("submitBtn")
  1. class name:标签类名字定位
find_element_by_class_name("text-center")
  1. tag name:标签名字定位
find_element_by_tag_name("button")
  1. link text:链接文本定位
find_element_by_link_text("提交")
  1. partial link text:部分链接文本定位
find_element_by_link_text("交")
  1. xpath:xpath定位
find_element_by_xpath("//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]")
  1. css selector:css选择器定位
方法例子描述
.class.toolbar-search-container选择 class = 'toolbar-search-container' 的所有元素
#id#toolbar-search-input选择 id = 'toolbar-search-input' 的元素
**选择所有元素
elementinput选择所有 <input\> 元素
element>elementdiv>input选择父元素为 <div\> 的所有 <input\> 元素
element+elementdiv+input选择同一级中在 <div\> 之后的所有 <input\> 元素
[attribute=value]type='text'选择 type = 'text' 的所有元素
find_element_by_css_selector('#submit')
find_element_by_css_selector('html>body>div>div>div>div>div>div>button')
定位一个元素定位多个元素含义
find_element_by_idfind_elements_by_id通过元素id定位
find_element_by_namefind_elements_by_name通过元素name定位
find_element_by_xpathfind_elements_by_xpath通过xpath表达式定位
find_element_by_link_textfind_elements_by_link_tex通过完整超链接定位
find_element_by_partial_link_textfind_elements_by_partial_link_text通过部分链接定位
find_element_by_tag_namefind_elements_by_tag_name通过标签定位
find_element_by_class_namefind_elements_by_class_name通过类名进行定位
find_elements_by_css_selectorfind_elements_by_css_selector通过css选择器进行定位

注意:高版本的selenium只提供了find_element和find_elements方法,通过什么寻找需要额外传入By参数。

在这里插入图片描述

from selenium.webdriver.common.by import By

class By:
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"

def find_element(self, by=By.ID, value=None) -> WebElement:
     pass
def find_elements(self, by=By.ID, value=None) -> List[WebElement]:
 	pass

操作标签的一些方法

方法说明
set_window_size()设置浏览器的大小
back()控制浏览器后退
forward()控制浏览器前进
refresh()刷新当前页面
clear()清除文本
send_keys (value)模拟按键输入
click()单击元素
submit()用于提交表单
get_attribute(name)获取元素属性值
is_displayed()设置该元素是否用户可见
size返回元素的尺寸
text获取元素的文本
execute_script(js)执行js脚本
page_source获取页面源码(js已经加载过了)
from selenium import webdriver
from selenium.webdriver.common.by import By

edge = webdriver.Edge()
# 提交表单
edge.find_element(By.ID, "submit").submit()

浏览器控制(无头等)

修改浏览器窗口大小

def set_window_size(self, width, height, windowHandle: str = 'current') -> None:
    """
   	设置浏览器的长宽
   	driver.set_window_size(800,600)
    """

还有一些快捷方式:

def maximize_window(self) -> None:
     """
     最大化
     """
     command = Command.W3C_MAXIMIZE_WINDOW
     self.execute(command, None)

 def fullscreen_window(self) -> None:
     """
     全屏
     """
     self.execute(Command.FULLSCREEN_WINDOW)

 def minimize_window(self) -> None:
     """
     最小化
     """
     self.execute(Command.MINIMIZE_WINDOW)

前进后退

webdriver 提供 back 和 forward 方法来实现页面的后退与前进。

#返回
driver.back()
#前进
driver.forward()

焦点:selenium中,弹出新窗口,焦点不会切换过去,会导致找不到新页面的标签。

方法说明
current_window_handle获得当前窗口句柄
window_handles返回所有窗口的句柄到当前会话
switch_to.window()用于切换到相应的窗口,与上一节的switch_to.frame()类似,前者用于不同窗口的切换,后者用于不同表单之间的切换。
# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
driver.switch_to.window(windows[-1])

鼠标事件(事件链,操作链)

方法说明
ActionChains(driver)构造ActionChains对象
context_click()执行鼠标悬停操作
move_to_element(above)右击
double_click()双击
drag_and_drop()拖动
move_to_element(above)执行鼠标悬停操作
context_click()用于模拟鼠标右键操作, 在调用时需要指定元素定位
perform()执行所有 ActionChains 中存储的行为,可以理解成是对整个操作的提交动作
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

edge = webdriver.Edge()
# 提交表单
edge.maximize_window()

# 点击验证码
ActionChains(edge).move_to_element_with_offset(验证码标签,偏移量x,偏移量y).click().perform()

# 定位要拖动的元素
source = edge.find_element_by_xpath('xxx')
# 定位目标元素
target = edge.find_element_by_xpath('xxx')
# 执行拖动动作
ActionChains(edge).drag_and_drop(source, target).perform()

键盘事件

rom selenium.webdriver.common.keys import Keys

# 模拟回车键进行跳转(输入内容后) 
edge.find_element_by_id('xxx').send_keys(Keys.ENTER)
模拟键盘按键说明
send_keys(Keys.BACK_SPACE)删除键(BackSpace)
send_keys(Keys.SPACE)空格键(Space)
send_keys(Keys.TAB)制表键(Tab)
send_keys(Keys.ESCAPE)回退键(Esc)
send_keys(Keys.ENTER)回车键(Enter)
模拟键盘按键说明
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)
send_keys(Keys.F1…Fn)键盘 F1…Fn

等待

显示等待

显式等待:设置一个超时时间,每个一段时间就去检测一次该元素是否存在,如果存在则执行后续内容,如果超过最大时间(超时时间)则抛出超时异常(TimeoutException)。显示等待需要使用 WebDriverWait,同时配合 until 或 not until 。

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)

- driver:浏览器驱动
- timeout:超时时间,单位秒
- poll_frequency:每次检测的间隔时间,默认为0.5- ignored_exceptions:指定忽略的异常,如果在调用 until 或 until_not 的过程中抛出指定忽略的异常,则不中断代码,默认忽略的只有 NoSuchElementException 。

------------------------------------------------------

until(method, message=’ ‘)
until_not(method, message=’ ')

- method:指定预期条件的判断方法
- message:TimeoutException报错信息

-----------------------------------------------------------------------------

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
element = WebDriverWait(driver, 5, 0.5).until(EC.presence_of_element_located((By.ID, 'submitBtn')),
                                           message='guys,不是叔找不到,是你程序有Bug!')
方法描述
title_is(‘百度一下’)判断当前页面的 title 是否等于预期
title_contains(‘百度’)判断当前页面的 title 是否包含预期字符串
presence_of_element_located(locator)判断元素是否被加到了 dom 树里,并不代表该元素一定可见
visibility_of_element_located(locator)判断元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0
visibility_of(element)跟上一个方法作用相同,但传入参数为 element
text_to_be_present_in_element(locator , ‘百度’)判断元素中的 text 是否包含了预期的字符串
text_to_be_present_in_element_value(locator , ‘某值’)判断元素中的 value 属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it(locator)判断该 frame 是否可以 switch 进去,True 则 switch 进去,反之 False
invisibility_of_element_located(locator)判断元素中是否不存在于 dom 树或不可见
element_to_be_clickable(locator)判断元素中是否可见并且是可点击的
staleness_of(element)等待元素从 dom 树中移除
element_to_be_selected(element)判断元素是否被选中,一般用在下拉列表
element_selection_state_to_be(element, True)判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False
element_located_selection_state_to_be(locator, True)跟上一个方法作用相同,但传入参数为 locator
alert_is_present()判断页面上是否存在 alert

隐式等待

隐式等待是全局性的,即运行过程中,如果元素可以定位到,它不会影响代码运行,但如果定位不到,则它会以轮询的方式不断地访问元素直到元素被找到,若超过指定时间,则抛出异常。

# 等待5s
driver.implicitly_wait(5)
try:
	# 触发隐式等待
    driver.find_element_by_id('btn')
except Exception as e:
    print(f'叔找不到你要的标签,叔给你报个错:{str(e)}')

强制等待

time.sleep()

iframe

  1. 对于内嵌的页面 selenium 是无法直接定位的,需要使用 switch_to.frame() 方法将当前操作的对象切换成 frame/iframe 内嵌的页面。

  2. switch_to.frame() 默认可以用的 id 或 name 属性直接定位,但如果 iframe 没有 id 或 name ,这时就需要使用 xpath 进行定位。

# 1.通过id定位
driver.switch_to.frame('fuck off')
# 2.通过name定位
driver.switch_to.frame('fuck off')
# 3. 通过xpath定位
iframe_label = driver.find_element_by_xpath('/html/body/fuck/off/iframe')
driver.switch_to.frame(iframe_label)

弹窗处理

PS:**的,之前写超星某某通自动评论的脚本,弹窗真心恶心到我了。

JavaScript 有三种弹窗 alert(确认)、confirm(确认、取消)、prompt(文本框、确认、取消)。

方法描述
text获取弹窗中的文字
accept()接受(确认)弹窗内容
dismiss()解除(取消)弹窗
send_keys()发送文本至警告框
alert = driver.switch_to.alert
alert.accept()

文件操作

对于文件操作,我还是觉得可以先拿到url然后多进程,多线程,协程下载就行

上传

edge.find_element_by_xpath('//*[@name="upload"]').send_keys(file_path)

下载

需要用到浏览器选项,from selenium.webdriver import ChromeOptions

from selenium.webdriver import ChromeOptions

prefs = {
	# 禁止弹窗
	'profile.default_content_settings.popups': 0,
	# 文件下载路径
    'download.default_directory': "file_Path"
}
option = webdriver.ChromeOptions() 
option.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome(options=option)


driver.find_element_by_xpath('下载链接').click()
# 如果出现非安全需要切换到最新窗口
driver.switch_to.window(driver.window_handles[-1])
# 输入thisisunsafe即可
driver.find_element_by_xpath('./html').send_keys('thisisunsafe')

cookies

  1. edge.get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
  2. edge.get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
  3. edge.add_cookie(cookie_dict):将 cookie 添加到当前会话中
  4. edge.delete_cookie(name):删除指定名称的单个 cookie。
  5. edge.delete_all_cookies():删除会话范围内的所有 cookie。

关闭操作

  1. quit()关闭所有页面并退出驱动
  2. close()关闭当前页面

下拉框选择操作

from selenium.webdriver.support.select import Select
方法说明
select_by_value(“选择值”)select标签的value属性的值
select_by_index(“索引值”)下拉框的索引
select_by_visible_testx(“文本值”)下拉框的文本值
sel = edge.find_element_by_xpath("//select[@id='nr']")

for selectIndex in range(len(sel.options)):
	# 索引
	print(selectIndex)

Select(sel).select_by_value('50')  # 显示50条

图片处理

# 截图
edge.get_screenshot_as_file(r'截图后保存路径')
# 获取网页图片二进制数据
edge.find_element_by_xpath(xx).screenshot_as_png()
# 对页面进行截图,返回二进制数据
edge.get_screenshot_as_png()

其他属性

# 获取当前页面url
driver.current_url
# 获取当前html源码
driver.page_source
# 获取当前页面标题
driver.title
# 获取浏览器名称(chrome)
driver.name
# 对页面进行截图,返回二进制数据
driver.get_screenshot_as_png()
# 设置浏览器尺寸
driver.get_window_size()
# 获取浏览器尺寸,位置
driver.get_window_rect()
# 获取浏览器位置(左上角)
driver.get_window_position()
# 设置浏览器尺寸
driver.set_window_size(width=1000, height=600)
# 设置浏览器位置(左上角)
driver.set_window_position(x=500, y=600)
# 设置浏览器的尺寸,位置
driver.set_window_rect(x=200, y=400, width=1000, height=600)

无头浏览器

from selenium import webdriver
from selenium.webdriver import ChromeOptions

option = ChromeOptions()
option.add_argument('--headless')
# 都无头了要个锤子图片,不用gpu渲染
option.add_argument("--disable-gpu")
browser = webdriver.Chrome(options=option)

反屏蔽

防止webdriver检测不通过,感谢 @优快云博主「小小明-代码实体」

from selenium import webdriver
from selenium.webdriver import ChromeOptions

option = ChromeOptions()
option.add_argument("--headless")
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
option.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36')
option.add_argument("--disable-blink-features=AutomationControlled")
browser = webdriver.Chrome(options=option)

# https://github.com/kingname/stealth.min.js
with open('stealth.min.js') as f:
    js = f.read()
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': js
})

FAQ

使用quit()无法关闭窗口

爬虫采用Selenium,经过调试,发现访问一些网站结束后,调用webdriver.quit()函数时,代码直接hang住,无法返回及异常发生。

  1. quit() 方法:

    • quit() 方法会完全退出WebDriver会话,关闭所有打开的窗口和标签页,并释放与WebDriver相关的所有资源。
    • 这意味着它会关闭整个浏览器应用程序,而不仅仅是当前窗口或标签页。
    • 通常在测试结束后调用,以确保清理所有资源。

    例如:

    driver.quit()
    
  2. close() 方法:

    • close() 方法只关闭当前窗口或标签页,但不会退出WebDriver会话。
    • 如果当前窗口是唯一打开的窗口,那么它将等同于 quit() 方法,关闭整个浏览器应用程序。
    • 如果有多个窗口打开,close() 将只关闭当前窗口,WebDriver会话将继续运行,可以在其他窗口上执行操作。

    例如:

    driver.close()
    

总结来说,quit() 会完全退出WebDriver会话并关闭浏览器应用程序,而 close() 只会关闭当前窗口或标签页,但不会终止WebDriver会话。

在大多数情况下,如果只需要关闭当前窗口,可以使用 close() 方法。如果需要结束整个WebDriver会话,包括关闭所有窗口和释放所有资源,应该使用 quit() 方法。

  1. 可能的解决方案1
simulated_browser = webdriver.Chrome() 
simulated_browser.get("http://www.baidu.com") 
simulated_browser.close() 
simulated_browser.quit()
  1. 可能的解决方案2:
    退出浏览器,有需要用户确认的确认框引起的阻塞

    # 关闭确认弹出框选项
    browser.tabs.warnOnClose = false
    browser.tabs.warnOnCloseOtherTabs  =  false
    dom.disable_beforeunload  = true
    # 代码屏蔽onunload与onbeforeunload事件
    browser.execute_script("window.οnunlοad=null; window.οnbefοreunlοad=null")
    
  2. 可能的解决方案3:

    使用 try/except 块捕获可能发生的异常,以防止它们阻塞程序执行。

    try:
        driver.quit()
    except Exception as e:
        print(f"发生异常: {e}")
    
    
  3. 可能的解决方案4:
    强制关闭,如果上述方法都无效,可以考虑使用操作系统级别的命令来终止WebDriver进程。在Windows上,可以使用taskkill命令,而在Linux/macOS上,可以使用pkill或kill命令。

    import os
    
    os.system("taskkill /im chromedriver.exe /f")  # Windows示例,如果使用的是其他浏览器,请替换成对应的驱动程序名称
    
好的,我可以为您提供一些关于使用Selenium爬虫解析网页的基本步骤和方法。 1. 安装Selenium和浏览器驱动 首先,您需要安装Selenium模块和浏览器驱动。Selenium支持多种浏览器,如Chrome、Firefox等,您需要根据自己的需求选择相应的浏览器和对应的驱动。 例如,如果您想使用Chrome浏览器,可以通过以下代码安装Selenium和Chrome驱动: ```python pip install selenium ``` ```python from selenium import webdriver # 指定Chrome浏览器的路径和驱动位置 chrome_driver = 'C:/chromedriver.exe' browser = webdriver.Chrome(executable_path=chrome_driver) ``` 2. 访问网页并获取页面源代码 接下来,您需要使用Selenium打开网页并获取页面源代码,以便后续解析。 ```python url = 'https://www.example.com' browser.get(url) # 获取网页源代码 html = browser.page_source ``` 3. 解析网页 有了页面源代码后,您可以使用常见的Python解析库(如BeautifulSoup和lxml)解析网页,获取需要的数据。 ```python from bs4 import BeautifulSoup # 使用BeautifulSoup解析网页 soup = BeautifulSoup(html, 'lxml') # 获取需要的数据 data = soup.find('div', {'class': 'example-class'}).text ``` 4. 关闭浏览器 最后,记得关闭浏览器,释放资源。 ```python browser.quit() ``` 以上就是使用Selenium爬虫解析网页的基本步骤和方法。需要注意的是,Selenium模拟的是真实的浏览器行为,所以速度较慢,不适合高频率的数据抓取。如果您需要进行大规模的数据爬取,建议使用其他更为高效的爬虫框架。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Generalzy

文章对您有帮助,倍感荣幸

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

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

打赏作者

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

抵扣说明:

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

余额充值