鬼谷子论坛一波中特|一波中特图库 ?
實驗:利用Java+JSoup實現頁面資源的下載
作者:強官濤   類型:Java開發    類別:實驗   日期:2018-12-13    閱讀:1317 次   消耗積分:0 分

實驗簡介



在進行性能測試的時候,有一個問題必須引起我們的重視,那就是瀏覽器渲染頁面的過程是需要有很多輔助資源的支撐的。

簡單理解就是瀏覽器不可能單純靠一個HTML源文件就可以將頁面渲染出來,一個HTML頁面通常是由多個資源文件構成的。

比如常見的資源文件通常有圖片,CSS樣式文件,JavaScript文件等,所以為了模擬真實的用戶使用場景,我們也需要對頁面中的所有資源文件進行下載處理,這樣才是一個完整的請求,統計到的響應時間才是更為準確的。


在上一實驗中,我們統計到的請求的響應時間,其實是存在嚴重問題的,因為這只是針對一個HTML響應的持續時間,但是一個頁面中通常不止一個請求(JSON數據除外)。就像我們在使用協議監控工具監控一個頁面的請求時一樣,我們可以看到,雖然用戶只提供了一個網址或者點擊了一次提交按鈕,但是請求數量卻是非常多的。


本實驗主要為大家介紹如何利用Java原生代碼及JSoup完成對頁面資源文件的下載,以及針對一個頁面的所有請求,統計出相對準確的響應時間及響應的內容大小。


 

實驗目的



1.理解性能測試的真實場景設計并進行應用。

2.熟練運用Java完成頁面資源的獲取與下載。

3.熟練運用JSoup完成頁面資源的解析與獲取。

 


實驗流程



1.利用Java獲取頁面資源。


在接口測試部分,我們已經理解了如何利用正則表達式完成頁面資源的URL地址的獲取。在性能測試的腳本開發過程中,這也是一個必不可少的部分,否則我們統計到的性能測試數據將會與實際的用戶體驗有較大的差距。我們先來看看,當我們在URL地址欄輸入一個URL地址后,整個過程發生了什么。


(1).首先將整個頁面的HTML響應內容下載回來。

(2).解析整個HTML頁面中的資源文件。

(3).通過解析獲取到整個頁面中的圖片資源。

(4).繼續解析獲取整個頁面中的CSS資源文件。

(5).利用CSS資源文件結合HTML標記內容對整個頁面進行渲染。

(6).繼續解析整個頁面中的JavaScript文件,并獲取到其內容。

(7).如果頁面中還有其它資源文件,如Flash,視頻,音頻等,繼續下載處理。

(8).通常針對Flash,視頻,音頻等額外的資源文件,我們可以進行額外的處理。

 

那么,如何利用Java結合正則表達式針對Phpwind的首頁進行頁面資源的URL地址的獲取呢?基本思路就是根據每一種資源文件在響應中的相對固定的特征,通過設置左右邊界的方式,獲取到某個資源的URL地址,并對其進行組裝處理,變為一個完整的以http://開頭的絕對URL地址。

代碼如下:


// 設置類成員變量

private HttpRequestor hr = new HttpRequestor();

private String baseUrl = "http://localhost/phpwind/";

private FileUpDownload updown = new FileUpDownload();

 

// 獲取所有CSS資源

public List<String> getAllCss(String response) {

List<String> list = hr.findListData(response, "text/css\" href=\"", "\"");

List<String> urls = new ArrayList<String>();

for (int i=0; i<list.size(); i++) {

if (!list.get(i).startsWith("http://")) {

urls.add("http://localhost/phpwin/" + list.get(i));

}

else {

urls.add(list.get(i));

}

}

return urls;

}

 

// 獲取所有圖片資源

public List<String> getAllImage(String response) {

List<String> list = hr.findListData(response, "img src=\"", "\"");

List<String> urls = new ArrayList<String>();

for (int i=0; i<list.size(); i++) {

if (!list.get(i).startsWith("http://")) {

urls.add(baseUrl + list.get(i));

}

else {

urls.add(list.get(i));

}

}

return urls;

}

 

// 獲取所有JS資源

public List<String> getAllJs(String response) {

List<String> list = hr.findListData(response, "JavaScript\" src=\"", "\"");

List<String> urls = new ArrayList<String>();

for (int i=0; i<list.size(); i++) {

if (!list.get(i).startsWith("http://")) {

urls.add(baseUrl + list.get(i));

}

else {

urls.add(list.get(i));

}

}

return urls;

}

 

當我們獲取到頁面的所有資源文件后,我們需要對其進行下載。此時通常有兩種處理方式,一種是直接保存到內存中,這是一種無緩存的方式,下一次訪問同樣繼續進行一次下載操作。另外一種就是利用瀏覽器緩存,將資源文件保存到硬盤的臨時文件中,下一次只需要從硬盤直接加載即可。其實無論哪一種情況,我們都需要先將這些資源文件從服務器獲取到客戶端環境中來,無非就是是否要保存到硬盤的問題而已。


