python urllib模塊(urlopen/response/request/headler/異常處理/URL解析)

簡介

Urllib是Python內置的HTTP請求庫。它包含四個模塊:

  • urllib.request :請求模塊
  • urllib.error :異常處理模塊
  • urllib.parse url : 解析模塊
  • urllib.robotparser :robots.txt解析模塊,用的比較少

相比Python2變化:
Python2: import urllib2 response=urllib2.urlopen(‘http://www.baidu.com’)
Python3: import urllib.request response=urllib.request.urlopen(‘http://www.baidu.com’)

基礎函數用法

一、urlopen

這個函數很簡單,就是請求一個url的內容。其實這就是爬蟲的第一步:網頁請求,獲取內容。
函數用法:

urllib.request.urlopen(url,data = None,[timeout]*,cafile = None,capath = None,cadefault = False,context = None)#urlopen前三個分別是(網站,網站的數據,超時設置)

可以看到,這個函數可以有非常多的參數,前三個用的最多,我們來逐一看看。

1.url參數

示例代碼:

import urllib.request
response =urllib.request.urlopen('http://www.baidu.com') #把請求的結果傳給response
print(response.read().decode('utf-8'))#轉換成字符串

在交互模式下執行以上命令就可以打印出百度首頁的源代碼了。
這是一種GET類型的請求,只傳入了一個參數(url)。
下面演示一種POST類型的請求:

2.data參數

示例代碼:

import urllib.parse
import urllib.request

data=bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf8')
#需要傳入一個data參數,需要的是bytes類型,這裏用了urlencode方法傳入一個字典,並指定編碼
response=urllib.request.urlopen('http://httpbin.org/post',data=data)
#給urlopen函數傳入兩個參數,一個是url,一個是data
print(response.read())

http://httpbin.org/ 是一個HTTP測試頁面)
可以看到打印出了一些Json字符串:


我們可以從打印結果看到,我們成功的把’word’:'hello’這個字典通過urlopen函數以POST形式把它傳遞過去了。這樣我們就完成了一個POST的請求。
總結:加了data參數就是以POST形式發送請求,否則就是GET方式了

3.timeout

再來看看第三個參數:超時時間。如果超過了這個時間沒有得到響應的話,就會拋出異常。
示例代碼:

import urllib.request
response=urllib.request.urlopen('http://httpbin.org/get',timeout=1)#設置超時時間
print(response.read())

再看另一種情況,我們把超時時間設置爲0.1:

import socket
import urllib.request
import urllib.error

try:
    response=urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
	#必須在0.1秒內得到響應,否則就會拋出異常
except urllib.error.URLError as e:
	if isinstance(e.reason,socket.timeout):#類型判斷,如果是超時錯誤,那麼打印
		print('TIME OUT')


二、響應(response)

獲取響應類型:type()

示例:

import urllib.request
response =urllib.request.urlopen('https://www.python.org')
print(type(response))#打印響應的類型

通過調用這個方法我們可以看到響應的類型。

狀態碼、響應頭

一個響應裏面包含了兩個比較有用的信息:狀態碼和響應頭。

在這裏插入圖片描述

以上面提到的http://httpbin.org/ 爲例,我們可以在審查中,找到狀態碼和響應頭(上圖紅框所示)。
這兩個信息是判斷響應是否成功的非常重要的標誌。
在這裏我們可以用status參數獲取響應的狀態碼,用getheaders()方法獲取響應頭。
示例:

import urllib.request

response =urllib.request.urlopen('https://www.python.org')
print(response.status)#狀態碼
print(response.getheaders())#響應頭
print(response.getheader('Server'))#可以取得響應頭中特定的信息

另外,關於read()方法,它獲取的是響應體中的內容(bytes形式)

import urllib.request
response =urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))#將字節流解碼爲utf-8


三、請求(request)

如果我們要在請求中加入別的信息怎麼辦呢?用上面的urlopen函數是無法滿足的。
例如我們要在請求中加上Headers參數,但是urlopen函數中是沒有提供這個參數的。
如果我們想要發送一些更爲複雜的請求的話,比如加上Headers,怎麼辦呢?
那麼我可以創建一個request對象——使用Request(它也是屬於urllib.request模塊的)。
來看示例:

import urllib.request
request=urllib.request.Request('https://python.org')#把url構造成一個request對象
response=urllib.request.urlopen(request)#再把request對象傳給urlopen
print(response.read().decode('utf-8'))

這樣也可以成功實現請求。
有了這樣的方法,我們就能更方便地指定請求的方式,還可以加一些額外的數據。
那麼現在嘗試構造一個POST請求,並且把headers加進來。

from urllib import request,parse
#構造一個POST請求
url='http://httpbin.org/post'
headers={
	'Users-Agent':'..............',
	'Host':'httpbin.org'
}
dict={
	'name':'Germey'
}
data=bytes(parse.urlencode(dict),encoding='utf8')#fontdata數據
req=request.Request(url=url,data=data,headers=headers,method='POST')#構建一個Request()的一個結構
response = request.urlopen(req)
print(response.read().decode('utf-8'))

我們把以上代碼寫成一個py文件並運行之:

