web自动化测试入门篇03——selenium使用教程_(2)在上述学习基础上,自行选择一个合适的网站,进一步在实践中去运用selenium webd

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

browser.find_element(By.CSS_SELECTOR, ‘input#kw’)


   
 **多属性定位–tag+class属性**



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.CSS_SELECTOR, ‘input.s_ipt’)


   
 同样的,其他的组合方式大家可以举一反三,不断尝试,比如模糊匹配`input[class ~= "局部关键字"]`、层级定位`#form > span > input`等等等等。


 


#### 5.4.5 link text定位


这种定位方式适用于页面中带有超链接的元素,直接通过超链接标签内的文字进行元素定位。


我们以百度首页为例,可以看到该页面中有很多的超链接标签,如果我们想模拟点击跳转至新闻对应页面的操作,就可以用link text的元素定位方法来进行实现。


![在这里插入图片描述](https://img-blog.csdnimg.cn/ebbc739d0b154c909f8726fec7609c0f.png)  
    
 **使用超链接标签对中的“新闻”一词来进行定位。**



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.LINK_TEXT, ‘新闻’).click()


 


#### 5.4.6 partial link text定位


这个定位方式与link text定位十分相像,实际上也就是link text的模糊查找定位方式,对象也是超链接内的文字,只不过他匹配的不是全部文字而是局部。



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.PARTIAL_LINK_TEXT, ‘新’).click()


 


#### 5.4.7 tag定位


tag定位的效率总体来说不高,也不太推荐单独使用,html页面中一般也是由很多相同或不同的标签对组成。就tag而言标签重复的越多,定位的效率其实也就越低。


比如我们想在百度的搜索栏中输入“selenium”关键字,那么光使用tag其实就很难达到我们的目的,甚至无法准确定位到我们想要的元素。如果运气好,搜索栏的input标签在html页面中排在第一位那还好,只要不是第一位,我们就需要编写其他的代码逻辑来辅助我们继续定位这个元素。  
  


**所以下面的代码实在是不能称之为高效的执行代码**



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.TAG_NAME, ‘input’).send_keys(‘selenium’)


 


#### 5.4.8 xpath定位


一般来说无法通过以上的这些元素定位方法定位的情况下,我们会使用xpath定位方法。但这里需要特别注意,xpath方法分为绝对路径和相对路径两种定位方式,博主只推荐如果真要使用xpath就使用相对路径+正则表达式的方式来进行元素定位。不推荐绝对路径的原因就不用博主多说了吧,只要你敢用,后期的脚本维护与复用绝对会让你抓狂的。


 


**还是老样子,我们使用xpath的相对路径写法来定位百度首页的搜索栏。**



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.XPATH, ‘//*[@id=“kw”]’).send_keys(‘selenium’)


   
 另外与find\_element方法相对应的find\_elements方法这里就不多做介绍了,该种方法是将当前页面中所有能匹配上对应元素定位方法的元素全部获取。大家可以根据自己的需求来进行选取和使用。


   
  


### 5.5 延时方式


  我们加载页面时通常会因为网络环境等各方面的客观因素而导致元素加载的速度各不相同,如果此时我们没有对业务操作进行一定的延时执行,那么大概率业务操作会出现各类的`no such element`报错。  
   那么我们就需要在页面元素加载完成之后再对相应的元素进行业务操作来规避上面说的这个问题。Selenium内可以使用三种延时的函数来进行对应的延时业务操作。


 


#### 5.5.1 隐式等待


隐式等待的作用是在页面加载是隐性的进行特定时长的等待,如果在规定的等待时长内页面加载完毕,则会继续进入下一个业务操作,如果没有加载完毕,则会抛出一个超时的异常。这里其实有两个问题,第一,隐式等待是全局性质的,也就是说一旦你设置了个5秒,那整个程序都会使用这个等待时间类进行配置,灵活性较低;第二,如果碰到了有些页面中的元素是局部加载的话,那整个页面的加载是否完成也就没有了其意义,隐式加载无法针对这样的情况作出调整,智能度较低。所以一般来说只要是对于页面的整体加载要求不高或者元素的加载比较稳定的程序,都可以使用隐式等待来进行延时操作。


 