通常情況下,瀏覽器都會利用緩存機制將資源文件保存到客戶端硬盤中,而且保存到硬盤這一步驟并不會太多影響響應時間。我們在接口測試部分已經實現了文件下載的操作類FileUpDownload,直接重用即可。具體的測試代碼如下:


// 統計下載一個頁面的響應時間

public void doTest() {

long startTime = System.currentTimeMillis();

String getUrl = "http://localhost/phpwind/";

String response = hr.sendGet(getUrl);

this. downloadResources(response);

long endTime = System.currentTimeMillis();

int duration = (int)(endTime - startTime);

System.out.println(duration);

}

 

// 下載頁面中的所有資源文件

public void downloadResources(String response) {

List<String> cssList = this.getAllCss(response);

List<String> jsList = this.getAllJs(response);

List<String> imgList = this.getAllImage(response);


for (int i=0; i<cssList.size(); i++) {

updown.doDownload(cssList.get(i), "D:\\TestUD");

}

for (int i=0; i<jsList.size(); i++) {

updown.doDownload(jsList.get(i), "D:\\TestUD");

}

for (int i=0; i<imgList.size(); i++) {

updown.doDownload(imgList.get(i), "D:\\TestUD");

}

}

 

上述代碼我們直接調用了已經實現的方法“doDownload()”,成功地從頁面響應內容中識別出了頁面資源并完成了下載。但是這里面仍然存在三個問題需要解決:


(1).頁面中可能會存在一些資源文件是解析錯誤的。比如在對Phpwind首頁的過程中,我們看到了這樣一個頁面資源“init.php?sitehash=10VgMHBFJQB1YBDFAHCQcDVFNVAFQKDAhXBgVdUQRXXAw&v= 7.3.2&c=0”,由于這個資源文件也是一個JavaScript腳本,但是卻存在兩個問題。一是本身無法下載,連接到的服務器已經失效。二是無法將該文件按原文件名在硬盤上新建文件,因為操作系統文件名命名規范的原因。雖然這種情況不一定每個系統都存在,但是通常針對我們自己的系統的性能測試開發過程中,我們需要對其進行特殊處理,比如在getAllJs()方法中將其排除。當然,另外一種解決方案是直接提交一個Bug,將其問題修復。


(2).關于資源文件的下載問題,由于我們在性能測試過程中,會模擬大量用戶并發操作。所以我們應該要為每一個用戶創建一個用于保存資源文件的單獨的目錄,這樣才是一種真實的模擬。否則,極有可能導致多個線程同時操作同一個文件夾下面的同一樣的文件,則很有可能引起資源沖突。當然,其解決方案比較簡單,我們可以利用線程名稱來創建一個子目錄。重構后的downloadResource()方法的代碼如下:


public void downloadResources(String response) {

List<String> cssList = this.getAllCss(response);

List<String> jsList = this.getAllJs(response);

List<String> imgList = this.getAllImage(response);


// 為每一個線程單獨創建一個目錄

String threadName = Thread.currentThread().getName();

String folder = "D:\\TestUD\\" + threadName;

File file = new File(folder);

if (!file.exists()) {

file.mkdirs();

}


for (int i=0; i<cssList.size(); i++) {

updown.doDownload(cssList.get(i), folder);

}

for (int i=0; i<jsList.size(); i++) {

updown.doDownload(jsList.get(i), folder);

}

for (int i=0; i<imgList.size(); i++) {

updown.doDownload(imgList.get(i), folder);

}

}

 

(3).最后一個,也是非常重要的一個問題,必須引起我們足夠的重視。由于通常情況下,由于瀏覽器緩存的原因,當用戶第一次訪問了某一個頁面后,資源文件會被保存到本地硬盤。但是下一次用戶再訪問同一個頁面時,瀏覽器將優先讀取本地硬盤的資源,如果存在,則直接從本地硬盤獲取資源進行頁面渲染,而不需要再次發送請求給服務器獲取。這個過程將節省大量的服務器處理資源和帶寬占用。所以我們在性能測試的執行過程中,也必須嚴格模擬這個過程,否則測試數據也將失去其準確性。思路清楚了,解決方案其實相對簡單,就是每一個線程的每一次運行,我們都對其目錄下的文件進行一次判斷,如果存在,則可以直接跳過下載這一步。我們可以對FileUpDoenload類的代碼重構如下:


