1. 程式人生 > >Python爬蟲基礎(一)——HTTP

Python爬蟲基礎(一)——HTTP

前言

  因特網聯絡的是世界各地的計算機(通過電纜),全球資訊網聯絡的是網上的各種各樣資源(通過文字超連結),如靜態的HTML檔案,動態的軟體程式······。由於全球資訊網的存在,處於因特網中的每臺計算機可以很方便地進行訊息交流、檔案資源交流······。基於因特網的幫助,我們可以在web客戶端(如瀏覽器等)通過HTTP訪問或者下載web服務端(如網站伺服器)上面的web資源。

  因特網由TCP/IP統籌,在TCP/IP的基礎上進行HTTP活動。HTTP位於TCP/IP的應用層。瞭解HTTP是為了讓爬蟲程式模擬客戶端的行為去請求伺服器資料和反爬蟲。

  通過在開發者工具裡檢視分析網頁客戶端(瀏覽器)HTTP的請求報文,獲得網頁HTTP的請求URL、請求方法、請求頭、cookie······,爬蟲程式可以完備的模擬瀏覽器去爬取網站的資源;通過檢視分析伺服器(網站)返回的HTTP響應報文,瞭解響應狀態,響應主體······,爬蟲程式就可以根據這些響應內容去實現程式邏輯、處理響應內容、提取目標資訊······

HTTP基礎

相關術語

  • Internet:因特網,一種把各個網路聯絡起來的網路,主要由許多計算機和電纜組成
  • WWW(world wide web):全球資訊網,一種抽象的資訊空間
  • web瀏覽器(web browser):可以顯示網頁伺服器或者檔案系統的HTML網頁,並讓使用者與網頁互動的軟體。
  • Web伺服器(Web Server):是駐留於因特網上某種型別計算機的程式,可以向瀏覽器等Web客戶端提供文件,也可以放置網站檔案,讓全世界瀏覽;可以放置資料檔案,讓全世界下載。一般指網站伺服器,
  • HTTP(HyperText Transfer Protocol):超文字傳輸(轉移)協議,處於TCP/IP協議簇的應用層。
  • URL(Uniform Resource Locator):統一資源定位符
  • URI(Uniform Resource Identifier):統一資源識別符號
  • URN(Universal Resource Name):統一資源名稱
  • HTML(HyperText Markup Language):超文字標記語言
  • TCP/IP(TCP/IP Protocol Suite):TCP/IP協議族,其中一種定義為網際網路相關的各類協議族的總稱
  • TCP(Transmission Control Protocol):傳輸控制協議
  • IP(Internet Protocol Address):網際協議地址
  • cookie:一種使用者識別機制,一種功能強大且高效的持久身份識別的技術
  • DNS(Domain Name System):域名系統,提供域名到IP地址的解析服務,處於TCP/IP協議簇的應用層。
  • IPv4(Internet Protocol Version 4):網際協議第四版,規定IP長度為32位
  • IPv6(Internet Protocol Version 6):網際協議第六版,規定IP長度為128位

URL

  通常我們說的網址就是一個URL,URL是URI的一個子集,URN也是URI的一個子集,URL和URN存在交集,大概率的情況都是URI=URL,關係如下: 

  當我們在web客戶端瀏覽器輸入網址(URL)的時候,如果網址無誤,通過HTTP就能得到web服務端的響應。URL語法如下:

  1. scheme:方案,訪問web伺服器時使用的協議型別,如http:或https:不區分大小寫,最後加一個冒號:
  2. user:使用者,訪問伺服器時指定使用者登入,為可選項
  3. password:使用者密碼,和使用者相連,可選
  4. host:主機,伺服器地址,可以是DNS可解釋的域名(人性化)或IPv4/IPv6地址
  5. port:埠號,指定伺服器連線的埠號,即監聽的埠號,若客戶端省略則為預設埠,HTTP預設埠為80
  6. path:路徑,指定伺服器上的檔案路徑,定位資源。由一個斜槓/與前面的URL元件分隔開
  7. params:引數,指定輸入引數,形式為鍵值對,用;將其與path的部分隔開。可選
  8. query:查詢,為查詢字串,針對已選的路徑內的資源,傳入引數,用?將其與URL其他部分隔開。可選
  9. frag:片段,為片段識別符號,通常標記出以獲取的資源的子資源,通過#與URL其它部分隔開,不會傳遞到服務端,由客戶端內部使用。可選

    瞭解這些是有用的,其中的一個用途就是在爬蟲中構建自己的URL請求引數。例如書上所說的如果要爬取作者新的浪微博,由於微博是是ajax的方式載入,需要在開發者工具才能看到ajax請求和伺服器的響應,所以請求url需要在開發者工具裡查詢,經過查詢分析,發現xhr(可以檢視ajax的請求和響應資訊)中的請求URL傳入了4個引數(問號後面的即為查詢傳入的引數),前面三個是不變的,而變化的是最後一個,我們可以利用urllib模組中的urlencode模組來傳遞這些引數,連結如下:

