本文項目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退出的方法。
*/
}
}
四、結語
看到香鍋被說蹭熱度之後氣不打一處來,於是寫了這段代碼。不過,文明社會,人人有責,我們要做一個高尚的噴子,彈幕只發"藍公主"就好。