Python之urllib庫的用法


  參考:https://zhuanlan.zhihu.com/p/146016738

  urllib庫的作用

  爬蟲的第一個步驟是獲取網頁,urllib庫是用來實現這個功能:想服務器發送請求,得到服務器響應,獲取網頁的內容。

  Python的強大在於提供了功能齊全的類庫,來幫助我們完成這個請求,通過調用urllib庫,我們不需要了解請求的數據結構,HTTP,TCP,IP層網絡傳輸同學,以及服務器應答原理等。

  我們只需要關心以下三點,然后通過幾行調用urllib庫的代碼,就能夠獲得我們想要的網頁內容。

    • 請求的URL是什么
    • 傳遞的參數是什么
    • 如何設置可選的請求頭

   urllib庫的構成

  在python2中,曾經有urllib和urllib2兩個庫來實現請求的發送,但目前通用的python3版本中,兩個庫的功能已經合並成一個庫,統一為urllib,它是python內置函數,不需要額外安裝即可使用。

  urllib的四個模塊

 

  

  【1】requset:HTTP請求模塊,可以用來模擬發送請求,只需要傳入URL及額外參數,就可以模擬瀏覽器訪問網頁的過程。

  【2】error:異常處理模塊,檢測請求是否報錯,捕捉異常錯誤,進行重試或其他操作,保證程序不會終止。

  【3】parse:工具模塊,提供許多URL處理方法,如拆分、解析、合並等。

  【4】robotparser:識別網站的robots.txt文件,判斷哪些網站可以爬,哪些網站不可以爬,使用頻率較少。

  發送請求

  urlopen是request模塊中的方法,用於抓取網絡。

  官方文檔:https://docs.python.org/3/library/urllib.request.html

  我們以代碼示例,我們抓取百度的網頁

# 調用urllib庫中的request模塊
import urllib.request
# 發送請求獲取百度網頁的響應
response = urllib.request.urlopen("http://www.baidu.com")
# 打印響應內容
# read()是把響應對象內容全部讀取出來,讀取出來為bytes碼
# decode('utf-8')把bytes碼解碼
print(response.read().decode('utf-8'))

  返回的結果比較多,隨便截取其中一部分,可以看出是百度的網頁HTML源代碼。

 

   我們只用幾行代碼,就完成了百度的抓取,並打印了網頁的源代碼,接下來,我們看一看我們獲得的響應內容response到底是什么?利用type()方法來輸出響應的類型。

print(type(response))
# <class 'http.client.HTTPResponse'>

  它是一個HTTPResponse類型的對象

  包含方法:read()、readinto()、getheader()、getheaders()、fileno()等

  包含屬性:msg、version、status、reason、debuglevel、closed等屬性。

  通過調用以上的方法和屬性,就能返回我們所需的信息。

  比如一開始我們打印獲取網頁內容時,就用到了read()方法。

  調用status屬性則可以得到返回結果的狀態碼,200代表強求成功,404代表網頁未找到等。

  再看一個代碼示例加深理解:

