前言
一直以來,對爬蟲技術都十分嚮往,雖然是學Java出身,但是很少有編碼的機會,因爲熱愛,想認真學習一下這方面的技術,故用此係列文章來記錄自己的學習過程。一方面是提升自己的學習效果,另一方面希望能對同樣想學習爬蟲技術的同學能提供一些小小的幫助。
一、HTTP基礎知識
(一)關於URL
URL的全稱爲 Universal Resource Locator,即統一資源定位符,也就是我們常說的網址。
URL由三部分組成:資源類型、存放資源的主機域名、資源文件名,也可認爲由4部分組成:協議、主機、端口、路徑。
URL的一般語法格式爲:(帶方括號[]的爲可選項):
protocol:// hostname[:port] / path / [;parameters][?query]#fragment,例如:https://www.baidu.com/index.php
(二)關於URI
URI的全稱是統一資源標識符(Uniform Resource Identifier),是一個用於標識某一互聯網資源名稱的字符串。
該種標識允許用戶對任何(包括本地和互聯網)的資源通過特定的協議進行交互操作。URI由包括確定語法和相關協議的方案所定義。Web上可用的每種資源 -HTML文檔、圖像、視頻片段、程序等 - 由一個通用資源標識符(Uniform Resource Identifier, 簡稱"URI")進行定位。
URI的語法格式:[協議名] : //[用戶名]:[密碼]@[服務器地址]:[服務器端口號]/[路徑]?[查詢字符串]#[片段ID]
URL 是 URI 的子集,也就是說每個 URL 都是 URI,但不是每個 URI 都是 URL。URI 還包括一個子類叫作 URN,它的全稱爲 Universal Resource Name,即統一資源名稱。 URN當前的使用很少,我們暫可以理解爲一般的網址都是URL。
(三)關於HTML
HTML稱爲超文本標記語言,是一種標識性的語言。它包括一系列標籤,通過這些標籤可以將網絡上的文檔格式統一,使分散的Internet資源連接爲一個邏輯整體。HTML文本是由HTML命令組成的描述性文本,HTML命令可以說明文字,圖形、動畫、聲音、表格、鏈接等。當我們瀏覽一個網頁時,按“F12”或者右鍵檢查元素時,看到的網頁源碼就是HTML,如圖所示。
(四)HTTP和HTTPS的區別
HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是因特網上應用最爲廣泛的一種網絡傳輸協議,所有的WWW文件都必須遵守這個標準。HTTP是一個基於TCP/IP通信協議來傳遞數據(HTML 文件, 圖片文件, 查詢結果等)。
HTTPS (全稱:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全爲目標的 HTTP 通道,在HTTP的基礎上通過傳輸加密和身份認證保證了傳輸過程的安全性 。HTTPS 在HTTP 的基礎下加入SSL 層,HTTPS 的安全基礎是 SSL,因此加密的詳細內容就需要 SSL。 HTTPS 存在不同於 HTTP 的默認端口及一個加密/身份驗證層(在 HTTP與 TCP 之間)。
可以理解爲HTTPS是HTTP的安全版,不過安全性不是絕對的,且使用HTTPS會稍微影響傳輸效率,不過HTTPS依然是當前各大應用的趨勢。
(五)HTTP請求過程
作爲基礎知識,學習爬蟲的同學一定要熟悉HTTP的整個請求過程,以在瀏覽器輸入某個網址爲例,說明一下之後發生了哪些事情。
(1)對網址進行DNS域名解析,得到對應的IP地址;
(2)根據這個IP,找到對應的服務器,發起TCP的”三次握手“;
(3)建立TCP連接後,瀏覽器向服務器發起HTTP請求命令和請求頭信息;
(4)服務器響應HTTP請求,服務器進行應答,併發送應答頭信息和html代碼信息;
(5)瀏覽器解析html代碼,並繼續請求 html 代碼中的資源信息(如js、css、圖片等);
(6)瀏覽器對頁面進行渲染,最後將渲染結果呈現給用戶;
(7)服務器關閉關閉TCP連接。
這中間涉及了很多知識,比如:域名是如何解析的?什麼是”三次握手“?頁面是怎麼渲染的?TCP的相關知識。
這裏用兩種方式簡單說明一下什麼是 “三次握手”:
標準解釋:
所謂的三次握手即TCP連接的建立。這個連接必須是一方主動打開,另一方被動打開的。握手之前主動打開連接的客戶端結束CLOSED階段,被動打開的服務器端也結束CLOSED階段,並進入LISTEN階段。隨後開始“三次握手”:
(1)首先客戶端向服務器端發送一段TCP報文,其中:
- 標記位爲SYN,表示“請求建立新連接”;
- 序號爲Seq=X(X一般爲1);
- 隨後客戶端進入SYN-SENT階段。
(2)服務器端接收到來自客戶端的TCP報文之後,結束LISTEN階段。並返回一段TCP報文,其中:
- 標誌位爲SYN和ACK,表示“確認客戶端的報文Seq序號有效,服務器能正常接收客戶端發送的數據,並同意創建新連接”(即告訴客戶端,服務器收到了你的數據);
- 序號爲Seq=y;
- 確認號爲Ack=x+1,表示收到客戶端的序號Seq並將其值加1作爲自己確認號Ack的值;
- 隨後服務器端進入SYN-RCVD階段。
(3)客戶端接收到來自服務器端的確認收到數據的TCP報文之後,明確了從客戶端到服務器的數據傳輸是正常的,結束SYN-
- SENT階段。並返回最後一段TCP報文。其中:
- 標誌位爲ACK,表示“確認收到服務器端同意連接的信號”(即告訴服務器,我知道你收到我發的數據了);
- 序號爲Seq=x+1,表示收到服務器端的確認號Ack,並將其值作爲自己的序號值;
- 確認號爲Ack=y+1,表示收到服務器端序號Seq,並將其值加1作爲自己的確認號Ack的值; 隨後客戶端進入ESTABLISHED階段。
服務器收到來自客戶端的“確認收到服務器數據”的TCP報文之後,明確了從服務器到客戶端的數據傳輸是正常的。結束SYN-SENT階段,進入ESTABLISHED階段。
在客戶端與服務器端傳輸的TCP報文中,雙方的確認號Ack和序號Seq的值,都是在彼此Ack和Seq值的基礎上進行計算的,這樣做保證了TCP報文傳輸的連貫性。一旦出現某一方發出的TCP報文丟失,便無法繼續"握手",以此確保了"三次握手"的順利完成。
此後客戶端和服務器端進行正常的數據傳輸。這就是“三次握手”的過程。
通俗理解:
人們常用男女交往來舉例說明“三次握手”,方便理解:
第一次:男方發消息告白女方:我喜歡你;
第二次:女方回覆男方:我也喜歡你;
第三次:男方回覆女方:我知道你喜歡我了,那我們交往吧。
之後的交往過程就是後面的數據傳輸了。
(六)瀏覽器中“F12”的相關知識
不管是學習爬蟲還是前後端開發,學會使用瀏覽器的開發者模式是必不可少的,這個知識往往被一些入門者給忽視了,這裏我詳細說明一下,這在後面的學習過程中很重要。
首先,不管什麼瀏覽器,通過 F12 或者 右鍵中的 檢查元素 都可以進入開發者頁面。點擊 Network 或者 “網絡” 查看頁面請求情況,火狐瀏覽器打開效果如下:
不同瀏覽器打開的效果都不一樣,這裏簡單說一下各列的含義,各位可以對照自己的頁面進行驗證,以上圖爲例:
- 第一列:響應的狀態碼,這裏顯示爲 200,代表響應是正常的。通過狀態碼,我們可以判斷髮送了請求之後是否得到了正常的響應。
- 第二列:請求方法。常見的請求方法有兩種:GET 和 POST。
- 第三列:請求的網站。
- 第四列:請求的資源詳細資源信息。
- 第五列:觸發原因。
- 第六列:返回結果類型。
- 第七列:傳輸過程。
- 第八列:返回結果大小。
- 第九列:後面的都是響應時間,從上到下爲先後順序。
當我們點擊某一個請求時,會看到更詳細的信息,如下圖所示:
簡單說明一下,右邊部分從上到下分別是:
- 請求網址(URL)、請求方法、響應狀態碼,有的會顯示請求的IP和端口;
- 響應頭:包含了服務器的類型、文檔類型、日期等信息;
- 請求頭:包含了瀏覽器標識、Cookies、Host 等信息。
(七)內容補充
(一)常見請求方法
(二)響應狀態碼對照表
(三)Content-Type 和 POST 提交數據方式的關係
二、網頁基礎知識
(一)網頁的組成
我們常見的網頁主要由三大部分組成:HTML、CSS 和 JavaScript,一個簡化的網頁內容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>測試頁面</title>
<!-- 引入css文件 -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div class="split left">
<h1>Hello Word</h1>
<a href="#" class="button">Read More</a>
</div>
</div>
<!-- 引入js文件 -->
<script src="./main.js"></script>
</body>
</html>
(二)HTML
我們在網頁中看到的文字、圖片、視頻、按鈕等元素都是通過HTML中不同的標籤來實現的,比如是:< p >標籤、< img >標籤、< video >標籤、< div >標籤、< a >標籤等。那些複雜的結構是通過不同的標籤相互嵌套排列來實現的。這些標籤是整個網站的框架。百度首頁的源代碼效果如下圖所示,HTML即爲下方左邊的部分:
(三)CSS
HTML的內容決定了網頁的架構,但是隻有架構的網頁看起來十分簡陋且不美觀,爲了讓網頁看起來更好看,我們就會用到CSS。CSS,全稱叫作 Cascading Style Sheets,即層疊樣式表。上圖下方中間和後面的內容就是CSS。
在學習CSS時我們需要知道,CCS是如何來定位生效節點的。
(1)節點的 id 爲 logo,可以通過 #logo 來定位標籤;
(2)節點的 class name ,可以通過 .name 來定位標籤;
(3)節點類型爲 p 標籤,可直接通過 p 來定位標籤。
同時,CSS 選擇器還支持嵌套選擇,各個選擇器之間加上空格分隔開便可以代表嵌套關係,如 #logo.name p 則代表先選擇 id 爲 logo 的節點,然後選中其內部的 class 爲 name的節點,然後再進一步選中其內部的 p 節點。這就是 CSS 選擇器,其篩選功能十分強大的。
以上只是最基礎的功能,還有其他更復雜的選擇器功能值得大家去了解。
(四)JavaScript
單純的HTML+CSS只會呈現一個好看的靜態頁面,爲了讓頁面動起來或者與頁面產生交互,這時候就需要JavaScript。
JavaScript,簡稱 JS,是一種腳本語言。它的出現使得用戶與信息之間不只是一種瀏覽與顯示的關係,而是實現了一種實時、動態、交互的頁面功能。
綜上所述,HTML 定義了網頁的內容和結構,CSS 描述了網頁的佈局,JavaScript 定義了網頁的行爲。
(五)如何完成頁面解析
在 HTML 中,所有標籤定義的內容我們都把它當作一個節點,這些節點構成了一個 HTML DOM 樹。
通過 HTML DOM ,我們可以使用JavaScript 對各個節點進行訪問,修改,創建和刪除。爲了方便編輯,對這個樹形結構做了詳細的層級定義,用來描述節點之間的關係。
三、Session 與 Cookies
首先,瞭解 Session 與 Cookies 之前,大家需要了解 HTTP 的無狀態,也就是 HTTP 協議對事務處理是沒有記憶能力的。也就是說客戶端和服務器端的交互是單次的,第二次交互不會與第一次交互結果產生關聯。舉個例子:無狀態情況下,你第一次登陸成功一個網站,這是刷新頁面,又會被要求重新登陸,因爲服務器不知道你已經登錄過了。
爲了解決這一問題,這種用於保持 HTTP 連接狀態的技術就出現了,它們分別是 Session 和 Cookies。
Session 在服務端,也就是網站的服務器,用來保存用戶的 Session 信息。
Cookies 在客戶端,也可以理解爲瀏覽器端,有了 Cookies,瀏覽器在下次訪問網頁時會自動附帶上它發送給服務器,服務器通過識別 Cookies 並鑑定出是哪個用戶,然後再判斷用戶是否是登錄狀態,進而返回對應的響應。
在爬蟲中,有的頁面需要登錄才能訪問時,我們一般會直接將登錄成功後獲取的 Cookies 放在請求頭裏面直接請求,而不必重新模擬登錄。
Session 知識點
Session ,簡稱爲會話,是一段有始有終的動作或消息。就和打電話一樣,從開始撥號到掛斷電話,這是一個完整的過程,像這樣的一個過程我們可以稱之爲一個 Session 。
Session 對象中儲存着用戶的信息,當用戶請求在頁面間跳轉時,存儲在Session 中的信息不會消失,會被用於下一個被請求的頁面,或者在緩存中等待被使用。用戶第一次請求時,服務其中沒有該用戶的 Session ,服務器會自動創建一個 Session 對象。Session 不會自己消失,只有在過期或者被放棄後,纔會被服務器終止。
Session 與 Cookies 對接的過程:
客戶端第一次請求時,服務器會返回一個響應頭中帶有 Set-Cookie 字段的響應給客戶端,用來標記是哪一個用戶,客戶端瀏覽器會把 Cookies 保存起來。當瀏覽器下一次再請求該網站時,瀏覽器會把此 Cookies 放到請求頭一起提交給服務器,Cookies 攜帶了 Session ID 信息,服務器檢查該 Cookies 即可找到對應的 Session 是什麼,然後再判斷 Session 來以此來辨認用戶狀態。
在成功登錄某個網站時,服務器會告訴客戶端設置哪些 Cookies 信息,在後續訪問頁面時客戶端會把 Cookies 發送給服務器,服務器再找到對應的 Session 加以判斷。如果 Session 中的某些設置登錄狀態的變量是有效的,那就證明用戶處於登錄狀態,此時返回登錄之後纔可以查看的網頁內容,瀏覽器再進行解析便可以看到了。
反之,如果傳給服務器的 Cookies 是無效的,或者 Session 已經過期了,我們將不能繼續訪問頁面,此時可能會收到錯誤的響應或者跳轉到登錄頁面重新登錄。
所以,Cookies 和 Session 需要配合,一個處於客戶端,一個處於服務端,二者共同協作,就實現了登錄 Session 控制。
Cookies 知識點
Cookies 是某些網站爲了辨別用戶身份,進行Session跟蹤而儲存在用戶本地終端上的數據(通常經過加密),由用戶客戶端計算機暫時或永久保存的信息。
會話 Cookie :就是把Cookie 放在瀏覽器內存裏,瀏覽器在關閉之後該 Cookie 即失效;
持久 Cookie: 就是把Cookie保存到客戶端的硬盤中,下次還可以繼續使用,用於長久保持用戶登錄狀態。
其實嚴格來說,沒有會話 Cookie 和持久 Cookie 之 分,只是由 Cookie 的 Max Age 或 Expires 字段決定了過期的時間。因此,一些持久化登錄的網站其實就是把 Cookie 的有效時間和 Session 有效期設置得比較長,下次我們再訪問頁面時仍然攜帶之前的 Cookie,就可以直接保持登錄狀態。
這裏有一個知識點需要理解: 當我們關閉瀏覽器與服務器端斷開連接時,並不意味着服務器端的 Session 消失了。因爲你關閉客戶端的操作,服務器端是不知道的。如果你的 Cookie 沒有被瀏覽器保存到硬盤上,那麼你下次登錄的時候不能憑藉之前的Cookie 找到對應的 Session ,所以網站會要求你重新登陸。如果Cookie 保存到了硬盤上,那麼下次訪問帶上這個Cookie 去訪問就可以找到原來的 Session ,保持登錄狀態了。
所以爲 Session 設置一個過期時間就十分重要了,超過這個時間就終止這個 Session 的活動,這樣可以節省服務器資源。
通過下圖看看 Cookies 中包含了那些信息,因爲涉及個人賬戶信息所以部分內容碼了一下:
可以看到,這個請求的 Cookies 大致包含了這些內容:頁面用戶信息,本次訪問的相關的網頁信息,Cookies 本身相關的一些信息。 當前不同的瀏覽器看到的信息內容結構是不一樣的,不過主要內容是差不多的。
四、多線程
爬蟲在使用的過程中,不可避免的會用到多線程技術,這裏會用 Python 中的技術來進行演示,講解多線程的知識和使用場景。
首先簡單瞭解一下什麼是 “多進程、多線程” :我們在打開一臺計算機時,可以同時運行多個程序,這就是多進程。有的程序工作量比較大,會使用多個線程來處理,這就是多線程。進程就是由一個或多個線程構成的,線程是操作系統進行運算調度的最小單位,是進程中的一個最小運行單元。
舉例來說,當你打開一個瀏覽器,就是在運行一個瀏覽器的進程,你使用這個瀏覽器打開多個標籤頁進行瀏覽就是靠多個線程在運行。
併發
併發(concurrency),指單個處理器上同一時刻只能有一條指令執行,但是多個線程的對應的指令被快速輪換地執行。
並行
並行(parallel),指同一時刻,有多條指令在多個處理器上同時執行,並行必須要依賴於多個處理器。
處理器運行機制:就單個處理器來說,在某個時間點只能運行一個處理命令,然後快速切換其他處理命令,因爲這個運算和切換過程十分迅速,所以用戶在使用時根本感受不到停頓,給人處理器在同時處理多個任務的感覺。
多線程適用場景
在命令執行過程中,有的命令有一定的相應時間,需要等待一段時間才能進行下一步操作,這時候就可以使用多線程來利用這個等待的時間,增加命令的執行效率。
命令的執行場景根據類型主要分爲:
1)IO 密集型任務,需要等待服務器響應,適合用多線程;
2)計算密集型任務,需要處理器一直運行,不適合用多線程。
使用多線程需要注意的問題
1)管理線程的關閉順序問題;
2)守護線程的使用場景;
3)互斥鎖的使用場景。
五、多進程
由於Python 進程中 GIL 的存在,Python 中的多線程並不能很好地發揮多核優勢,一個進程中的多個線程,在同一時刻只能有一個線程運行。而對於多進程來說,每個進程都有屬於自己的 GIL,所以,在多核處理器下,多進程的運行是不會受 GIL 的影響的。因此,多進程可以更好地發揮多核的優勢。
多進程的使用要點較多,準備單獨開一文進行闡述,包括多線程的一些場景使用。這裏先羅列一下多進程中會遇到的問題:
(1)如何創建進程;
(2)如何使用守護進程;
(3)進程的等待和終止;
(4)進程互斥鎖的使用;
(5)進程中信號量的問題;
(6)隊列;
(7)管道;
(8)進程池。
六、爬蟲的基本流程
首先,什麼是爬蟲?其實爬蟲就是獲取網頁並提取和保存信息的自動化程序。
(一)獲取網頁
本次學習主要是使用python語言,python中提供了很多庫來幫助我們獲取頁面信息,如:requests、urllib等。通過這些庫我們獲取到的是未經處理的頁面信息,我們需要按照自己的需求進行結構解析,獲取自己想要的部分。
(二)提取信息
因爲頁面結構的規則性,我們可以通過一些庫快速的提取頁面信息,如節點信息,文本內容,一些數值等。這個過程需要一定的分析能力,需要從複雜的頁面結構中,獲取到有價值的信息,且能使用效率最高的方式來實現。
(三)保存信息
我們從頁面獲取到的大量數據,我們可以按照用多種形式來保存。如:可以保存爲txt文本、json文本或者存儲到數據庫中。甚至可以直接存儲到遠端的服務器中。
(四)自動化運行
爬取任務往往是複雜的、大量的,我們需要編寫一些自動化腳本或者通過一些自動化工具來實現抓取過程的自動化、異常處理、錯誤重試等問題。
(五)其他問題
現在網站的前端框架越來越多樣,很多網站的內容都是經過JavaScript 渲染出來的,我們直接讀取原頁面的代碼往往是什麼都沒有的,這時候,我們可以通過 Selenium、Splash 這樣的庫來實現模擬 JavaScript 渲染。
七、簡單爬蟲實戰
這裏我簡單做了一個讀取CSDN網站某篇文章閱讀量的小爬蟲,十分簡單,可以先了解一下,如果有什麼不對的地方,歡迎指正。
import re
import requests
def main():
url="https://blog.csdn.net/m0_46422300/article/details/105756002"
read_num = link_crwael(url)
print('此文章閱讀量:' + read_num)
def get_span(html):
web_regex=re.compile(r"<span class=\"read-count\">[0-9]*</span>")
return web_regex.findall(html)
def get_num(list):
num_regex=re.compile(r"[0-9]+")
s=""
for i in list:
s+=str(i)
return num_regex.findall(s)
def link_crwael(start_url):
html = download(start_url)
temp = get_num(get_span(html))
read_num = str(temp[0])
return read_num
def download(url):
print(f"訪問的鏈接是:{url}")
resp=requests.get(url)
if resp.status_code==200:
return resp.text
if __name__ == '__main__':
main()
執行結果:
訪問的鏈接是 : https://blog.csdn.net/m0_46422300/article/details/105756002
此文章閱讀量:842
八、下期預告
下一篇文章,準備編寫一些python爬蟲常用庫的使用方式,並進行項目實戰,盡情期待。