当前位置: 首页 > news >正文

设计素材网站p兰州模板网站seo价格

设计素材网站p,兰州模板网站seo价格,长沙优化网站建设,wordpress ftp 权限目录 一、什么是自动化测试 二、Selenium介绍 1、Selenium是什么 2、Selenium的原理 三、了解Selenium的常用API 1、webDriver API 1.1、元素定位 1.1.1、CSS选择器 1.1.2、Xpath元素定位 1.1.3、面试题 1.2、操作测试对象 1.3、添加等待 1.4、打印信息 1.5、浏…

目录

一、什么是自动化测试

二、Selenium介绍

1、Selenium是什么

2、Selenium的原理

三、了解Selenium的常用API

1、webDriver API  

1.1、元素定位

1.1.1、CSS选择器

1.1.2、Xpath元素定位

1.1.3、面试题

 1.2、操作测试对象

1.3、添加等待 

1.4、打印信息

 1.5、浏览器的操作

1.6、键盘事件

1.7、鼠标事件 

1.8、定位一组元素

1.9、多层框架/窗口定位

1.10、下拉框处理

1.11、弹窗处理

1.12、上传文件操作

1.13、关闭浏览器操作

1.14、窗口的切换

1.15、截图 



一、什么是自动化测试

自动化测试指软件测试的自动化,自预设下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将认为驱动的测试行为转化为机器执行的过程。

 自动化测试包括UI自动化、接口自动化、单元测试自动化。按照这个金字塔模型来进行自动化测试规划,可以产生最佳的自动化测试产出投入比,可以用较少的投入获得很好的收益。

✨单元测试

最大的投入应再单元测试上、单元测试运行的频率也更加高

Java的单元测试框架是Junit

✨接口自动化

接口测试就是API测试,相对于UI自动化API自动化更加容易实现、执行起来也更稳定。

接口自动化有以下特点

  • 可在产品前期,接口完成后介入
  • 用例维护量小
  • 适合接口变动较小,界面变动频繁的项目

✨UI自动化

虽然测试金字塔告诉我们尽量多做API层的自动化测试,但是UI层的自动化测试更加贴近用户的需求和软件系统的实际业务。并且有时候我们不得不进行UI层的测试。

UI自动化的特点

  • 用例维护量大
  • 页面相关性强、必须后期项目页面开发完成之后介入
  • UI测试适合与界面变动较小的项目。

二、Selenium介绍

1、Selenium是什么

Selenium是用来做web自动化测试的框架。Selenium提供了多种编程语言的绑定,包括Python、Java、C#、JavaScript等,使开发人员能够使用自己熟悉的编程语言来编写自动化脚本。它还支持各种浏览器(Edge、Chrome),支持各种平台(Linux、Windows、Mac),具有丰富的API。

2、Selenium的原理

自动化脚本代码、浏览器驱动和浏览器之间的关系是,自动化脚本代码向浏览器驱动发起请求,驱动操控浏览器执行测试步骤,最终浏览器将结果返回给驱动,驱动最终再将结果返回给自动化程序。

三、了解Selenium的常用API

