使用Selenium實現直播平臺的自動刷屏

本文項目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退出的方法。
         */

    }
}

四、結語

看到香鍋被說蹭熱度之後氣不打一處來,於是寫了這段代碼。不過,文明社會,人人有責,我們要做一個高尚的噴子,彈幕只發"藍公主"就好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章