# 打印響應狀態碼
print(response.status,"\n")
# 200
# 打印響應頭信息
print(response.getheaders(),"\n")
# [('Bdpagetype', '1'), ('Bdqid', '0x90ae03e0000443e3'), ('Cache-Control', 'private'), ('Content-Type', 'text/html;charset=utf-8'), ('Date', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('Expires', 'Tue, 24 Aug 2021 01:18:50 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BAIDUID=162948399648CA18CD24B2476E039F6B:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BIDUPSID=162948399648CA18CD24B2476E039F6B; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1629767930; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'BAIDUID=162948399648CA18CA2A9672599961C8:FG=1; max-age=31536000; expires=Wed, 24-Aug-22 01:18:50 GMT; domain=.baidu.com; path=/; version=1; comment=bd'), ('Set-Cookie', 'BDSVRTM=17; path=/'), ('Set-Cookie', 'BD_HOME=1; path=/'), ('Set-Cookie', 'H_PS_PSSID=34437_34441_31253_34004_34092_26350_34390; path=/; domain=.baidu.com'), ('Traceid', '1629767930035569306610425274448017114083'), ('Vary', 'Accept-Encoding'), ('Vary', 'Accept-Encoding'), ('X-Frame-Options', 'sameorigin'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', 'close'), ('Transfer-Encoding', 'chunked')]
# 打印響應頭的Server值
print(response.getheader('Server'),"\n")
# BWS/1.1

  在打印的三行代碼中

  第一行輸出了響應的狀態碼(200代表正常)

  第二行輸出了響應的頭信息

  第三行通過調用getheader()方法並傳遞參數Server,獲取了第二行響應頭信息中的Server對應的值BWS/1.1。

  參數

  利用基本的urlopen()方法可以完成最基本的簡單網頁GET方法抓取。

  如果想達成更復雜一些的任務,需要給鏈接傳遞一些參數,該如何實現。

  urlopen()的函數原型如下:

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None) 

  除了第一個參數傳遞URL之外,我們還可以傳遞其他參數,比如data(附加數據),timeout(超時時間)等。

  data參數

  data用來指明往服務器請求中的額外參數信息,data默認是None,此時以GET方式發送請求;當用戶給出data參數的時候,改為POST方式發送請求。

  data參數是可選的,如果需要添加該參數,需要使用bytes()方法將參數轉化為二進制數據。

  還是通過代碼理解

  首先了解模塊urllib.parse.urlencode的用法

  urllib.parse.urlencode用於把一個字典轉換成對應的str

  舉例說明

# urllib.parse.urlencode把字典轉換成字符串str
# 如果字典包含多個元素則之間使用&分隔
import urllib.parse
print(urllib.parse.urlencode({"word":"hello"}))
# word=hello
print(urllib.parse.urlencode({"word":"hello","word1":"hello1"}))
# word=hello&word1=hello1

  下面代碼演示POST方法

import urllib.parse
import urllib.request
# urllib.parse.urlencode({'word':'hello'})把字典轉換為字符串 "word=hello"
# bytes("word=hello",encoding='utf-8')把字符串轉換成二進制b"word=hello"
data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
# 以下方法和上面轉換的結果一致
# data = urllib.parse.urlencode({'word1':'hello'}).encode('utf-8')
response = urllib.request.urlopen("http://httpbin.org/post",data=data)
print(response.read().decode('utf-8'))

  首頁這里請求的站點是,它是一個HTTP請求測試網站,記住它后面舉例會經常用到它。

  這次我們要使用post方式請求,而不是get,因為post是需要攜帶表單信息的(類似登陸的用戶名和密碼)所以我們要在urlopen函數中傳遞data參數。

  前面講了data參數必須是bytes型。

  bytes()這個方法第一個參數需要str(字符串類型),這里就需要用到urllib庫的parse模塊里的urlencode()方法來將參數字典轉化為字符串。第二個參數是  指定編碼格式,這里指定為utf-8。

  運行結果如下:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "word": "hello"
  },
  "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "10",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Python-urllib/3.6",
    "X-Amzn-Trace-Id": "Root=1-6124521e-0844c17c161a90e06ad543c0"
  },
  "json": null,
  "origin": "116.25.236.109",
  "url": "http://httpbin.org/post"
}

  該網頁通過POST傳遞了表單數據,在form內輸出一個字典格式,如果傳遞字典有多個元素則form下也對應多個元素。

  可以看到,我們傳遞的參數data中的字典鍵值對"word":"hello"出現在了form字段中,這表明了表單提交的方式,以POST方式傳輸數據。

  timeout參數

  timeout參數用於設置超時時間,單位為秒,意思是如果請求時間超過了設置的時間,還沒有得到響應,就會拋出異常,在實際爬蟲中,我們要對許多URL發起請求,中途肯定會出現爬取異常的URL,短時間無法獲得響應,我們需要識別出這種異常,就需要用到timeout參數。

  如果不指定timeout參數,會使用全局默認時間。它支持HTTP、HTTPS、FTP請求。

  通過代碼示例理解:

