- Python 3反爬蟲原理與繞過實戰
- 韋世東
- 3563字
- 2021-02-07 09:17:46
2.2 瀏覽器
網頁是一個包含HTML標簽的純文本文件,也是構成Web應用的元素之一。除了HTML文件外,網頁中還包含JavaScript、CSS、圖片和其他媒體等文件。我們最常用的客戶端就是瀏覽器,它幫助我們發起網絡請求,并將服務器端返回的資源渲染成錯落有致的頁面。
爬蟲程序可以模擬瀏覽器向服務器端發起網絡請求,它們得到的資源與正常訪問服務器端得到的資源是相同的,但顯示的內容卻不同。這是因為瀏覽器具有解釋HTML、JavaScript和CSS的能力,而爬蟲程序不具備這些能力,這個差異造成爬蟲程序無法做到“所見即所得”。很多反爬蟲手段利用了瀏覽器和爬蟲程序之間的差異,因此要想深入理解反爬蟲,我們必須了解瀏覽器的相關知識。
2.2.1 瀏覽器的主要結構
瀏覽器的主要組件如圖2-7所示。

圖2-7 瀏覽器的主要組件
? 用戶界面:包括地址欄、前進/后退/刷新等按鈕、頁面主窗口等。
? 瀏覽器引擎:負責將用戶的操作傳遞給對應的渲染引擎。
? 渲染引擎:能夠調用解釋器解釋HTML、CSS和JavaScript代碼,然后根據解釋結果重排頁面并繪制渲染樹。
? HTML解釋器:解釋HTML代碼。
? JavaScript解釋器:解釋JavaScript代碼。
? CSS解釋器:解釋CSS代碼。
? 界面后端:繪制組合框和窗口等基本部件。
? 數據存儲:在本地存儲一些體積較小的數據,如Cookie、Storage對象等。
? 網絡:自動加載HTML文檔中所需的其他資源。
HTML、JavaScript和CSS的代碼都需要解釋器才能運行。瀏覽器之所以能夠將HTML文本變成內容豐富的網頁,就是因為內置了對應的解釋器,否則這些代碼只能作為文本出現。
2.2.2 頁面渲染
頁面渲染是瀏覽器特有的功能,也是爬蟲程序無法做到“所見即所得”的重要原因。頁面渲染是將資源從文本變成網頁的過程,如圖2-8所示。

圖2-8 頁面渲染流程
首先,渲染引擎會解析HTML文檔并將其轉換為DOM節點,同時解析外部CSS文件和頁面標簽中的樣式代碼。CSS和DOM共同組成渲染樹,其中包含多個帶有視覺屬性(例如顏色、大小)的矩形。在渲染樹構建完成之后,就會進入布局的過程,此時每個節點都會確定在瀏覽器中的具體位置。然后進入繪制階段,此時會遍歷渲染樹并繪制每一個節點,繪制的結果最終會顯示在屏幕上。
瀏覽器的工作流程如圖2-9所示。

圖2-9 瀏覽器的工作流程
要注意的是,在這個過程中,有很多工作可以同時進行,比如HTML文檔的解析和CSS的解析,而且解析工作和網絡請求也有可能同時進行。我們也可以理解為渲染工作和資源加載工作是異步進行的,這種異步的工作方式使得瀏覽器顯示內容的速度變得更快。
CSS樣式和JavaScript都作用于HTML,二者之間的不同是CSS樣式只是修飾HTML,而JavaScript可以通過可編程的DOM改變頁面顯示的內容。我們可以通過一個例子來理解這段話,HTML文檔的代碼如下:
<!DOCTYPE html> <html> <head> <meta charset="utf8"></meta> </head> <body> <h2 class="title">華為消費者業務簡介</h2> <p> <span class="colors">華為消費者業務</span>產品全面覆蓋手機、移動寬帶終端、終端云等 </p> <p>憑借自身的全球化網絡優勢、全球化運營能力,致力于將最新的科技帶給消費者</p> <p>讓世界各地享受到技術進步的喜悅,以行踐言,實現夢想</p> <p id="slogen"></p> </body> </html>
這段代碼在瀏覽器中的顯示效果如圖2-10所示。

