1. 程式人生 > >Selenium Webdriver 的使用java執行js程式碼 解決 ScriptEngine不支援瀏覽器內建物件window,document的問題

Selenium Webdriver 的使用java執行js程式碼 解決 ScriptEngine不支援瀏覽器內建物件window,document的問題

問題場景:

使用java 掉用js程式碼,發現 ScriptEngine不支援瀏覽器內建物件window,document的問題;

問題一:為什麼要 用java掉用js程式碼?

    比如在 抓取(爬取)對方網站時,需要破解一些js邏輯程式碼合作加密演算法,但是js混淆了,不能直接翻譯出對應的邏輯,或者翻譯的代價太高;

   那麼 不如 直接 使用js檔案,模擬呼叫;這是 就會 使用到 java呼叫js的場景;

  java 有支援呼叫js的解析引擎,示例程式碼如下:

/**
	 * 獲取java 提供的 ScriptEngine 指令碼執行引擎 資訊
	 * @throws IOException
	 */
	@Test
	public void showScriptEngine() throws IOException {
		ScriptEngineManager manager = new ScriptEngineManager();
		List<ScriptEngineFactory> factories = manager.getEngineFactories();
		for (ScriptEngineFactory f : factories) {
			System.out.println(f.getEngineName());
			System.out.println(f.getEngineVersion());
			System.out.println(f.getLanguageName());
			System.out.println(f.getLanguageVersion());
			System.out.println(f.getExtensions());
			System.out.println(f.getMimeTypes());
			System.out.println(f.getNames());
		}
	}
	  
	/**
	 * java呼叫js 解析執行js指令碼(js檔案)程式碼
	 * 場景:
	 * 1,使用js特有的優勢
	 * 2,js特有的一些加密方式,並且js程式碼進行混淆了,轉換為java方式 代價比較高的情況下
	 * @throws Exception
	 */
	@Test
	public void testjava2js() throws Exception {
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("javascript");
		
		// 如果需要引用 js外部檔案,可以通過流把js檔案(jquery.js)讀到一個String變數
		
		/*
		String jsFileName = "C:\\jquery.min.js"; // 讀取js檔案
		FileReader reader = new FileReader(jsFileName); // 執行指定指令碼
		engine.eval(reader);
		*/
		String script = "function add(op1,op2){return op1+op2};var res1=2,res=10; "; //定義函式並呼叫
		engine.eval(script);
		Invocable invoke = (Invocable) engine; // 呼叫方法,並傳入兩個引數
		
		// 方式1 通過物件呼叫方法, 獲取結果
		Object c = invoke.invokeFunction("add", 1,2);
		System.out.println(c);
		
		// 方式2 執行js指令碼呼叫方法, 獲取結果
		engine.eval("var res = add(2,3);");
		
		// 獲取新定義的變數,會覆蓋原有同名變數
		Object o = engine.get("res");
		System.out.println(o);
		
		// 獲取 原有指令碼/指令碼檔案 中的 變數
		Object o2 = engine.get("res1");
		System.out.println(o2);
		
		// 測試 瀏覽器 內建物件 是否支援
		try {
			engine.eval("alert(2);");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("document.write(2);");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("var innerHeight=window.innerHeight");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		try {
			engine.eval("var userAgent=navigator.userAgent");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		// ...so, ScriptEngine 不支援 瀏覽器內建物件;nodejs 也一樣不支援  瀏覽器內建物件
	}

問題,這裡有個問題,如果js程式碼中 有獲取 瀏覽器 內建物件的 地方,比如 window.location, navigator.userAgent等,

那麼ScriptEngine 解析的時候 就會 提示:

ReferenceError: "alert" is not defined in <eval> at line number 1
ReferenceError: "document" is not defined in <eval> at line number 1
ReferenceError: "window" is not defined in <eval> at line number 1
ReferenceError: "navigator" is not defined in <eval> at line number 1

那麼 怎麼 讓 解析的時候 讓ScriptEngine 支援 瀏覽器內建物件列?

首先 ScriptEngine 本身 不帶 瀏覽器核心,那麼就需要去 找其他 解析引擎;

其次 嘗試 看了nodejs的文件,傳說中 nodejs使用的v8引擎,應該支援吧,

const v8 = require('v8');
const vm = require('vm');

嘗試了一下 發現 並不支援。他也是 後臺的模式,和java的ScriptEngine 一致,只支援解析 非瀏覽器核心的js邏輯程式碼;

也對,畢竟 後臺執行的js邏輯程式碼 怎麼會 需要 獲取瀏覽器物件列?

只能說我這個場景 太特殊了;

ok,此時 兩個辦法:

1,找到一個 java可以呼叫的 瀏覽器核心,模擬 js在瀏覽器中執行,並獲取 執行之後的結果。

2,老老實實 讀 js程式碼,讀懂後 找到 java的邏輯實現方式。

分工一下 兩個方式 同時開始。

小插曲:

 為什麼 不可以 把js程式碼放在jsp 或者html中,通過http的方式呼叫 獲取返回的結果?

 首先 把js程式碼放在jsp 或者html中,獲取執行結果。

jsp是在 伺服器端就執行好了,js程式碼是瀏覽器中執行的;

http訪問的是伺服器,中間沒有經過瀏覽器,就是說 頁面中jsp的程式碼 執行了,但是js程式碼是不會有機會執行。

其次:http訪問jsp或者html 返回的是伺服器編譯執行後的 結果程式碼,而這些 頁面程式碼 在通過瀏覽器 執行後,呈現給使用者;

使用者才看到的是 華麗的頁面;

所以這裡 不清楚 web程式設計 和 html js css 解析渲染時機的 同學要注意了。

所以 把js程式碼 掛在 web容器中 再訪問 是不能解決這個問題,因為 中間 同樣沒有瀏覽器核心。

雖然 以為在瀏覽器 訪問 web地址 可以看到 結果;但是 用java http 後臺訪問 是不行滴。

我來繼續尋找,瀏覽器核心的解決方式。

這時想起了 web自動化測試的方案,曾經看到測試的同事 用java編寫 web自動化測試的指令碼;

可以通過 java程式碼開啟瀏覽器進行 元素定位,數值校驗,邏輯登陸 結果校驗。。

百度了一下:WEB 自動化測試工具 看到了 selenium web自動化測試工具套 

可以看看 這個文章 ,搭建環境示例 :http://blog.csdn.net/kash_chen007/article/details/40586067

邏輯是這樣的:

java 通過 selenium 工具套件,結合 本地的 瀏覽器驅動 與 本地的 瀏覽器 進行互動,來 模擬操作;

達到的效果是:

java 通過 selenium   可以開啟 chrome/firefox/IE等瀏覽器 (裝不同的驅動,開啟不同的瀏覽器)

java 通過 selenium  開啟瀏覽器 輸入網址,獲取 瀏覽器解析之後的 結果。

還可以通過 元素定位 ,元素解析的方式,獲取指定區域的 資料。

好了 ;

原理問題解決了,廢話少說 ,上程式碼:

方式1,顯示呼叫;

方式2,後臺執行,用於部署到正式環境(這個方式不需要依賴驅動包和本地瀏覽器,有jar包即可)

/**
	 * 方式1;使用 ChromeDriver 顯式呼叫 瀏覽器
	 * 當然這裡 也可以用firefox IE等其他 瀏覽器和diver,下載不同的瀏覽器和驅動即可
	 * java 通過 selenium 工具套-->開啟本地瀏覽器-->輸入html檔案地址(支援遠端和本地)-->執行html中的指令碼程式碼-->獲取結果
	 * 這裡 獲取的結果 不是 html程式碼,而是 在瀏覽器解析完html js程式碼之後展示的資訊(這裡可能不太好理解,一會我畫個圖 單獨描述下,html js jsp執行的時機問題)
	 * 詳細描述:
	 * java通過 selenium 工具套 ,讓js指令碼在瀏覽器核心中執行,然後 獲取返回結果
	 * 這樣就解決了 java ScriptEngine 不支援瀏覽器內建物件的問題
	 * 如此一樣js程式碼 與實際web執行的環境一樣執行
	 * @throws Exception
	 */
	@Test
	public void testSelenium0() throws Exception {
		// 第一步:因為是 使用chromediver所以 需要先安裝chrome瀏覽器,chromediver
		// 這個部落格介紹的很詳細,地址:https://www.testwo.com/blog/6931
		
		// 第二步:設定環境變數
		System.setProperty("webdriver.chrome.driver",
				"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chromedriver.exe");
		ChromeDriver webDriver = new ChromeDriver();
		
		long start = System.currentTimeMillis();
		
		// 第三步:設定訪問地址
		//----
		// 這裡和瀏覽器 位址列目 可以輸入的 地址一樣 支援 遠端地址 和 本地地址
		//webDriver.get("http://www.51jdy.cn");
		//webDriver.get("http://localhost:8080/web/Noname2.html?pwd=98912&username=halou");
		webDriver.get("file:///Users/guangtaozhai/Documents/workspace/web/WebContent/Noname2.html?pwd=98912&username=halou");
		
		// 第四步:獲取輸出資訊;html中的js程式碼執行後 在body中document.write()或者 賦值給body或者div
		// 元素定位 獲取 <html> 中<body> 下面的 內容
		WebElement webElement = webDriver.findElement(By.xpath("/html/body"));
		System.out.println(webElement.getText());
		System.out.println("耗時:"+(System.currentTimeMillis()-start));
		///---
		webDriver.close();
	}
	
	/**
	 * 方式2:隱式呼叫(java 後臺呼叫瀏覽器/selenium/webdriver)
	 * 因為 方式1 會開啟本地瀏覽器,這樣一是速度慢,二是 不滿足後臺執行的方式 比如 部署在linux伺服器上以後
	 * 方式2 通過 selenium 提供的 HtmlUnitDriver 進行後臺呼叫;執行效率大幅上升
	 * @throws Exception
	 */
	@Test
	public void testSelenium1() throws Exception {
		// 使用HtmlUnitDriver 是不需要 安裝 瀏覽器 和 驅動支援
		HtmlUnitDriver webDriver = new HtmlUnitDriver();
		webDriver.setJavascriptEnabled(true); // 設定支援 js指令碼解析 ,是不是跟 安卓的 webview設定很像?
		
		long start = System.currentTimeMillis();
		//---- 
		// 這裡和瀏覽器 位址列目 可以輸入的 地址一樣 支援 遠端地址 和 本地地址
		//webDriver.get("http://www.51jdy.cn");
		//webDriver.get("http://localhost:8080/web/Noname2.html?pwd=98912&username=halou");
		webDriver.get("file:///Users/guangtaozhai/Documents/workspace/java2js/WebContent/Noname2.html?pwd=98912&username=halou");
				
		WebElement webElement = webDriver.findElement(By.xpath("/html/body"));
		System.out.println(webElement.getText() );
		System.out.println(System.currentTimeMillis()-start);
		///---
		webDriver.close();
	}

注意:

在實際開發的時候 最好 單獨 起一個專案,釋出成服務的方式;

1,單獨專案 避免jar包衝突,在實際 開發的時候 我遇到了這個問題,企圖通過maven 解決 也沒有搞定。

2,釋出成獨立服務 避免了 效能問題 影響其他業務程式碼,這個 還是比較 耗費資源的。

專案測試程式碼git地址:https://git.oschina.net/s9/jdy-test

專案關鍵字:
java呼叫js
ScriptEngine
ScriptEngine不支援瀏覽器內建物件window,document等
selenium
Selenium Webdriver
web自動化測試
java開啟瀏覽器執行並獲取結果
java呼叫瀏覽器核心執行js
ChromeDriver
HtmlUnitDriver
java後臺呼叫瀏覽器核心
web爬蟲
selenium remote
Caused by: java.lang.ClassNotFoundException: org.openqa.selenium.remote.SessionNotFoundException

相關推薦

Selenium Webdriver 的使用java執行js程式碼 解決 ScriptEngine支援瀏覽器物件window,document的問題

問題場景: 使用java 掉用js程式碼,發現 ScriptEngine不支援瀏覽器內建物件window,document的問題; 問題一:為什麼要 用java掉用js程式碼?     比如在 抓取(爬取)對方網站時,需要破解一些js邏輯程式碼合作加密演算法,但是js混淆了

selenium(webdriver)中執行js

程式碼 JavascriptExecutor js = (JavascriptExecutor) driver; js.executeScript(String script, object...

JS封閉函式、閉包、物件

1、全域性變數:在函式之外定義的變數,為整個頁面公用,函式的內部外部都可以訪問。 2、區域性變數:在函式內部定義的變數,只能在定義該變數的函式內部訪問,外部無法訪問。函式內部訪問變數時,先在內部查詢是否有此變數,如果有,就使用內部,如果沒有,就去外部查詢  二、封閉函式 封閉函

selenium執行js程式碼,滑動頁面滾動條

1.滾動頁面底部 js="var q=document.getElementById('id').scrollTop=10000" driver.execute_script(js)     2.滾到頁面頂部    js="var q=document.

系統休眠或瀏覽器是當前活動狀態,執行js解決思路

color pan 瀏覽器 原生開發 可用 繼續 gettime != inter 手機端原生開發,當前頁面從系統中被喚起會有相應的事件觸發。但webapp確無法獲取。 基於瀏覽器自身規則,在系統休眠或著瀏覽器不處於當前活動狀態是,js是不執行的。 那麽如果想讓頁面能及時更

回車鍵 執行js程式碼 {鍵盤事件}

onkeypress=function(event){ if(event.keyCode == 13){ $('#btn').click(); } }; 完整 程式碼 <!doctype html>

firefox瀏覽器下href執行js程式碼

firefox瀏覽器裡,標籤a的href執行js程式碼時不能執行臺複雜的語句,只能執行單語句程式碼。即href="javascript: func1();func2();",此時可能會出錯。 href="javascript:funct1()"可以正確執行。如果非要執行復雜的

java執行bat程式碼

java執行bat程式碼.txt public static void runbat(String path,String filename) { String cmd = "cmd /c start"+path+"/"+filename; //String cmd = "cmd

怎麼在chrome的位址列中執行js程式碼

crtl+shift+j chorme自帶的開發人員工具  console+log可以直接輸出指令碼程式碼 javascript:alert("js")或者confirm("js");可以直接打印出

建立並執行Java執行程式碼的三種方式

1 概述 在Java中,建立執行緒執行時程式碼有三種方式。 第一種:繼承Thread類,覆寫其run方法,這種方式我們在之間的案例中已經見過。 第二種:實現Runnable介面,實現run方法,Thread類也實現了Runable介面。 第三種:實現Call

幾種自動執行js程式碼的方式

最近在看jquery,發現他居然能自動執行js程式碼,於是就查了下,收集了幾種常用的實現方法 jquery的方法 使用場景:任何需要執行的js特效 $(document).ready(fu

Selenium2(WebDriver)中執行JavaScript程式碼

在用selenium編寫web頁面的自動化測試程式碼時,可能需要執行一些JavaScript程式碼,selenium本身就支援執行js,我們在程式碼中可以使用executeScript、executeAsyncScript這兩個方法來執行JS。 exec

JS_實現頁面載入完再執行JS程式碼

1 在body中用onload: <body onload="myfunction()"> 2 在指令碼中用window.onload: <script type="text/javascript"> function myfun() { alert("this window

python+selenium對網頁執行js指令碼報錯“$ is not defined”

背景 在python裡用selenium模擬瀏覽器的時候需要傳送一個POST請求,我用的是webdriver的execute_script方法,對頁面執行下面的js程式碼來獲取資料 $.post(........) 然後執行的時候報錯 “$ is no

javac編譯成功,用java執行class檔案出現“找到或無法載入主類” 的問題解決起來很簡單

avac編譯成功,用java執行class檔案出現“找不到或無法載入主類” 的問題所在很簡單    學習android,順便又學習下java.    入門就遇到這樣的問題,環境變數按網上說的配好了,直接java 和 javac都有提示出來,說明沒問題了, 做了一個簡

原生JS實現DOM載入完成馬上執行JS程式碼

用原生JS我們經常使用window.onload事件來載入頁面。但是window.onload是在頁面元素都載入完畢後才執行,如果頁面內有大的圖片的話,會在頁面展現後好久時間後才執行。所以有時我們需要在DOM載入時馬上執行一些函式。jQuery提供了document.rea

WebView 無法執行js程式碼

下午在研究webView 與js之間的呼叫,於是到w3school上找了一個html的頁面。 html頁面: <html> <head> <script type="text/javascript"> function show

17-Python執行JS程式碼--PyExecJS、PyV8、Js2Py

一、Python執行JS程式碼--PyExecJS、PyV8、Js2Py 1.1、PyExecJS   PyExecJS的優點是您不需要照顧JavaScript環境。特別是,它可以在Windows環境中執行,而無需安裝額外的庫。PyExecJS的缺點之一是效能。PyExecJS通過文字傳達JavaScript

win10配置java環境變數,解決javac是內部或外部命令等問題

昨天重灌了win10系統,發現以前配好的java環境變數和tomcat環境變數全都清空了,在重新配置的時候總是出現問題,即在cmd命令視窗下,輸入java,顯示正常,輸入java -version 也是顯示正常,唯獨輸入javac,顯示“javac不是內部或外部命令,布拉布拉

解決laravel執行資料庫遷移檔案修改支援enum型別的方法

報錯如下: In AbstractPlatform.php line 423: Unknown database type enum requested, Doctrine\DBAL\Platforms\MySQL57Platform may not support it.