# 調用urllib庫中的request模塊
import urllib.request
response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.01)
print(response.read().decode('utf-8'))

  這里我們把timeout參數設成0.01秒,網頁反應時間是沒這么快的,所以一定會報錯,我們來看一下報錯的信息:

urllib.error.URLError: <urlopen error timed out>

  顯示異常屬於urllib.error模塊,錯誤原因是超時。

  在實際爬蟲中,我們可以用過設置這個超時時間來控制一個網頁在長時間未響應時,就跳過它的抓取。這可以利用try except 語句來實現。

  代碼示例:

import socket
import urllib.request

try:
    response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.01)
    print(response.read().decode('utf-8'))
except urllib.error.URLError as e :
    if isinstance(e.reason,socket.timeout):
        print("time out !")

  在代碼中加入try except 語句后,如果響應超時,便會跳過抓取,我們捕獲了URLError異常后,接着判斷異常是socket.timeout類型(意思就是超時異常),於是打印出了time out !

  輸出結果如下:

time out !

  其他參數(少用)

  context參數:它必須是ssl.SSLContext類型,用來指定SSL設置,實現SSL加密傳輸。

  cafile、capath:分別指定CA證書和它的路徑,用於實現可信任的CA證書的HTTP請求。

  cadefault:已經棄用,默認False。

  2,Request

   上文已經講解了如何利用urlopen()方法實現最基本的請求發起。但這幾個簡單的參數並不足以構建一個完整的請求(過於簡單的請求會被瀏覽器識別為爬蟲而拒絕響應)。如果請求中需要加入Headers等信息,就需要用到Requset類來構建。

  同樣通過實例展示Request用法:

# 展示Request
import urllib.request

request = urllib.request.Request('http://httpbin.org/get')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

  我們依然是用urlopen()方法來發送請求,只是這次的參數不在是url,而是一個Request類型對象。通過構造這個數據類型,我們可以將請求獨立為一個對象,並且更靈活的配置參數。

  Reque的函數原型如下:

class urllib.request.Request(url,data = None,headers ={ },origin_req_host =None,unverifiable = False,method = None)

  下面來看它的具體參數:

  url:用於請求的URL,這是畢傳參數,其他都是可選參數

  data:必須是bytes類型,如果它是字典,可以先用urllib.parse模塊里的urlencode方法編碼(用於POST請求)

  headers:是一個字典,它就是請求頭,我們可以在構造請求時,通過headers參數直接構造,也可以通過調用請求示例的add_header()方法添加

  origin_req_host:指的是請求方的host名稱或IP地址。

  unverifiable:表示這個請求是否無法驗證,默認為False,意思是說用戶沒有足夠權限來選擇接收這個請求的結果。比如我們請求一個HTML文檔中的圖片,但是我們沒有自助抓取圖象的權限,這是unverifiable的值就是True。

  method:是一個字符串,用來指示請求使用方法,如GET、POST、PUT等。

# Request請求多個參數
import urllib.request,urllib.parse

url = "http://httpbin.org/post"
headers = {
 "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0",
 "Host":"httpbin.org" 
}

dict = {'name':'Germey'}
data = bytes(urllib.parse.urlencode(dict),encoding='utf-8')
req = urllib.request.Request(url=url,data=data,headers=headers,method='POST')
response = urllib.request.urlopen(req)
print(response.read().decode('utf-8'))

  在這個示例中,我們通過4個參數構造了一個請求,我們加入了url、data、headers、method,其中最重要的是把頭信息(包含User-Agent和Host)寫入了  Request,讓請求變得更完整。

  運行結果如下:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "Germey"
  },
  "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "11",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0",
    "X-Amzn-Trace-Id": "Root=1-61246460-1f4d152f11557e9f6670f33a"
  },
  "json": null,
  "origin": "116.25.236.109",
  "url": "http://httpbin.org/post"
}

  可以看到,我們成功通過新建的參數,設置了data、headers 和 method。

  另外之前提到的headers也可以用add_header()方法添加,示例代碼如下:

req = urllib.request.Request(url=url,data=data,method='POST')
req.add_header('User-Agent',"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0")

  傳遞參數格式為key value格式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM