本文项目Github地址:https://github.com/zhouhuanghua/auto-flood-screen
最近,电竞圈LOL届最大的瓜非"蓝公主"莫属。本来,我也只是一名吃瓜群众,感觉都无所谓。但是,后面Baolan的亲妈粉闪现开团:嘲讽皇族官宣香锅退役是蹭热度,抢走了宝蓝生日的风头。这就不能忍了,作为RNG的忠实铁粉,而且还是香锅退役这么隆重的事情。怎么办呢?只能去最多人可以看到的网络直播平台刷"蓝公主"来过瘾一下了。不过,手动去刷的话太慢了没啥效果,作为一名码农,时刻要想到利用程序去处理问题。废话不多说哈,开始搞事情了。
这里以某牙为例,其它的大家可以举一反三。发弹幕最大的问题就是账号登录问题,网站设计者想尽一切办法来阻止机器人行为。但是,道高一尺,魔高一丈,现在连滑动解锁的方式都已经有了解决方案。不过,Huya还没那么复杂,相对来说算比较简单的了。
这里示范了两种实现方式:第一种,自动登录-->用户直接执行程序自动打开浏览器并进入指定的直播间,然后根据配置的账号密码或者手机号实现自动登录,最后开始刷指定的弹幕;第二种,手动登录-->用户首先执行一条cmd命令(或者封装成一段程序)打开浏览器,然后自己手动打开一个直播间登录,最后执行一段程序开始刷指定的弹幕。
老规矩,上镇楼图
首先,有必要给大家介绍一点前置知识。
1、什么是Selenium?
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。
这是摘自百度百科的说法,一句话来说就是:通过写代码让程序像人一样去操作浏览器。它依赖具体的浏览器驱动。
2、怎么去定位元素?
Selenium提供了很多方式去定位一个元素,就跟JQuery一样,什么根据id呀class呀tag呀应有尽有,还有css选择器。不过,我最喜欢用的是xpath,为啥呢,因为简单,有手就行。下面来示范一下
首先打开你的Chrome浏览器,进入一个网页,选中指定元素右击,然后点击检查。或者按F12像下面这样操作
这样子之后就会定位到对应得HTML。接着,鼠标放到这一行上面,右击后选择Copy。最后在显示出来的界面点击最后一个Copy XPath,就复制了对应的值了。小伙伴们学会了吗?代码里面都是基于这种方式。
3、驱动和jar包去哪里下载?
驱动:http://npm.taobao.org/mirrors/chromedriver/,下载跟你浏览器一样或者接近的版本。
jar包:http://npm.taobao.org/mirrors/selenium/,我喜欢最新的。
然后,就开始进入今天的主题了。
一、自动登录
talk is cheap,show me the code。
1、(完整代码)自动打开浏览器,进入到指定网页,根据账号密码自动登录。
/**
* 自动登录刷弹幕
*
* @author z_hh
*/
public class Auto {
public static void main(String[] args) throws Throwable {
// 设置驱动位置
System.setProperty("webdriver.chrome.driver", "./driver/chromedriver.exe");
// 创建一个驱动对象
WebDriver driver = new ChromeDriver();
// 打开指定网页
String url = "https://www.huya.com/17859972";
driver.navigate().to(url);
// 智能等待一下子
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
// 窗口最大化
driver.manage().window().maximize();
// 等待,直到登录元素可以点击
WebElement loginElement = driver.findElement(By.xpath(".//*[@id=\"nav-login\"]"));
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
wait.until(ExpectedConditions.elementToBeClickable(loginElement));
//点击登录
loginElement.click();
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
// 切换到登录框里面
driver.switchTo().frame("UDBSdkLgn_iframe");
// 账号登录
accountLogin(driver);
// 手机验证码登录
// mobileLogin(driver);
// 发弹幕
Utils.send(driver);
}
private static void accountLogin(WebDriver driver) {
// 输入账号
driver.findElement(By.xpath(".//*[@id=\"account-login-form\"]/div[1]/input")).sendKeys("13800138000");
// 输入密码
driver.findElement(By.xpath(".//*[@id=\"account-login-form\"]/div[2]/input")).sendKeys("xx00");
// 登录
driver.findElement(By.xpath(".//*[@id=\"login-btn\"]")).click();
}
private static void mobileLogin(WebDriver driver) {
// 切换到手机验证码登录
driver.findElement(By.xpath(".//*[@id=\"login-head-nav\"]/ul/li[2]")).click();
// 输入手机号
driver.findElement(By.xpath(".//*[@id=\"phone-login-form\"]/div[1]/input")).sendKeys("13800138000");
// 发送验证码
driver.findElement(By.xpath(".//*[@id=\"phone-login-form\"]/div[2]/span")).click();
// 获取验证码输入框的值,直到够了6位数
for (;;) {
String text = driver.findElement(By.xpath(".//*[@id=\"phone-login-form\"]/div[2]/input")).getAttribute("value") ;
if (Objects.nonNull(text) && Objects.equals(text.length(), 6)) {
break;
}
}
// 点击登录
driver.findElement(By.xpath(".//*[@id=\"phone-login-btn\"]")).click();
}
}
2、(完整代码)定时循环发送弹幕。
/**
* 通用工具类
*
* @author z_hh
*/
public class Utils {
/**
* 无限循环发送弹幕
*
* @param driver 驱动
*/
public static void send(WebDriver driver) {
// 弹幕内容
String msg = "蓝公主";
// 输入框元素
WebElement inputElement = driver.findElement(By.xpath(".//*[@id=\"pub_msg_input\"]"));
// 发送按钮元素
WebElement sendElement = driver.findElement(By.xpath(".//*[@id=\"msg_send_bt\"]"));
for (int i = 1; ; i++) {
try {
// 找到输入框并写入内容(为了不重复,后面拼上次数)
inputElement.sendKeys(msg + i);
// 点击发送
sendElement.click();
// 等待10秒钟,并清空输入框
TimeUnit.SECONDS.sleep(10);
inputElement.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
简单补充说明一下
1)驱动的位置,我放在项目的一个文件夹里面,代码写的是相对路径。
2)切换到登录框。这个很关键,要不然定位不到填写账号密码的元素。
3)这里两种登录方式选其一即可。手机验证码登录需要手动配合输入验证码,这里只是为了提供多一种选择以备不时之需。
二、手动登录
既然有了自动登录,为啥还要手动登录呢?有三点原因:(1)自由度比较高;(2)以备不时之需;(3)最重要,分享一个重要的知识点。
打起精神来,这个知识点就是:
有时候我们需要手动打开浏览器,进入到所需的页面,执行一些手动任务,如输入表单、输入验证码,登录成功后,才开始运行自动化脚本。这种情况下如何使用Selenium来接管先前已经打开的浏览器呢?
其实就是,使用Selenium控制已打开的浏览器。针对谷歌浏览器,我们可以利用Chrome DevTools协议:它允许客户检查和调试Chrome浏览器。
怎么使用呢?
1、将Chrome程序添加到计算机的环境变量Path里面。
如图,桌面选中Google Chrome,右击,打开文件所在的位置。
2、在cmd命令窗口输入chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\selenum\AutomationProfile"之后回车,在浏览器自动打开之后,手动进入到指定的直播间并进行登录。
- remote-debugging-port:可以指定任何打开的端口。
- user-data-dir:指定创建新Chrome配置文件的目录。它是为了确保在单独的配置文件中启动chrome,不会污染你的默认配置文件。
作为一名程序员,这个操作你也可以用代码完成。
/**
* Debug模式打开浏览器
*
* @author z_hh
*/
public class OpenChrome {
public static void main(String[] args) {
try{
Runtime.getRuntime().exec("chrome.exe --remote-debugging-port=9527 --user-data-dir=\"C:/selenum/AutomationProfile\"");
}catch(Exception e){
e.printStackTrace();
}
}
}
3、(完整代码)创建一个控制已打开浏览器的WebDriver对象。
之后,就可以去刷弹幕了(同第一种方式)。
/**
* 刷屏
*
* @author
*/
public class Flood {
public static void main(String[] args) throws Throwable {
// 设置驱动位置
System.setProperty("webdriver.chrome.driver", "./driver/chromedriver.exe");
// 配置选项(端口跟打开浏览器时设置的要一样)。还有其它的,此处不做介绍
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress", "127.0.0.1:9527");
// 创建一个驱动对象
WebDriver driver = new ChromeDriver(options);
System.out.println(driver.getTitle());
Utils.send(driver);
}
}
主体部分讲解完了。最后,送给大家一点补习资料。
三、(完整代码)附上Selenium的一些其它操作
这段代码网上找来的,我写的没这么烂~~~
public class Demo {
public static void main(String[] args) {
System.setProperty("webdriver.chrome.driver", "D:/eclipse_workspace/automation/driver/chromedriver.exe");// chromedriver服务地址
WebDriver driver = new ChromeDriver(); // 新建一个WebDriver 的对象,但是new 的是谷歌的驱动
String url = "http://www.baidu.com";
driver.get(url); // 打开指定的网站
//driver.navigate().to(url); // 打开指定的网站
/*
*
* driver.findElement(By.id("kw")).sendKeys(new String[] { "hello" });//
* 找到kw元素的id,然后输入hello driver.findElement(By.id("su")).click(); // 点击按扭
*/
try {
/**
* WebDriver自带了一个智能等待的方法。 dr.manage().timeouts().implicitlyWait(arg0, arg1);
* Arg0:等待的时间长度,int 类型 ; Arg1:等待时间的单位 TimeUnit.SECONDS 一般用秒作为单位。
*/
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
//获取当前浏览器的信息
System.out.println("Title:" + driver.getTitle());
System.out.println("currentUrl:" + driver.getCurrentUrl());
//执行js脚本
String jString = "alert('122')";
((JavascriptExecutor) driver).executeScript(jString);
//获取单个元素
WebElement element = driver.findElement(By.id("wrapper"));
System.out.println(element.getAttribute("class"));
//获取多个元素
List<WebElement> elList = driver.findElements(By.tagName("input"));
for (WebElement e : elList) {
System.out.println("获取多个元素:"+e.getAttribute("name"));
}
//定位层级元素
WebElement e = driver.findElement(By.cssSelector("#qrcode-item qrcode-item-1"));
List<WebElement> list = e.findElements(By.tagName("div"));
for (WebElement e1 : list) {
System.out.println("定位层级元素:"+e1.getAttribute("class"));
}
//获取当前的窗口
String currentWindow = driver.getWindowHandle();
//获取所有的窗口
Set<String> handles = driver.getWindowHandles();
//遍历窗口
Iterator<String> iterator = handles.iterator();
while (iterator.hasNext()) {
if (currentWindow == iterator.next())
continue;
//跳转到弹出的窗口
WebDriver driver2 = driver.switchTo().window(iterator.next());
driver2.getTitle();
}
//处理弹出框
Alert alert = driver.switchTo().alert();
alert.getText();
alert.dismiss();//相当于点击取消
Alert confirm = driver.switchTo().alert();
confirm.getText();
confirm.accept();//相当于点击确认
Alert prompt = driver.switchTo().alert();
prompt.getText();
prompt.accept();
prompt.sendKeys("测试1");//输入值
//处理下拉列表
Select select = new Select(driver.findElement(By.id("select")));
select.selectByIndex(1);
select.selectByValue("西安");
select.selectByVisibleText("咸阳");
//获取下拉框的全部选项
List<WebElement> list2 = select.getOptions();
for (WebElement webElement : list2) {
webElement.click();//点击下拉框
}
//处理cookie
//添加一个cookie
Cookie cookie = new Cookie("COOKIE", "NAME","D://COOKIES");
driver.manage().addCookie(cookie);
//获取cookies
Set<Cookie> set = driver.manage().getCookies();
Iterator<Cookie> iterator2 = set.iterator();
while (iterator2.hasNext()) {
Cookie c = iterator2.next();
c.getName();
c.getValue();
c.getPath();
}
driver.manage().deleteAllCookies();
driver.manage().deleteCookie(cookie);
driver.manage().deleteCookieNamed("COOKIE");
//等待加载完页面
try {
driver.manage().timeouts().wait(1);
driver.manage().timeouts().implicitlyWait(1,TimeUnit.SECONDS);//等待界面加载完
} catch (InterruptedException e2) {
e2.printStackTrace();
}
//模拟鼠标和键盘操作
Actions action = new Actions(driver);
action.click();
action.dragAndDrop(element, e);
action.sendKeys(element,"12222").perform();
action.click(element);
action.keyDown(currentWindow);
// driver.quit();// 退出浏览器
/**
* dr.quit()和dr.close()都可以退出浏览器,简单的说一下两者的区别:第一个close,
* 如果打开了多个页面是关不干净的,它只关闭当前的一个页面。第二个quit,
* 是退出了所有Webdriver所有的窗口,退的非常干净,所以推荐使用quit最为一个case退出的方法。
*/
}
}
四、结语
看到香锅被说蹭热度之后气不打一处来,于是写了这段代码。不过,文明社会,人人有责,我们要做一个高尚的喷子,弹幕只发"蓝公主"就好。