from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
browser.implicitly_wait(5)
browser.find_element(By.XPATH, ‘//*[@id=“kw”]’).send_keys(‘selenium’)


 


#### 5.5.2 显式等待


显式等待的作用则是使用特定的等待时长来进行某些业务逻辑判断,如果判断(比如元素是或否加被定位)在时间完成那继续执行下一个业务操作,如果判断失败也会抛出no such element的异常,而显式等待的默认检查元素周期为0.5秒。乍一看好像与隐式等待差不多,其实不然,首先显式等待是针对页面中某个或某组特定元素而执行的,隐式则是全局,对所有的元素都生效;其二,显式等待可以通过自定义条件来进行元素的定位和选取,隐式则不行。


 



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
ele = WebDriverWait(browser, 10, 0.5).until(EC.presence_of_element_located((By.XPATH, ‘//*[@id=“kw”]’)))
ele.send_keys(‘selenium’)


 


#### 5.5.3 强制等待


这个应该是平时大家代码中用的最多的等待方式了吧,sleep是针对线程进行挂起的一种等待方式,等待时长根据指定的参数来进行决定。最大的好处就是简单粗暴,无任何逻辑在里面,所以也被称为强制等待。


 



from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)
sleep(2)
browser.find_element(By.XPATH, ‘//*[@id=“kw”]’).send_keys(‘selenium’)


   
 那么以上的三种等待方式其实各有各的特点与缺点,三者之间没有绝对的好用和不好用,而在我们的日常工作场景中也希望大家可以根据实际的情况有选择性的使用。


   
  


### 5.6 超时等待


  元素加载超时我们可以使用以上三种延时方式来进行处理,那么页面超时了又该如何操作呢?selenium也为我们准备了两个函数来对应这样的局面。  
    
 **页面加载超时**



browser.set_page_load_timeout(30)


这里推荐将超时的时间有效的拉长,不宜过短。过短的超时时间容易导致整体页面出现未加载html代码情况下直接令驱动无法工作的情况。  
    
    
 **页面异步js或ajax操作超时**



browser.set_script_timeout(30)


这个函数是用于`execute_async_script()`相关的异步js操作超时报错,由于是异步操作,等待时间同理也不易过短。


   
  


### 5.7 键鼠操作


  浏览器中键盘与鼠标的操作也是不可或缺的重要一环,在被测对象的业务要求中往往占有不少的戏份。


   
 **文字输入**



browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)


   
 **点击**



browser.find_element(By.ID, ‘kw’).click()


   
 **点击并按住不放(左键长按),这些模拟鼠标操作需要导入ActionChains包**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’)
act.click_and_hold(ele).perform()


   
 **右键点击**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’)
act.context_click(ele).perform()


   
 **双击**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’)
act.double_click(ele).perform()


   
 **拖拽元素至另一个元素处,ele\_a 为source,ele\_b 为target**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele_a = browser.find_element(By.ID, ‘btn_a’)
ele_b = browser.find_element(By.ID, ‘btn_b’)
act.drag_and_drop(ele_a, ele_b).perform()


   
 **拖拽元素至指定位置后松开,元素后为x,y坐标值**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘btn_a’)
act.drag_and_drop_by_offset(ele, 200, 100).perform()


   
 **鼠标移动至指定元素**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘btn_a’)
act.move_to_element(ele).perform()


   
 **按下指定的键位(示例代码中是回车键)**



from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.key_down(Keys.ENTER).perform()


   
 **松开指定的键位,这里也可以用链式写法**



from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.key_down(Keys.ENTER)
act.key_up(Keys.ENTER)

链式写法

act.key_down(Keys.ENTER).act.key_up(Keys.ENTER).perform()


   
 **移动鼠标到指定坐标位置**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.move_by_offset(100, 200).perform()


   
 **移动到距离指定元素多少距离的位置(从左上角0, 0开始计算)**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.move_to_element_with_offset(ele, 100, 200).perform()


   
 **在指定元素位置松开鼠标**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.click_and_hold(ele).release(ele).perform()


   
 **发送指定的键或者内容至指定元素**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.send_keys_to_element(ele, ‘selenium’).perform()


   
 **暂停所有操作,默认单位为秒**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’).send_keys(‘selenium’)
act.context_click(ele).pause(5).double_click(ele).perform()


   
  


### 5.8 组件操作


  页面中也存在着很多不同种类的组件,比如单选框、多选框、下拉列表、选项卡等。这些操作也可以通过selenium提供的函数进行实现。


   
 **清除指定元素中的内容(输入框等)**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’)
ele.send_keys(‘selenium’)
sleep(2)
ele.clear()


   
 **提交确认(类似于Keys.ENTER的效果)**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from time import sleep

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

act = ActionChains(browser)
ele = browser.find_element(By.ID, ‘kw’)
ele.send_keys(‘selenium’)
sleep(2)
ele.submit()


   
 下拉列表,我们就可以使用Select方法来实现选取操作  
 使用Select方法需要从selenium.webdriver.support.select导入该方法  
 例如下图中某网站的下拉列表对象


![在这里插入图片描述](https://img-blog.csdnimg.cn/aa9a9e93fb044470bc6f039eb8e6c520.png)


   
 **html构造如下**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b7e6a2d58a7b4fffa4a61d1aff869354.png)


   
 **select\_by\_index()方法**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)

ele = browser.find_element(By.ID, ‘input_factor_gj_count’)

需要注意下标要从0开始,选择1%那一项

Select(ele).select_by_index(‘0’)


   
 **select\_by\_value()方法**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)

ele = browser.find_element(By.ID, ‘input_factor_gj_count’)

指定元素的value属性值,选择1%那一项

Select(ele).select_by_value(‘0.01’)


   
 **select\_by\_value()方法**



from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)

ele = browser.find_element(By.ID, ‘input_factor_gj_count’)

指定元素的文本值,选择1%那一项

Select(ele).select_by_visible_text(‘1%’)


   
 **框架切换(Frame)**  
 如果在页面中定位某一个元素失败并检查其属性并没有问题时,就该考虑是否在祖先节点中是否存在frame或者iframe标签。这样的页面就表名存在多层框架嵌套,这时我们就需要进行框架切换的操作,来准确定位到指定元素。


例如某页面存在两层frame嵌套,内部框架的xpath为:`//*[@id="mainDiv"]/iframe`,此时如果定位某个输入框失败之后,我们就应该转而跳入该frame内进行元素定位。  
  



from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)

定位到指定的frame

path = browser.find_element(By.XPATH, ‘//*[@id=“mainDiv”]/iframe’)

切换至该frame内

browser.switch_to_frame(path)

ele = browser.find_element(By.CLASS_NAME, ‘input’)
ele.send_keys(‘selenium’)


   
 **标签页切换**  
 我们浏览器在使用中一般会打开多个浏览窗口,也就是多个标签页。此时我们就可以通过每个标签页的句柄来进行定位和互相切换。


switch\_to\_window()  
 我们利用浏览器窗口的句柄来进行标签页的切换



from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

第一个窗口的句柄下标为0,打开第二个就是1

browser.switch_to_window(browser.window_handles[1])

此时就会使用第二个标签页去访问淘宝

browser.get(‘https://www.taobao.com’)


   
 **弹窗处理**  
 页面中时常也存在着各类的弹窗,比如警告、确认、提示等等,那么对于这些弹窗我们也有着相应的业务处理方法。  
    
 首先我们需要明确的是每种类型的弹窗中元素也是各不相同,所以我们针对不同的弹窗使用不同的属性来定位和操作。


   
 **警告弹窗**  
 一般来说就是告知到使用者执行某些操作与页面之后将要注意的事项,这种窗口只需确认。



from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)
browser.find_element(By.ID, ‘btn_tg_title’).click()
sleep(1)

在警告弹窗中点击确认按钮

browser.switch_to_alert().accept()


   
 **确认弹窗**  
 多用于在用户执行提交操作后的动作确认,有确认和取消两个选项。



from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)
browser.find_element(By.ID, ‘btn_submit’).click()
sleep(1)
#确认二选一
browser.switch_to_alert().accept()
#取消二选一
browser.switch_to_alert().dismiss()


   
 **提示弹窗**  
 这个通常用来处理用户信息交互的场景,用户可以通过弹窗输入一些文字信息,来传递与进行后续的业务处理。