圖2-10 HTML例子
接著我們在HTML文檔中加入一些CSS代碼和JavaScript代碼:
<head> <!--增加CSS 代碼--> <style> .title {color:gray;} .colors {color:gray;font-size: 22px;} #slogen {font-size:16px;} </style> </head> <body> <!--增加JavaScript 代碼 --> <script> var slg = document.getElementById('slogen'); var text = '勇敢做自己!'; slg.innerHTML = text; </script> </body>
保存代碼后,刷新瀏覽器,就可以看到如圖2-11所示的頁面。

圖2-11 添加CSS和JavaScript后的頁面
與圖2-10相比,圖2-11中的顯示內容發生了變化。正文中的“華為消費者業務”這幾個字變大了,同時顏色也變淺了,正文最后還多出一句“勇敢做自己!”。頁面發生了變化,HTML代碼也被改變了嗎?這兩個頁面的源代碼如圖2-12所示。

圖2-12 頁面源代碼對比
可以看到網頁源代碼中HTML主體是相同的,也就是說雖然頁面顯示的內容變了,但HTML主體并沒有發生太大變化。這說明CSS和JavaScript樣式造成的頁面內容改變是在瀏覽器顯示層面的,而非直接改變HTML文本。
2.2.3 HTML DOM
假如需要動態改變頁面上的元素,實現頁面元素的添加、移除和修改,甚至是重排,那么就需要獲得能夠對HTML文檔中所有元素進行訪問的入口,這個入口就是文檔對象模型,簡稱DOM(Document Object Model)。DOM是W3C組織推薦的處理可擴展標志語言的標準編程接口。在網頁中,組織頁面或文檔的對象被放在一個樹形結構中,其中用來表示對象的標準模型就稱為DOM。DOM能夠以一種獨立于平臺和語言的方式訪問和修改一個文檔的內容和結構。它是表示和處理一個HTML或XML文檔的常用方法。DOM的設計以對象管理組織(OMG)的規約為基礎,因此可以用于任何編程語言。
實際上,DOM以面向對象的方式描述文檔模型,定義了表示和修改文檔所需的對象的名稱、對象的行為、對象的屬性和對象之間的關系。HTML文檔的每個部分都可以看作一個節點,比如下面這樣。
? 整個HTML文檔是一個文檔節點。
? 每個HTML標簽是一個元素節點。
? 包含在HTML元素中的文本是文本節點。
? 每一個HTML屬性是一個屬性節點。
? 注釋屬于注釋節點。
HTML文檔的所有節點組成了一個如圖2-13所示的節點樹。

圖2-13 節點與節點樹
節點樹起始于文檔節點,并由此伸出枝條,直到處于這棵樹最低級別的所有文本節點。節點樹中的節點可以擁有層級關系,比如父節點、子節點和兄弟節點等。父節點擁有子節點,而同級的子節點互為兄弟節點。節點樹與節點之間的關系如圖2-14所示。

圖2-14 節點樹與節點之間的關系
HTML DOM使JavaScript有能力改變HTML事件,這意味著可以在事件發生時執行JavaScript代碼,比如用戶在頁面上點擊按鈕或者頁面加載時。2.2.2節中使用JavaScript改變網頁顯示內容的例子就是在頁面加載時改變了DOM的位置,原HTML文檔的DOM樹由圖2-15變為圖2-16。

圖2-15 HTML文檔的DOM樹

圖2-16 添加CSS和JavaScript后的HTML DOM樹
HTML DOM的改變會使頁面重新布局和繪制,所以我們才會看到網頁內容的變化。
很多網站使用JavaScript來豐富頁面顯示效果,比如圖2-17中的輸入框格式驗證和點擊切換驗證碼等。

圖2-17 輸入框格式驗證
HTML DOM非常重要,它為JavaScript提供了訪問HTML中所有元素的入口,是開發者實現動態網頁的前提。
2.2.4 瀏覽器對象BOM
瀏覽器提供了一個對象模型,開發者可以通過它訪問瀏覽器的屬性或實現一些方法,這個對象模型就是瀏覽器對象模型,簡稱BOM(Browser Object Model)。BOM并沒有正式的標準,在交互性方面,由于現代瀏覽器幾乎實現了與JavaScrip相同的方法和屬性,所以這些方法和屬性被認為是BOM的方法和屬性。BOM中有很多對象,例如window、window.navigator、window.screen和window.history等。
1. window 對象
window對象表示瀏覽器窗口,所有的瀏覽器都支持它,并且所有的JavaScript全局對象、函數以及變量均自動成為該對象的成員。全局變量是該對象的屬性,全局函數則是該對象的方法。window對象的屬性如表2-6所示。
表2-6 window對象的屬性及其描述

我們可以通過window對象獲取瀏覽器的寬度和高度:
document.write(window.innerHeight ) // 打印瀏覽器窗口的內部高度 document.write("<br>") // 打印換行符 document.write(window.innerWidth )// 打印瀏覽器窗口的內部寬度 // 代碼輸出結果類似400 611
除了屬性之外,window對象還提供了一些方法,如表2-7所示。
表2-7 window對象提供的方法及其描述

如果要打開新窗口和關閉當前窗口,可以使用window.open()和window.close()方法。
2. window.navigator 對象
window.navigator對象包含訪問者瀏覽器的有關信息,沒有公開的標準,所有的瀏覽器都支持它。該對象的屬性如表2-8所示。
表2-8 window.navigator對象的屬性及其描述

要注意的是,window.navigator對象的返回值是可以被改變的。
3. window.location對象
window.location對象存儲在window對象的location屬性中,表示窗口當前顯示的文檔的Web地址,其屬性如表2-9所示。
表2-9 window.location對象的屬性及其描述

window.location對象的href屬性存放的是文檔的完整URL,其他屬性則分別描述了URL的各個部分。該對象用于表示瀏覽器當前顯示的文檔的URL(或位置),但其實它所能做的遠遠不止這些。
它還能夠控制瀏覽器顯示的文檔的位置。如果把一個含有URL的字符串賦予該對象或它的href屬性,瀏覽器就會把新的URL所指的文檔裝載并顯示出來。此外,還可以修改部分URL,此時只需要給該對象的其他屬性賦值即可。這樣做就會創建新的URL,其中的一部分與原來的URL不同,瀏覽器會將它裝載并顯示出來。例如設置了window.location對象的hash屬性,那么瀏覽器就會轉移到當前文檔中一個指定的位置。同樣,如果設置了search屬性,那么瀏覽器就會重新裝載附加了新的查詢字符串的URL。
window.location對象的reload()方法可以重新裝載當前文檔,replace()可以裝載一個新文檔而無須為它創建一個新的歷史記錄。也就是說,在瀏覽器的歷史列表中,新文檔將替換當前文檔。該對象提供的方法如表2-10所示。
表2-10 window.location對象提供的方法及其描述

下面這行代碼執行的操作與我們單擊瀏覽器的刷新按鈕時執行的操作一樣:
location.reload()
4. window.screen對象
window.screen對象存放訪問者瀏覽器的屏幕信息。JavaScript程序將利用這些信息優化它們的輸出,以達到用戶的顯示要求。該對象沒有公開的標準,但所有瀏覽器都支持它。window.screen對象的屬性如表2-11所示。
表2-11 window.screen對象的屬性及對應描述

5. window.history對象
window.history對象包含用戶在瀏覽器窗口中訪問過的URL,沒有公開的標準,但所有瀏覽器都支持它。該對象只有length一個屬性,用于返回瀏覽器歷史列表中的URL數量。
window.history對象最初用來顯示窗口的瀏覽歷史,但出于隱私方面的原因,它不再允許腳本訪問已經訪問過的實際URL。唯一保持使用的方法只有back()、forward()和go(),如表2-12所示。
表2-12 window.history對象提供的方法及對應描述

下面這行代碼所執行的操作與單擊兩次瀏覽器的后退按鈕時執行的操作一樣:
history.go(-2)
2.2.5 小結
在本節中,我們學習了瀏覽器的組成和頁面的渲染過程,并了解到JavaScript和CSS對網頁內容的改變實際上是對DOM的操作,而非直接改變HTML。除此之外,我們還可以通過瀏覽器對象獲取一些瀏覽器的信息,這些信息可以作為開發者判斷客戶端類型的依據。
- Mastering Concurrency Programming with Java 8
- Hands-On Machine Learning with scikit:learn and Scientific Python Toolkits
- Visual FoxPro程序設計教程
- Java Web基礎與實例教程(第2版·微課版)
- 深入淺出React和Redux
- Lighttpd源碼分析
- Kotlin開發教程(全2冊)
- Android嵌入式系統程序開發:基于Cortex-A8(第2版)
- Qt5 C++ GUI Programming Cookbook
- Arduino Wearable Projects
- Android Studio開發實戰:從零基礎到App上線 (移動開發叢書)
- 微信小程序開發邊做邊學(微課視頻版)
- Learning Shiny
- Python自動化開發實戰
- Go Programming Cookbook(Second Edition)