在這裏插入圖片描述

可以看到,我們構造的Request包含了之前所提到的信息,請求的時候我們是把Request當成一個整體傳遞給了urlopen,就可以實現這樣的請求了。
好處是整個Request的結構是非常清晰的。
此外還有另外一種實現方式,就是用add_header()方法,也可以實現相同的效果:

from urllib import request,parse
#構造一個POST請求
url='http://httpbin.org/post'
dict={
	'name':'Germey'
}
data=bytes(parse.urlencode(dict),encoding='utf8')#fontdata數據
req=request.Request(url=url,data=data,method='POST')#構建一個Request()的一個結構
req.add_header('User-Agent','................')#用add_header方法來添加header信息
response = request.urlopen(req)
print(response.read().decode('utf-8'))

add_header()方法作爲添加header的另一種方式,可以用來比較複雜的情況,比如要添加許多鍵值對,那麼可以用一個for循環來不斷調用這個方法,這也是比較方便的。

四、Headler

Headler相當於一個輔助工具,來幫助我們處理一些額外的工作,比如FTP、Cache等等操作,我們都需要藉助Headler來實現。比如在代理設置的時候,就需要用到一個ProxyHandler。更多的用法,請參閱官方文檔

代理

用來對ip地址進行僞裝成不同地域的,防止ip在爬蟲運行時被封掉。
示例:

from urllib import request
proxy_handler = request.ProxyHandler( #構建ProxyHandler,傳入代理的網址
{'http':'http://127.0.0.1:9743',
'https':'https://127.0.0.1:9743'
}) #實踐表明這個端口已經被封了,這裏無法演示了
 
opener = request.build_opener(proxy_handler)#再構建一個帶有Handler的opener
 
response = opener.open('http://www.baidu.com')
print(response.read())


Cookie

Cookie是在客戶端保存的用來記錄用戶身份的文本文件。
在爬蟲時,主要是用來維護登錄狀態,這樣就可以爬取一些需要登錄認證的網頁了。
在這裏插入圖片描述

在這裏插入圖片描述

實例演示:

from urllib import request
 
from http import cookiejar
cookie =cookiejar.CookieJar()#將cookie聲明爲一個CookieJar對象
 
handler = request.HTTPCookieProcessor(cookie)
 
opener = request.build_opener(handler)
 
response  =opener.open('http://www.baidu.com')#通過opener傳入,並打開網頁
 
for item in cookie:#通過遍歷把已經賦值的cookie打印出來
    print(item.name+'='+item.value)#通過item拿到每一個cookie並打印

在這裏插入圖片描述

Cookie的保存

我們還可以把cookie保存成文本文件,若cookie沒有失效,我們可以從文本文件中再次讀取cookie,在請求時把cookie附加進去,這樣就可以繼續保持登錄狀態了。
示例代碼:


from urllib import request
from http import cookiejar

filename="cookie.txt"
cookie=cookiejar.MozillaCookieJar(filename)
#把cookie聲明爲cookiejar的一個子類對象————MozillaCookieJar,它帶有一個save方法,可以把cookie保存爲文本文件
handler=request.HTTPCookieProcessor(cookie)
opener=request.build_opener(handler)
response=opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True,ignore_expires=True)#調用save方法

執行代碼後,我們就可以在運行目錄下找到已經保存好的cookie文本文件了:

在這裏插入圖片描述

還有另外一種格式:
在上面那段代碼的基礎上,換一個子類對象就可以了:

cookie=cookiejar.LWPCookieJar(filename)

可以看到,這次生了一個不同格式的cookie文本文件:


Cookie的讀取

我們可以選擇相對應的格式來完成讀取。以上面的LWP格式爲例:

from urllib import request
from http import cookiejar


cookie=cookiejar.LWPCookieJar() #z注意選擇相應的格式,這裏是LWP
cookie.load('cookie.txt',ignore_discard=True,ignore_expires=True)#load方法是讀取的關鍵
handler=request.HTTPCookieProcessor(cookie)
opener=request.build_opener(handler)
response=opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))

以上的代碼就可以完成讀取了。這樣,我們就可以在對網頁進行請求時,自動把之前的cookie附着進去,以保持一個登錄的狀態了。

五、異常處理

這是屬於urllib的另一大模塊。

from urllib import request,error

#我們試着訪問一個不存在的網址
try:
    response = request.urlopen('http://www.cuiqingcai.com/index.html')
except error.URLError as e:
    print(e.reason)#通過審查可以查到我們捕捉的異常是否與之相符

在這裏插入圖片描述

可以看到,返回了錯誤信息。這樣的異常處理可以保證爬蟲在工作時不會輕易中斷。
那麼,urllib可以捕捉哪些異常呢?詳見官方文檔
其實一般碰到有兩個:HTTP和URL。我們一般只需要捕捉這兩個異常就可以了。

from urllib import request,error

#我們試着訪問一個不存在的網址
try:
    response = request.urlopen('http://www.cuiqingcai.com/index.html')
except error.HTTPError as e:#最好先捕捉HTTP異常,因爲這個異常是URL異常的子類
	print(e.reason,e.code,e.headers,sep='\n')