from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get(‘https://www.xxxx.com’)
browser.find_element(By.ID, ‘btn_info_inquiry’).click()
sleep(1)
# 这里传递信息到提示弹窗,警告与确认两类弹窗不可使用,会报错
browser.switch_to_alert().send_keys(‘20100909’)


   
  


### 5.9 鉴权操作


  既然是web自动化测试,那我们又怎么能少了Cookie的相关操作呢?用户的状态和业务请求都的需要用他来进行鉴权。在selenium中对Cookie进行操作其实也很简单,首先我们需要手动的登录被测网站一次,待网站Cookie存入本地后即可使用Selenium自带的函数对齐进行业务操作。


   
 **首先我们获取对应网站的Cookie值**



import json
from selenium import webdriver

browser = webdriver.Chrome()
browser.get(‘https://www.baidu.com’)

写入保存为文件还是直接打印至控制台,各取所需

with open(‘cookies.txt’, ‘w’) as f:
f.write(json.dumps(browser.get_cookies()))

cookies = browser.get_cookies()
print(cookies)


   
 **接下来我们简单的对获得的Cookie进行处理**



删除所有的cookies

browser.delete_all_cookies()

循环打印cookie中的name和value

for c in cookies:
# 查看打印出的cookie对应的键值对
print(“%s : %s” % (c[‘name’], c[‘value’]))

根据自己的业务需求进行cookie的增删改

cookie = {“name”: “xxxx”, “value”: “xxxx”}

最后添加即可

driver.add_cookie(cookie)


   
  


### 5.9 js操作


  Selenium也为我们提供了可执行js脚本相关的函数,他们的作用是在某些页面中模拟一些业务动作(画面滑动,选择日期等)。在一些仅靠webdriver无法实现的业务场景中,我们就可以依靠此函数来辅助测试目的的达成。


 


**js的查找元素方法(ID)**



document.getElementById(“id”)


   
 **js的查找元素方法(name)**



document.getElementsByName(‘name’)


   
 **js的查找元素方法(class)**



document.getElementsByClassName(“class_name”)


   
 **js的查找元素方法(tag)**



document.getElementsByTagName(‘tag_name’)


   
 **js的查找元素方法(css)**



document.querySelector(“css selector”)


   
 **js的查找元素方法(css\_list)**





![img](https://img-blog.csdnimg.cn/img_convert/9d0707e9cdde88d4394e7fae46c9d745.png)
![img](https://img-blog.csdnimg.cn/img_convert/04bd2cad7938cd0d9b7c7fccdd223942.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618608311)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

kie)

5.9 js操作

Selenium也为我们提供了可执行js脚本相关的函数,他们的作用是在某些页面中模拟一些业务动作(画面滑动,选择日期等)。在一些仅靠webdriver无法实现的业务场景中,我们就可以依靠此函数来辅助测试目的的达成。

js的查找元素方法(ID)

document.getElementById("id")

js的查找元素方法(name)

document.getElementsByName('name')

js的查找元素方法(class)

document.getElementsByClassName("class\_name")

js的查找元素方法(tag)

document.getElementsByTagName('tag\_name')

js的查找元素方法(css)

document.querySelector("css selector")

js的查找元素方法(css_list)

[外链图片转存中…(img-TB5RsmWG-1715066922885)]
[外链图片转存中…(img-pG7vVXu3-1715066922885)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值