先来看一个完整的浏览器搜索框的测试

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;import java.util.List;import static java.lang.Thread.sleep;public class Main {public static void main(String[] args) throws InterruptedException {int flag = 0;//创建ChromeOptions对象,用于配置Chrome浏览器启动选项ChromeOptions options = new ChromeOptions();//允许所有请求options.addArguments("--remote-allow-origins=*");//创建Chrome引擎对象WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://www.baidu.com");//找到百度搜索输入框WebElement element = webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));//输入"软件测试(要搜索的内容)"element.sendKeys("软件测试");//找到百度一下按钮,实现点击webDriver.findElement(By.cssSelector("#su")).click();//这里添加休眠函数是为了更清楚的观察sleep(3000);//校验//找到搜索结果List<WebElement> elements = webDriver.findElements(By.cssSelector("a em"));for(int i = 0;i < elements.size();i++){//如果返回的结果有软件测试,证明测试通过,否则测试不通过if(elements.get(i).getText().equals("软件测试")){flag = 1;System.out.println("测试通过");break;}}if(flag == 0){System.out.println("测试不通过");}}
}

 这里getText()方法表示获取特定元素的文本内容。get().getText(),返回你所选择的元素的纯文本内容,不包括HTML标签。

1、webDriver API  

1.1、元素定位

1.1.1、CSS选择器

✨使用到的方法

方法作用
findElement()找到一个元素
findElements()找到一批元素
By.cssSelector()表示通过css选择器查找元素,这个方法用在findElement()方法中作为参数。
sendKeys()模拟用户在输入框中输入文本、按键、组合键等操作。参数可以是字符串、键盘按键、键盘组合键等。
getText()获取特定元素的文本内容

获取class属性值可以通过浏览器的f12键找到一个页面的源码

1️⃣上述是通过class的方式复制内容。

2️⃣我们也可以点到输入框的源码位置,单击鼠标右键,使用css选择器的方式定位输入框,这里复制到的内容是id属性的值。两种方式获取的属性值是不同的,是通过不同的方式(属性)定位到输入框的。

 程序

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;public class Test {public static void main(String[] args) {//启动ChromeChromeOptions options = new ChromeOptions();//允许所有请求options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);//打开百度首页webDriver.get("https://www.baidu.com");//找到百度搜索输入框(通过css选择器找到输入框,这里是通过class属性值定位到的)WebElement element = webDriver.findElement(By.cssSelector(".s_ipt"));//输入软件测试element.sendKeys("软件测试");}
}

 结果

1.1.2、Xpath元素定位
WebElement element = webDriver.findElement(By.xpath("//*[@id=\"kw\"]"));

结果:

1.1.3、面试题

1、css选择器和Xpath选择器的常见语法有哪些? 

✨CSS选择器

  1. 标签选择器:通过标签名来进行定位。语法格式为“标签名”。例如"input"。
  2. id选择器:通过元素的id属性值来定位对应的元素。语法格式"#id值",例如"#kw".
  3. 类选择器:通过元素的class属性值来定位对应的元素。语法格式".class值",例如:".s_ipt"。
  4. 后代选择器:通过空格分隔的两个元素标签来定位对应的元素。语法格式"父级选择器 子级选择器"。例如:"form input".

✨Xpath选择器:

  1. 绝对路径/html/head/title(绝对路径的效率非常低,所以不常用)
  2. 相对路径
  • 相对路径+索引//form[索引值]/span[索引值]/input (这里的索引如果不写,默认是从1开始。一个页面中存在多个相同的标签,这个时候使用索引,表示路径中的标签是页面中的第几个,或者是某个标签(form)下的第几个标签(input)).
  • 相对路径+属性值//input[@calss="s_ipt"]
  • 相对路径+通配符//*[@*=s_ipt]。表示将所有标签下的所有属性值为s_ipt的属性找到。
  • 相对路径+文本匹配//a[text()="新闻"]

 2、css选择器和Xpath选择器你觉得那个更好?

CSS选择器和Xpath选择器都有各自的优点,没有绝对的好会之分,二者在不同的场景下各有优势

  • CSS选择器在Chrome、火狐等浏览器中查找速度快、效率高,语法更简洁,在处理简单元素的查找时更推荐使用。
  • Xpath选择器功能更强大,可以通过包括和排除操作符进行元素集合运算,也可以选择元素的足心和后代,还支持正则表达式和序列运算。

 1.2、操作测试对象

方法作用
click

点击对象

sendKeys模拟用户在输入框中输入文本、按键、组合键等操作
submit提交
text用于获取元素的文本信息
getAttribute获取属性
clear清楚对象输入的文本内容

✨sendKeys、click和clear方法的使用

    private static void test01() throws InterruptedException {ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);webDriver.get("http://www.baidu.com/");sleep(2000);//找到百度输入框,输入"美食"webDriver.findElement(By.cssSelector("#kw")).sendKeys("美食");sleep(2000);//点击百度一下按钮webDriver.findElement(By.cssSelector("#su")).click();sleep(2000);//清空百度搜索输入框webDriver.findElement(By.cssSelector("#kw")).clear();}

由于这里的操作是连续的,小编不会插入动图,这里就展示最终的效果。

✨submit的使用

在上述代码中可以将click方法替换成submit方法,最终实现的效果是相同的。

webDriver.findElement(By.cssSelector("#su")).submit();

但是使用submit方法,存在一定的条件。如果点击的元素放在form标签中,此时使用submit实现的效果click是一样的,如果点击的元素放在非form标签中,此时使用submit会报错

 ✨getAttribute方法的使用

    private static void test03(){ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);webDriver.get("https://www.baidu.com/");String button_value = webDriver.findElement(By.cssSelector("#su")).getAttribute("value");if(button_value.equals("百度一下")){System.out.println("测试通过");}else{System.out.println(button_value);System.out.println("测试不通过");}}

这里获取到了"百度一下"这个文本信息,但是有的老铁就会产生疑问使用getText()方法也可以得到文本信息,为什么使用getAttribute来获取。

 我们使用getText()方法获取文本信息,只能是获取标签中间的文本信息。

1.3、添加等待 

1、强制等待(sleep)

2、智能等待(隐式等待、显示等待)

假设设置等待时间为3天

强制等待就会一直等待,等待时间为3天

隐式等待,最长等待3天时间,如果在三天之内获取页面上的元素,此时就会执行下面的程序,如果等待三天还是没有找到这个元素,就会报错。

强制等待:

隐式等待:

private static void test01() throws InterruptedException {ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);webDriver.get("https://www.baidu.com/");sleep(2000);//找到百度输入框,输入"美食"webDriver.findElement(By.cssSelector("#kw")).sendKeys("美食");//点击百度一下按钮webDriver.findElement(By.cssSelector("#su")).submit();//隐式等待webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.DAYS);//清空百度搜索输入框webDriver.findElement(By.cssSelector("#kw")).clear();}

 显示等待:

  private static void test04(){ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);webDriver.get("https://www.baidu.com/");//等待2秒,等待页面加载完成WebDriverWait wait = new WebDriverWait(webDriver,2);//等待元素加载完成并执行操作wait.until(ExpectedConditions.titleIs("百度一下,你就知道"));}

显示等待和隐式等待的区别

1、作用范围:隐式等待是全局性的等待设置,适用于整体页面的元素定位操作;显示等待是针对特定元素或条件的等待设置,更具体、可定制性更强。

2、等待时间:隐式等待直选哟设置一次,并适用于所有元素的定位操作;显示等待可以根据不同情况设定不同的等待时间。

3、等待条件:隐式等待没有明确的等待条件,只要在规定的时间内找到元素即可;显示等待可以指定等待元素可见、可点击等待特定条件。

4、操作方式:隐式等待是自动等待的,不需要再代码中显示调用;显示等待需要再代码中显示调用等待方法。

1.4、打印信息

打印title和url

    private static void test05() {ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");WebDriver webDriver = new ChromeDriver(options);webDriver.get("https://www.baidu.com/");String url = webDriver.getCurrentUrl();String title = webDriver.getTitle();if(url.equals("https://www.baidu.com/")&&title.equals("百度一下,你就知道")){System.out.println("当前页面url:"+url+",当前页面title:"+title);System.out.println("测试通过");}else{System.out.println("测试不通过");}}

 1.5、浏览器的操作

操作说明
browser.maxmize_window()浏览器窗口最大化
browser.set_window_size(width,hight)设置浏览器宽、高
browser.forward()浏览器的前进
browser.back()浏览器的后退
document.documentElement.scrollTop=0将浏览器滚动条滑动到最顶端
document.documentElement.scrollTop=10000将浏览器滚动条滑动到最低端

 这个代码演示了浏览器的前进、后退、刷新操作。但是介于小编不会使用动图,所以就不添加执行结果了。

    private static void test07() throws InterruptedException {WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://www.baidu.com/");//搜索521webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");//点击百度一下webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);//浏览器后退webDriver.navigate().back();//强制等待3秒sleep(3000);//浏览器刷新webDriver.navigate().refresh();//浏览器前进sleep(3000);webDriver.navigate().forward();}

浏览器的滚动条

private static void test08() throws InterruptedException {WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://www.baidu.com/");//搜索521webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");//点击百度一下webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);webDriver.navigate().forward();sleep(3000);//滚动条到结尾 ((JavascriptExecutor)webDriver).executeScript("document.documentElement.scrollTop=10000");}

 窗口的最大化、窗口的全屏化、设置窗口大小

    private static void test08() throws InterruptedException {WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://www.baidu.com/");//搜索521webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");//点击百度一下webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);((JavascriptExecutor)webDriver).executeScript("document.documentElement.scrollTop=10000");sleep(3000);//浏览器窗口最大化webDriver.manage().window().maximize();sleep(3000);//浏览器全屏化webDriver.manage().window().fullscreen();sleep(3000);//设置浏览器窗口大小webDriver.manage().window().setSize(new Dimension(600,800));}

1.6、键盘事件

操作说明
sendKeys(Keys.TAB)#TAB
sendKeys(Keys.ENTER)#回车
sendKeys(Keys.SPACE)#空格键
sendKeys(Keys.ESCAPE)#回退键(ESC)
sendKeys(Keys.CONTROL,'a')

#全选(Ctrl+A)

sendKeys(Keys.CONTROL,'c')#复制(Ctrl+C)
sendKeys(Keys.CONTROL,'x')#剪切(Ctrl+x)
sendKeys(Keys.CONTROL,'v')#粘贴(Ctrl+v)
    private static void test09() throws InterruptedException {WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://www.baidu.com/");//搜索521webDriver.findElement(By.cssSelector("#kw")).sendKeys("521");//ctrl+awebDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"a");sleep(3000);//ctrl+xwebDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"x");sleep(3000);//ctrl+vwebDriver.findElement(By.cssSelector("#kw")).sendKeys(Keys.CONTROL,"v");sleep(3000);}

1.7、鼠标事件 

操作说明
context_click()右击
double_dlick()双击
drag_and_drop()拖动
move_to_element()移动
    private static void test09() throws InterruptedException {WebDriver webDriver = new ChromeDriver();webDriver.get("https://www.baidu.com/");webDriver.findElement(By.cssSelector("#kw")).sendKeys("520");webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);// 找到图片按钮WebElement webElement = webDriver.findElement(By.cssSelector("#s_tab > div > a.s-tab-item.s-tab-item_1CwH-.s-tab-pic_p4Uej.s-tab-pic"));// 鼠标右击出现框Actions actions = new Actions(webDriver);sleep(3000);//contextClick()右击鼠标actions.moveToElement(webElement).contextClick().perform();}

1.8、定位一组元素

findElements示例,将页面上所有的checkbox都勾选上。

    private static void page01() {WebDriver webDriver = new ChromeDriver();webDriver.get("http://localhost:63342/_20230925testcode/src/main/Page/test01.html?_ijt=hk3glm0bcb2222roak6kf4826i&_ij_reload=RELOAD_ON_SAVE");webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.DAYS);List<WebElement> webElements = webDriver.findElements(By.cssSelector("input"));for(int i = 0; i < webElements.size(); i++) {// 如果每个元素type值等于checkbox进行点击// getAttribute获取页面上的元素属性值,里面的type是当前元素属性if(webElements.get(i).getAttribute("type").equals("checkbox")){webElements.get(i).click();} else {// 否则什么也不操作;}}}

1.9、多层框架/窗口定位

对于一个web应用,经常会出现框架或窗口的应用,这也就给我们的定位带来了一定的困难。

  • 定位一个frame:switch_to.frame(name_or_id_or_frame_element)
  • 定位一个窗口window:switch_to.window(name_or_id_or_frame_element)
    private static void page02() {WebDriver webDriver = new ChromeDriver();webDriver.get("http://localhost:63342/_20230925testcode/src/main/Page/test02.html?_ijt=sdck9iv3t1f7l8bv2khvu2k87t&_ij_reload=RELOAD_ON_SAVE");webDriver.switchTo().frame("f1");webDriver.findElement(By.cssSelector("body > div > div > a")).click();}

1.10、下拉框处理

通过IndexValue来选择演示:

    private static void page03() {WebDriver webDriver = new ChromeDriver();webDriver.get("http://localhost:63342/_20230925testcode/src/main/Page/test03.html?_ijt=ibu1q228hs9l4q026vbbfjp8r3&_ij_reload=RELOAD_ON_SAVE");WebElement webElement = webDriver.findElement(By.cssSelector("#ShippingMethod"));Select select = new Select(webElement);//通过下标来选择
//        select.selectByIndex(3);//通过value值来选择select.selectByValue("12.51");}

1.11、弹窗处理

    private static void test10() throws InterruptedException {WebDriver webDriver = new ChromeDriver();webDriver.get("http://localhost:63342/_20230925testcode/src/main/Page/test04.html?_ijt=bv9np3tl8gm9kv04oam7i2sfij&_ij_reload=RELOAD_ON_SAVE");webDriver.findElement(By.cssSelector("button")).click();sleep(3000);//alert弹窗取消webDriver.switchTo().alert().dismiss();sleep(3000);//点击按钮webDriver.findElement(By.cssSelector("button")).click();//在alert弹窗中输入”张三“webDriver.switchTo().alert().sendKeys("张三");//alert弹窗确认sleep(3000);webDriver.switchTo().alert().accept();}

1.12、上传文件操作

    private static void test11() {WebDriver webDriver = new ChromeDriver();webDriver.get("http://localhost:63342/_20230925testcode/src/main/Page/test05.html?_ijt=16g56va44mth0ok9g9lfkdqj32&_ij_reload=RELOAD_ON_SAVE");webDriver.findElement(By.cssSelector("input")).sendKeys("E:\\360MoveData\\Users\\26542\\Desktop\\图片");}

1.13、关闭浏览器操作

关闭浏览器的操作有两种,quit()方法和close()方法

quit方法:我们以百度的新闻连接为例,进行说明

    private static void test12() throws InterruptedException {WebDriver webDriver = new ChromeDriver();webDriver.get("https:www.baidu.com");webDriver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();sleep(4000);webDriver.quit();}

close()方法:还是以百度的新闻连接为例,进行说明

    private static void test12() throws InterruptedException {WebDriver webDriver = new ChromeDriver();webDriver.get("https:www.baidu.com");webDriver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();sleep(4000);webDriver.close();
}

 新闻页打开之后,之前get方法打开的百度首页被关闭。

注意:两个方法quit()和close()之间的区别

  • quit方法关闭了整个浏览器,close关闭当前页面。(这里的当前页面是指被操作的页面。)
  • quit方法会清空缓存、close不会清空缓存

1.14、窗口的切换

先来看一下错误的代码示范

    private static void test13() throws InterruptedException {WebDriver webDriver = new ChromeDriver();//打开百度首页webDriver.get("https://baidu.com/");//跳转到新闻连接的页面webDriver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();sleep(3000);//找到新闻页面中的搜索框,然后输入"新闻联播"webDriver.findElement(By.cssSelector("#ww")).sendKeys("新闻联播");//在新闻页面中找到点一下按钮,点击webDriver.findElement(By.cssSelector("#s_btn_wr")).click();}

这是因为现在的代码默认获取get方法打开的页面中的这些元素,我们在这里要获取的是另一个页面的元素,所以就会获取不到。就会报错。

这个时候我们就需要使用getWindowHandles方法来获取所有窗口的句柄。

方法说明
getWindowHandles()方法获取所有窗口的句柄
getWindowHandles()方法获取get打开的页面窗口句柄
    private static void test13() throws InterruptedException {WebDriver webDriver = new ChromeDriver();webDriver.get("https://baidu.com/");webDriver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();sleep(3000);//获取所有页面的窗口句柄Set<String> handles = webDriver.getWindowHandles();String target_handle = "";for (String handle:handles){target_handle = handle;}//拿到当前窗口的句柄webDriver.switchTo().window(target_handle);sleep(3000);webDriver.findElement(By.cssSelector("#ww")).sendKeys("新闻联播");webDriver.findElement(By.cssSelector("#s_btn_wr")).click();}

1.15、截图 

这里首先需要引入一个依赖

        <dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency>
    private static void test15() throws InterruptedException, IOException {WebDriver webDriver = new ChromeDriver();webDriver.get("https://www.baidu.com/");webDriver.findElement(By.cssSelector("#kw")).sendKeys("软件测试");webDriver.findElement(By.cssSelector("#su")).click();sleep(3000);//强转成截图对象File file = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);//将截图好的图片存储到E://20231005jietu.png路径下FileUtils.copyFile(file, new File("E:\\12\\20230521.png"));}

http://www.tj-hxxt.cn/news/108821.html

相关文章:

  • qq炫舞做浴缸的网站百度链接提交收录入口
  • 捡个校花做老婆是哪个网站的重庆企业网站排名优化
  • 夸网站做的好怎么夸在线识别图片
  • 太原网站建设的公司排名网上售卖平台有哪些
  • 青岛做网站企业神马搜索seo优化排名
  • 哈尔滨网站设计多少钱手机如何创建网站
  • 东乡哪里有做网站快速网站搭建
  • 定制类电商平台天津seo优化排名
  • 响应式网站制作公司seo比较好的公司
  • 网站后台一般是用什么做的中国最新军事新闻直播
  • 太原网站建设推广微博上如何做网站推广
  • 怎么做app和网站购物中国足球世界排名
  • 做电影网站如何不侵权seo站长优化工具
  • 大型网站建设用什么系统好seo综合优化公司
  • 做建筑设计网站百度推广收费标准
  • 佛山营销网站开发怎么选软件开发工具
  • 网站功能配置腾讯云域名
  • 社会保险网站nba湖人最新新闻
  • 网页设计与制作的实训报告怎样写快速排名生客seo
  • 做网站banner课程封面搜索优化的培训免费咨询
  • 杭州旅游团购网站建设免费网站推广群发软件
  • 贝壳找房网站做销售互联网网络推广
  • 最简单的做网站的工具互联网推广的好处
  • 网站开发前期调研网络营销与市场营销的区别
  • 快速做网站关键词排名百度搜索引擎推广怎么弄
  • 英国房产网站大全成都新闻今日最新消息
  • 东莞高端模板建站网店运营工资一般多少
  • 一个月做网站只要做好关键词优化
  • 网站制作完成之后软广告经典例子
  • 网站 动画 怎么做的seo推广优化排名软件