except error.URLError as e:
    print(e.reason)
else:
	print('Request Successfully!')

在這裏插入圖片描述

如上,打印出了錯誤的相關信息。
此外,e.reason也是一個類,它可以得到異常的類型。
我們試着看看:

from urllib import request,error
import socket


try:
    response = request.urlopen('http://www.baidu.com',timeout = 0.01)#超時異常
except error.URLError as e:
    print(type(e.reason))
    if isinstance(e.reason,socket.timeout):#判斷error類型
        print('TIME OUT')

異常類型被打印出來了,確實是超時異常。

在這裏插入圖片描述

六、URL解析

這是一個工具性質的模塊,即拿即用就行。
官方文檔

urlparse

這個方法將將url進行分割,分割成好幾個部分,再依次將其複製。

urllib.parse.urlparse(urlstring,scheme='',allow_fragments = True)
#分割成(url,協議類型,和#後面的東西)

具體的例子:

from urllib.parse import urlparse
 
result = urlparse('https://www.baidu.com/s?wd=urllib&ie=UTF-8')
print(type(result),result)  #<class 'urllib.parse.ParseResult'>
 
#無協議類型指定,自行添加的情況
result1 = urlparse('www.baidu.com/s?wd=urllib&ie=UTF-8',scheme = 'https')
print(result1)
 
#有指定協議類型,默認添加的情況?
result2 = urlparse('http://www.baidu.com/s?wd=urllib&ie=UTF-8',scheme = 'https')
print(result2)

#allow_fragments參數使用
result3 = urlparse('http://www.baidu.com/s?#comment',allow_fragments = False)
 
result4 = urlparse('http://www.baidu.com/s?wd=urllib&ie=UTF-8#comment',allow_fragments = False)
print(result3,result4)
#allow_fragments=False表示#後面的東西不能填,原本在fragment位置的參數就會往上一個位置拼接,可以對比result1和result2的區別


從這個例子我們也可以知道,一個url可以分成6個字段。

urlunparse(urlparse的反函數)

這個函數用來拼接url。
看看這個例子:

from urllib.parse import urlunparse
#注意即使是空符號也要寫進去,不然會出錯

data = ['http',  'www.baidu.com', 'index.html','user','a=6' 'comment']
print(urlunparse(data))


urljoin

這個函數用來拼合url。
通過例子感受以下:
以後面的參數爲基準,會覆蓋掉前面的字段。如果後面的url,存在空字段而前面的url有這個字段,就會用前面的作爲補充。

from urllib.parse import urljoin
print(urljoin('http://www.baidu.com','FQA.html'))
#http://www.baidu.com/FQA.html
 
print(urljoin('http://www.baidu.com','http://www.caiqingcai.com/FQA.html'))
#http://www.caiqingcai.com/FQA.html
 
print(urljoin('https://www.baidu.com/about.html','http://www.caiqingcai.com/FQA.html'))
#http://www.caiqingcai.com/FQA.html
 
print(urljoin('http://www.baidu.com/about.html','https://www.caiqingcai.com/FQA.html'))
#https://www.caiqingcai.com/FQA.html


urlencode

這個函數用來將字典對象轉化爲get請求參數。

from urllib.parse import urlencode
 
params = {
'name':'zhuzhu',
'age':'23'
}
 
base_url = 'http://www.baidu.com?'
 
url = base_url+urlencode(params)#將params對象編碼轉換
 
print(url)

在這裏插入圖片描述
七、robotparser

用來解析robot.txt。用的比較少,這裏不再贅述。
官方文檔
 

八、實例

request = urllib2.Request(self.url)
request.add_header('Cookie','PHPSESSID=79lo60cmtl1ck70h4ufruq6n53; mmf_searchhotkeyandroid=%E5%A4%A9%E6%B6%AF%E7%A4%BE%E5%8C%BA%2C%E7%A9%BF%E8%A1%A3%E5%8A%A9%E6%89%8B%2C%E5%A4%A9%E6%B0%94%2C%E9%B3%84%E9%B1%BC%E5%B0%8F%E9%A1%BD%E7%9A%AE%E7%88%B1%E6%B4%97%E6%BE%A12%2C%E6%B0%B4%E6%9E%9C%E5%BF%8D%E8%80%85%2C%E4%B8%96%E7%95%8COL%2C%E6%88%98%E5%A4%A9; mmf_msisdn=08e2b01ad5dd5b3d297ef6558a60ec26; mmf_us=08e2b01ad5dd5b3d297ef6558a60ec26.39; mmf_userVisitPageIndex=79lo60cmtl1ck70h4ufruq6n53.2')

request.add_header('Connection','keep-alive')
request.add_header('Accept','*/*')
request.add_header('Accept-Language','zh-CN,zh;q=0.8')
request.add_header('Accept-Encoding','gzip,deflate,sdch')
request.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36')

response = urllib2.urlopen(request)
print response.code

if response.info().get('Content-Encoding')=='gzip':
    buf = StringIO(response.read())
    f = gzip.GzipFile(fileobj = buf)
    data = f.read()
    f.close()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章