public void doDownload(String getUrl, String folder) {

try {

URL url = new URL(getUrl);

HttpURLConnection urlConnection =

(HttpURLConnection) url.openConnection();

 

// 本次連接相關的參數設置

urlConnection.setConnectTimeout(30000);

urlConnection.setReadTimeout(30000);

urlConnection.setUseCaches(true);

urlConnection.setRequestMethod("GET");


// 建立與服務器的連接

urlConnection.connect();


// 通過URL地址中的最后一個"/"作為分隔符,分離出原始文件名,此處可自定義文件名

int posLast = getUrl.lastIndexOf("/") + 1;

String fileName = getUrl.substring(posLast);


// 定義一個文件輸出流,用于將下載的文件保存到硬盤

File outfile = new File(folder + "\\" + fileName);


// 模擬瀏覽器緩存,如果文件已經存在,則跳過下載這一步

if (outfile.exists()) {

return;

}


OutputStream os = new FileOutputStream(outfile, false);


// 定義一個輸入流,用于從服務器端獲取到該文件字節流

InputStream is = urlConnection.getInputStream();


// 新建一個字節數組,用于緩存從服務器端讀取來的內容

byte[] buf = new byte[1024];

int bufLen = 0; // 定義每一次循環讀取到的字節數組的長度

while ((bufLen = is.read(buf)) != -1) {

byte[] temp = new byte[bufLen];

// 將buf中的內容復制到temp中

System.arraycopy(buf, 0, temp, 0, bufLen);

// 將字節數組寫入到outfile中

os.write(temp);

}


// 釋放資源

urlConnection.disconnect();

os.close();

is.close();

System.out.println("文件下載完成.");

}

catch (Exception e) {

e.printStackTrace();

}

}

 

針對上述下載資源的代碼,通常情況下,我們還可以為該方法設定一個開關參數,當調用時我們可以指定是否需要模擬緩存。代碼重構的關鍵部分如下所示:


public void doDownload(String getUrl, String folder, boolean useCache) {

// 此處省略一批代碼

// 當文件存在并且指定使用緩存時,才跳過本次下載

if (outfile.exists() && useCache) {

return;

}

// 此處省略一批代碼

}

 

當然,進行了這樣的重構以后,我們在方法downloadResources()中調用doDownload()時必須額外再多加一個參數,指示其是否使用緩存。

 

2.利用JSoup獲取頁面資源。


JSoup是一個可以在內存中直接解析HTML內容的工具,提供了類似于DOM操作HTML頁面的方式直接對頁面中的各種內容進行處理,比較適用于對頁面進行更加完成的資源文件的加載和處理。

要使用JSoup,當然我們首先需要去其官網:“https://jsoup.org/”或者Maven的阿里云鏡像“http://maven.aliyun.com/nexus/#welcome”下載并導入到當前項目中即可使用。


利用JSoup獲取頁面的資源將會更加的方便,代碼如下:


public List<String> getResources(String url) {

List<String> urls = new ArrayList<String>();

try {

Document doc = Jsoup.connect(url).get();

// 獲取頁面中的所有超鏈接

Elements links = doc.select("a[href]");

//Elements pngs = doc.select("img[src$=.png]");

//Elements images = doc.getElementsByTag("img");

// 獲取頁面中的所有圖片資源

Elements images = doc.select("img[src]");

// 獲取頁面中的所有導入的資源

Elements imports = doc.select("link[href]");

for (Element e: links) {

urls.add(e.absUrl("href"));

}

for (Element e: images) {

urls.add(e.absUrl("src"));

}

for (Element e: imports) {

urls.add(e.absUrl("href"));

}

} catch (IOException e) {

e.printStackTrace();

}

return urls;

}

 

上述的代碼可以將頁面中常見的資源的絕對URL地址全部獲取到,剩下的資源下載的部分直接利用前面的Java代碼即可,JSoup定位元素的方式與JavaScript操作頁面元素或者是Selenium定位頁面元素的方式非常類似。關于JSoup操作HTML頁面響應的具體使用方法,各位讀者可以直接參考網址:“http://www.open-open.com/jsoup/”。


當我們獲取到頁面的所有資源進行下載后,整個過程才是相對完整地模擬了性能測試過程。其實,對于頁面資源文件的處理,一方面是為了模擬真實的瀏覽器訪問情況,統計到相對準確的響應時間。更重要的一方面也是為了讓服務器承擔正常的負載,包括處理靜態資源的負載,而不是單純處理一些最基本的GET和POST請求。這個方面便是與基于接口的功能測試不太一樣的地方,請各位讀者一定引起重視。

 

思考練習



1.請使用原生Java的正則表達式或JSoup完成對Phpwind的其它頁面進行資源解析和下載。

2.請對截止目前的關于接口測試和性能測試的代碼進行重構,保持結構上的簡潔。








為了答謝大家對蝸牛學院的支持,蝸牛學院將會定期對大家免費發放干貨,敬請關注蝸牛學院的官方微信。


20181009_153045_341.jpg




版權所有,轉載本站文章請注明出處:蝸牛學院在線課堂, http://www.gpkmad.shop/note/247
上一篇: 資訊:Python 網絡爬蟲實戰直播課,限額30人,先到先得~
下一篇: 資訊:雙十二為了防止大家“剁手”,西安校區竟……
提示:登錄后添加有效評論可享受積分哦!
鬼谷子论坛一波中特 炒股群 牛市快讯每天推送 pk10精准高手计 常来海南麻将黑3黑a 期货配资 北京小赛车 11选5一定牛江苏 苹果在哪里下熊猫麻 股票分析微信 证券分析师有用吗 广东11选5今天开