https://m.weibo.cn/api/container/getIndex?type=uid&value=2830678474&containerid=1076032830678474&page=2

程式碼如下:

import requests
from urllib.parse import urlencode
base_url = 'https://m.weibo.cn/api/container/getIndex?'
headers = {
    'Host': 'm.weibo.cn',
    'Referer': 'https://m.weibo.cn/u/2830678474',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest',
}      # 模擬請求頭


def get_page(page):
    params = {
        'type': 'uid',
        'value': '2830678474',
        'containerid': '1076032830678474',
        'page': page
    }
    url = base_url + urlencode(params)
    response = requests.get(url, headers=headers)
    return response.json()    # 由於內容是由ajax載入,響應的內容是json的形式


if __name__ == '__main__':
    for page in range(1,3):
        result = get_page(page)
        print(result)

  再如要要抓取今日頭條一些街拍的圖片,在搜尋框輸入“街拍”二字之後回車便進入到街拍頁面,看下網頁的url是:https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D,但是直接拿這個去爬取圖片是不成功的,因為這些資料是ajax載入,不存在網頁原始碼檔案中,為之奈何?和前面的處理方法一樣啊,其中可變引數是offset,這是構造的關鍵,其中連結的形式如下:

https://www.toutiao.com/search_content/?offset=20&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&curtab=1&from=search_tab

程式碼如下:

import requests
from urllib.parse import urlencode


