天天看點

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

目錄

  • 網絡庫urllib
    • request
      • 發送GET請求
      • 發送POST請求
      • 請求逾時處理
      • 爬蟲僞裝
      • 代理
      • 擷取Cookie
    • Parse
      • 中文的編碼與解碼
      • quote與unquote
      • URL解析
      • 連接配接URL
      • 參數轉換(parse_qs與parse_qsl)
    • Robots協定
      • Robots協定的定義規則
      • Robots協定的解析
    • error
      • HTTPError

urllib庫是Python3内置的HTTP請求庫,不需要單獨安裝,預設下載下傳的Python就已經包含了該庫。

urllib庫有4個子產品:

  1. request:最基本的HTTP請求子產品,可以用法發送HTTP請求,并接收伺服器的響應資料,這個過程就像在浏覽器位址欄輸入URL一樣。
  2. parse:工具子產品,提供了很多處理URL的API,如拆分、解析、合并等。
  3. robotparser:用來識别網站的robots.txt檔案,然後判斷哪些網站可以抓取,哪些不能抓取。
  4. error:異常處理。如果出現請求錯誤,可以捕獲這些異常,然後根據代碼的需要,進行處理。

下面,我們來分别介紹urllib庫這4個子產品。

request子產品,包含發送請求,獲得響應,Cookie,代理等相關API。這裡,我們分别來介紹其使用的方式。

首先,我們一般進行爬蟲程式編寫時,開始都需要發送請求,然後獲得響應進行處理。比如通過GET請求擷取網頁源代碼,示例如下:

import urllib.request

response=urllib.request.urlopen("")
print(response.read().decode("UTF-8"))
           

如上面代碼所示,運作之後,我們會得到網頁html源代碼。

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏
這裡response是一個HTTPResponse對象,調用它多種方法及其屬性,就可以進行多樣的處理。

比如,我們這裡來擷取CSDN首頁,調用其屬性及其方法,示例如下:

import urllib.request

response = urllib.request.urlopen("

")
print("status:", response.status, "  msg:", response.msg, "  version:", response.version)
print(response.getheaders())
           

這裡,我們輸出了CSDN首頁的響應狀态碼,響應消息以及HTTP版本的屬性,同時也列印其完整的響應頭資訊。

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

預設request.urlopen()函數發送的GET請求,如果需要發送POST請求,我們需要使用data指令參數,該參數類型是bytes。示例代碼如下:

import urllib.request
import urllib.parse

data = bytes(urllib.parse.urlencode({'username': 'name', 'age': '123456'}), encoding="UTF-8")
response = urllib.request.urlopen("http://httpbin.org/post", data=data)
print(response.read().decode("UTF-8"))
           

運作之後,如果請求成功就會傳回一大堆字元串資料。

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

在實際的爬蟲項目中,我們都需要考慮一個關鍵的問題,那就是請求逾時。如果請求網址因為長時間沒有響應,那麼卡在這裡往往浪費大量的資源。

是以,我們需要設定一個逾時時間,如果在限定的時間沒有響應,就應該重新抓取,或者不在抓取。示例代碼如下:

import urllib.request
import urllib.parse
import urllib.error
import socket

data = bytes(urllib.parse.urlencode({'username': 'name', 'age': '123456'}), encoding="UTF-8")
try:
    response = urllib.request.urlopen("http://httpbin.org/post", data=data, timeout=0.1)
    print(response.read().decode("UTF-8"))
except urllib.error.URLError as e:
    if isinstance(e.reason,socket.timeout):
        print("逾時處理")
finally:
    print("繼續爬蟲的工作")
           

現在隻要是知名的網站,都具有定義的防爬蟲技術。是以,我們在開發爬蟲程式時,需要将自己的請求僞裝成浏覽器。

不過,urlopen()并沒有header請求頭參數,我們需要使用request.Request()進行構造請求,示例代碼如下:

import urllib.request
import urllib.parse

data = bytes(urllib.parse.urlencode({'username': 'name', 'age': '123456'}), encoding="UTF-8")
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
req = urllib.request.Request("http://httpbin.org/post", data=data, headers=headers)
response = urllib.request.urlopen(req)
print(response.read().decode("UTF-8"))
           

這裡,我們将爬蟲程式僞裝成從火狐浏覽器進行的請求。

當然,Request類還有許多其他的資訊,下表是部落客列出來供讀者參考使用的。

參數 意義
url 用于發送請求的URl,必選參數(除這個參數之外,其他參數可選)
data 要送出的表單資料,必須是bytes類型
headers 請求頭
origin_req_host 請求方的host名稱或IP位址
unverifiable 表示這個請求是否是無法驗證的,預設是False,主要是說明使用者沒有足夠的權限來選擇接收這個請求的結果。舉個例子,請求一個HTML的圖像,但沒有自動抓取圖像的權限,這個時候該參數為True
method 用來指定是什麼請求,比如GET,POST,PUT等,如果指定了data,預設就是POST請求
需要注意的是最後一個參數method,如果你在程式中,即指定了請求是GET,同時也有data表單資料,那麼預設表單不會送出給伺服器。

其實,伺服器除了根據請求頭進行判斷是否為爬蟲之外,最簡單判别爬蟲的方式,就是判斷是否是同一個IP短時間大量通路。

如果同一個IP短時間大量通路,那麼可以直接判别為爬蟲程式。而這個時候,我們可以通過代理進行僞裝。當然,使用代理時應該不斷更換代理伺服器。

示例代碼如下:

import urllib.error
from urllib.request import ProxyHandler, build_opener

proxy_handler = ProxyHandler({
    'http': 'http://183.47.138.80:8888',
    'http': 'http://125.78.226.217:8888'
})
opener = build_opener(proxy_handler)
try:
    response = opener.open('')
    print(response.read().decode('UTF-8'))
except urllib.error.URLError as e:
    print(e.reason)
           

運作之後,與前文一樣傳回CSDN首頁的源代碼。不過,需要注意的是,部落客在寫部落格時,這個代碼IP是有效的,但如果你讀到這篇博文,可能已經失效了,你需要自己取找免費的代理伺服器取測試。

一般來說,當我們使用爬蟲登入網站之後,會使用伺服器傳回的Cookie進行操作,有了這個Cookie,伺服器就知道我們已經登入過。

那麼如果擷取Cookie資料呢?示例如下:

import urllib.error
from urllib.request import ProxyHandler
import http.cookiejar

cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
try:
    response = opener.open('')
    for item in cookie:
        print("name=", item.name, "  value=", item.value)
except urllib.error.URLError as e:
    print(e.reason)
           

如上面代碼所示,我們可以通過http.cookiejar庫進行操作,這裡會傳回cookie的鍵值對,并輸出列印,效果如下:

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

如果本身網站的Cookie時效比較長的話,那麼一次爬蟲之後,我們可以将其儲存在使用,這個時候會用到MozillaCookieJar類與LWPCookieJar類。

這2個類會在擷取Cookie的同一時間,将Cookie分别儲存為Mozilla浏覽器格式和libwww-perl(LWP)格式。示例代碼如下:

import urllib.error
from urllib.request import ProxyHandler
import http.cookiejar

cookie = http.cookiejar.MozillaCookieJar('cookies.txt')
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
try:
    response = opener.open(')
    cookie.save(ignore_discard=True,ignore_expires=True)
except urllib.error.URLError as e:
    print(e.reason)
           

運作之後,會生成一個cookies.txt檔案,具體内容如下:

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏
而LWPCookieJar類的使用方式與上面MozillaCookieJar類似,這裡不在贅述。至于讀取通過cookie.load()方法進行加載Cookie。

在實際的爬蟲進行中,我們經常會遇到各種的編碼問題。而這些問題就可以通過Parse進行解決處理。

比如,在某些網址的請求頭中,有些網站請求頭會含有中文。但預設不進行任何處理肯定會報UnicodeEncodeError錯誤。

讀者可以通過上面的代碼,在headers字典中添加一個鍵值對,值是中文試試。下面,我們需要對其進行編碼,示例如下:

import urllib.parse
import base64

value = urllib.parse.urlencode({'name': '安踏'})
print("編碼後的中文:", value)
print("解碼後的中文:", urllib.parse.unquote(value))
#base64編碼與解碼
base64Value=base64.b64encode(bytes('學習網絡urllib庫',encoding="UTF-8"))
print("編碼後的中文:", base64Value)
print("解碼後的中文:", str(base64.b64decode(base64Value),"UTF-8"))
           

運作之後,效果如下:

史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏
需要注意的是,urlencode隻能對url參數進行編碼,比如這裡對headers請求頭進行了編碼。而網站中的資料,有的是通過base64進行編碼的,這個時候,我們也需要掌握base64的解碼與編碼。

quote函數也是一個編碼函數,但它與urlencode不同,urlencode隻能對URL進行編碼。而quote可以給任意字元串進行編碼。

至于unquote是編碼的逆過程(解碼),示例如下:

from urllib.parse import quote, unquote

url = 'https://www.baidu.com/s?wd=' + quote("王者榮耀")
print(url)
url = unquote(url)
print(url)
           
史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

在前面介紹的時候,我們也說了parse子產品可以分解,分析,合并URL。下面,我們來通過這些函數,舉一些列子,讀者就會明白了。

from urllib import parse

url = ''
split_result=parse.urlsplit(url);
print(split_result)
result = parse.urlparse(url)
print('scheme:', result.scheme)  # 網絡協定
print('netloc:', result.netloc)  # 域名
print('path:', result.path)  # 檔案存放路徑
print('query:', result.query)  # 查詢字元
print('fragment:', result.fragment)  # 拆分文檔中的特殊貓
           
史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

對于URL來說,有時候我們需要将URL的多個部分進行組合。比如,我們在爬取某一個網站下圖檔時,其預設的前部分基本都相同,可能隻是ID不同。

那麼這個時候,我們需要将前部分URL與ID進行拼接。示例代碼如下:

import urllib.parse

print(urllib.parse.urljoin('https://','liyuanjinglyj'))
print(urllib.parse.urljoin('https://t','https://www.baidu.com'))
print(urllib.parse.urljoin('https://','index.html'))
print(urllib.parse.urljoin('https://php','?name=30'))
           
史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏

urljoin的使用規則:第1個參數是base_url,是一個基URL,隻能設定scheme、netloc和path,第2個參數是url。

如果第2個參數不是一個完整的URL,會将第2個參數的值加到第1個參數的後面,自動添加斜杠"/”。

如果第2個參數是一個完整的URL,那麼直接傳回第2個URL。

parse_qs函數将多個參數拆成字典的形式,key是參數名,value是參數值。

而parse_qsl函數傳回一個清單,每個元素是一個包含2個元素值的元組,第1個元素表示key,第2個元素表示value。

示例如下:

from urllib.parse import parse_qs, parse_qsl

query = "name=李元靜&age=29"
print(parse_qs(query))
print(parse_qsl(query))
           
史上最詳細的Python爬蟲庫urllib講解:絕對經典,值得收藏
2個函數僅傳回的參數形式不同,内容基本都是比對的。同時,它們2個函數不管對什麼類型,轉換結果都是字元串。

Robots協定也稱為爬蟲協定、機器人協定,它的全名是網絡爬蟲排除标準(Robots Exclusing Protocol)。

用來告訴爬蟲和搜尋引擎哪些頁面可以抓取,哪些不可以抓取。該協定通常放在一個名為robots.txt的文本檔案中,它位于網站的根目錄下。

特别注意,robots.txt檔案僅僅是為了告訴開發中哪些資料可以抓取,哪些資料不可以,并不能阻止開發中進行爬蟲操作。但作為一個有良知的程式員,應盡量尊重這些規則。

robots.txt的文本檔案一般有3個值:

  1. User-agent:如果它為*,表示對所有爬蟲有效
  2. Disallow:哪個目錄下的資源不能抓取,比如Disallow:/index/,那麼禁止爬取index檔案中的所有資源。如果為/,表示該網站都不能抓取。
  3. Allow:表示哪個目錄下的資源可以抓取。比如Allow:/,表示所有網站都可以抓取。值與Disallow一樣,隻是反意義。

當然,User-agent還可以禁止某些爬蟲抓取,比如禁止百度爬蟲抓取:User-agent:BaiduSpider。常用的爬蟲名如下表:

爬蟲名稱 搜尋引擎
Googlebot 谷歌
360Spider 360搜尋
Bingbot 必應
BaiduSpider 百度

當然,我們擷取Robots協定,肯定是為了擷取哪些資源不可爬。但是,直接通過字元串進行解析雖然也能完成,不過稍微複雜了點。

而urllib庫的robotparser子產品提供了相應的API進行解析,也就是RobotFileParser類。示例如下:

from urllib.robotparser import RobotFileParser
#一種解析方式
robot = RobotFileParser()
robot.set_url()
robot.read()
print(robot.can_fetch()

#另一種解析方式
robot = RobotFileParser()
print(robot.can_fetch('*',))
           

這裡,can_fetch方法用來判斷該網址根據Robots協定是否可以抓取。這裡判斷的是CSDN排行榜能否抓取,傳回了True。

異常處理在上面的代碼中,我們已經用過了。比如在請求逾時的講解之中,我們用到了URLError。但是它還有一個子類HTTPError。

它主要用于HTTP請求錯誤的處理,比如400,404等錯誤。

  1. code:伺服器傳回的狀态碼,如404等
  2. reason:傳回錯誤的原因
  3. headers:傳回請求頭