def get_page(offset):
    headers = {
        'User-Agent': "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0;"
                      " .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    }
    params = {
        'offset': offset,
        'format': 'json',
        'keyword': '街拍',
        'autoload': 'true',
        'count': '20',
        'cur_tab': '1',
        'from': 'search_tab',
    }
    base_url = 'https://www.toutiao.com/search_content/?'
    url = base_url + urlencode(params)
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            print(response.status_code)
            return response.json()
    except requests.ConnectionError:
        return None


if __name__ == '__main__':
    for offset in range(20, 60, 20):
        result = get_page(offset)
        print(result)

HTTP方法

  GET方法和POST方法是HTTP中最常用的方法。它們能夠訪問和下載和訪問網站伺服器資源,這些網頁就是我們要爬取並摘取資料的資源,爬蟲程式模擬了瀏覽器實現這種HTTP的GET或者POST等方法去獲取資源。

GET方法

  GET 方法用來請求訪問已被 URI 識別的資源。指定的資源經伺服器端解析後返回響應內容。也就是說,如果請求的資源是文字,那就保持原樣返回;如果是像 CGI(Common Gateway Interface,通用閘道器介面)那樣的程式,則返回經過執行後的輸出結果。如圖:

POST方法

  POST 方法用來傳輸實體的主體。

  雖然用 GET 方法也可以傳輸實體的主體,但一般不用 GET 方法進行傳輸,而是用 POST 方法。雖說 POST 的功能與 GET很相似,但POST 的主要目的並不是獲取響應的主體內容。如圖:

 HTTP報文

  HTTP報文是指客戶端和伺服器用於HTTP互動的的資訊,客戶端的HTTP報文稱為請求報文,伺服器端的報文稱為響應報文。需要爬蟲某網頁的時候,我們可以在開發者工具裡面查詢並分析這些報文以瞭解客戶端的HTTP請求及URL等,從而模擬客戶端去進行資料爬取。

  HTTP報文大致可以分為報文首部和主體,兩者由最初出現的空行(CR+LF)來劃分,如圖:

  • 請求首部和請求例項

  請求首部由請求行(HTTP請求方法、URL、HTTP協議版本)和首部欄位(請求首部欄位、通用首部欄位、實體首部欄位)及其他內容組成,以下是請求頭部結構和請求例項,其中請求頭User-Agent是爬蟲在模擬HTTP方法請求網頁資源時常常需要新增的一個引數。

  • 響應首部和響應例項

  響應首部則由狀態行(HTTP協議版本、響應狀態碼(數字和原因短語))和HTTP首部欄位(響應首部欄位、通用首部欄位、實體首部欄位)組成,以下分別是響應頭部結構和響應例項

  • 通用首部欄位

  • 請求首部欄位

  • 響應首部欄位

Cookie

  HTTP 是無狀態協議,它不對之前發生過的請求和響應的狀態進行管理。也就是說,無法根據之前的狀態進行本次的請求處理。為解決此矛盾,Cookie技術通過在請求和響應報文中寫入Cookie資訊來控制客戶端的狀態。Cookie會根據從服務端傳送過來的報文內的一個叫Set-Cookie的首部欄位資訊,通知客戶端儲存cookie。當下次客戶端再向此伺服器傳送請求時,客戶端會自動在請求報文加入值後再發過去給服務端。服務端接收到客戶端傳送過來的cookie之後,會檢查這個cookie究竟是從哪一個客戶端傳送過來,然後對比之前的服務記錄,最後得到之前的狀態資訊。爬蟲中也會模擬這種帶cookie的HTTP請求來實現反爬蟲或使得抓取的資料更全面等,如圖

TCP/IP 的分層管理

  因特網由TCP/IP統籌,所以全球資訊網間接由它統籌。由於HTTP位於TCP/IP協議簇的應用層,瞭解TCP/IP協議簇有助於我們更加了解HTTP。CP/IP協議族裡重要的一點就是分層,分層的好處在於,當網際網路需要改動時,分層之後只需把變動對應的層替換掉即可,設計也變得相對簡單。TCP/IP協議族按層次分別分為以下4層:應用層、傳輸層、網路層和資料鏈路層。如圖:

  • 應用層:應用層決定了向用戶提供應用服務時通訊的活動。

  TCP/IP協議族內預存了各類通用的應用服務。比如FTP(FileTransfer Protocol,檔案傳輸協和DNS(Domain Name System,域名系統)服務就是其中兩類。HTTP協議也處於該層。

  • 傳輸層:傳輸層對上層應用層,提供處於網路連線中的兩臺計算機之間的資料傳輸。

  在傳輸層有兩個性質不同的協議:TCP(Transmission ControlProtocol,傳輸控制協議)和UDP(User Data Protocol,使用者資料報協議)。

  • 網路層(又名網路互連層):

  網路層用來處理在網路上流動的資料包。資料包是網路傳輸的最小資料單位。該層規定了通過怎樣的路徑(所謂的傳輸路線)到達對方計算機,並把資料包傳送給對方。與對方計算機之間通過多臺計算機或網路裝置進行傳輸時,網路層所起的作用就是在眾多的選項內選擇一條傳輸路線。

  • 鏈路層(又名資料鏈路層,網路介面層):

  用來處理連線網路的硬體部分。包括控制作業系統、硬體的裝置驅動、NIC(Network Interface Card,網路介面卡,即網絡卡),及光纖等物理可見部分(還包括聯結器等一切傳輸媒介)。硬體上的範疇均在鏈路層的作用範圍之內。

 

  利用 TCP/IP 協議族進行網路通訊時,會通過分層順序與對方進行通訊。傳送端從應用層往下走,接收端則往應用層往上走。如圖:

  首先,客戶端在應用層(HTTP協議)發出一個想看某個網頁的HTTP請求,接著為了傳輸方便,傳輸層(TCP協議)把從應用層接受到的資料(請求報文)進行分割,並在各個報文上打上標記序號和埠號傳送給網路層,在網路層(IP協議),增加作為通訊目的地的MAC地址傳送給鏈路層,至此,發往伺服器的通訊請求就準備齊全了

  接著服務端在鏈路層接收到客戶端發來的請求,然後按序一直往上傳送到應用層,當請求傳到應用層服務端才算真正接收到客戶端傳送過來的HTTP請求。傳送端在層與層之間傳輸資料時,每經過一層時必定會被打上一個該層所屬的首部資訊。反之,接收端在層與層傳輸資料時,每經過一層時會把對應的首部消去。這種把資料資訊包裝起來的做法稱為封裝(encapsulate)

TCP三次握手

  TCP處於HTTP協議的傳輸層,三次握手的目的在於保證請求資訊的有效性,防止失效的連線請求報文段被服務端接收,從而產生錯誤。關於三次握手網上很多有趣的解釋

參考

  本文敘述的是一些與Python爬蟲相關的HTTP內容,主要參考自《HTTP權威指南》、《圖解HTTP》和《Python3網路爬蟲開發實戰》,僅僅是個人理解,望指正。