程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

【Python爬蟲必備技術棧】urllib庫&&urllib3庫

編輯:Python

上一篇博文兩萬字博文教你python爬蟲requests庫【詳解篇】被眾多爬蟲愛好者/想要學習爬蟲的小伙伴們閱讀之後,小伙伴們都說學費了學費了,但是又有不少同學私信我做一些要求使用urllib庫實現的爬蟲項目,腦瓜子疼~

(苦笑)程序猿世界:“授之以魚不如授之以漁,” 所以本博主又連夜加班加點,苦苦熬制本文,為大家深入全面的講解一下Python中請求URL連接的官方標准庫——urllib庫以及urllib3庫!”

題外話——如果小伙伴們想要系統性學習爬蟲這門技術,那麼建議本文是你爬蟲路上的第一個必須要熟練掌握的爬蟲庫哦!(而入坑爬蟲的話必備知識點是這篇——一篇萬字博文帶你入坑爬蟲這條不歸路 【萬字圖文】)


重點來啦!重點來啦!!

相信有不少小伙伴已經通過我的上篇博文完全地學費了requests庫,而本篇文講解的urllib庫則是Python內置的HTTP請求庫,也就是說不需要額外安裝即可使用(隨拿隨用,多爽!),同樣是爬蟲必備哦~

️urllib庫詳解️

  • 第一部分:urllib庫
    • 一、request模塊:發送請求
      • 1.urlopen()
        • (1)簡單使用:
        • (2)高級使用:如果想給鏈接傳遞一些參數,就要用到urlopen()函數的其他參數!
          • ①data參數
          • ②timeout參數
      • 2.Request()
        • (1)簡單使用
          • ①初體驗
          • ②實戰—靈活運用參數
          • 實戰一之請求頭添加!
          • 實戰二之請求頭添加以及POST請求方式的使用!
        • (2)高級用法
    • 二、error模塊:處理異常
      • 1.URLError
      • 2.HTTPError
    • 三、parse模塊:解析鏈接
      • (1-1)urlparse()
        • ①基本使用:
        • ②高級使用:
      • (1-2)urlunparse()
      • (2-1)urlsplit()
      • (2-2)urlunsplit()
      • (3)urljoin()
      • (4-1)urlencode()
      • (4-2)parse_qs()
      • (5)parse_qsl()
      • (6-1)quote()
      • (6-2)unquote()
    • 四、robotparse模塊:分析Robots協議
      • (1)Robots協議
      • (2)robotparser
        • ①簡單講解:
        • ②靈活使用:
  • 第二部分:urllib3庫
    • (1)request方法:
        • 小拓展:使用json模塊的loads方法獲取特定信息
    • (2)發送json數據:
    • (3)文件上傳:
    • (4)二進制數據的上傳:
    • (5)設置代理:



直接跳到末尾 ——>領取專屬粉絲福利


第一部分:urllib庫

閒談一嘴:

  • urllib是Python中請求url連接的官方標准庫,在Python2中主要為urllib和urllib2,在Python3中整合成了urllib。

  • 而urllib3則是增加了連接池等功能,兩者互相都有補充的部分。

  • 就先來看看urllib庫。

簡介:urllib庫(官方文檔!),是Python內置的HTTP請求庫(不需要額外安裝就可以使用哦~),它包含如下4個模塊:

1.request:它是最基本的HTTP請求模塊,可以用來模擬發送請求。就像在浏覽器裡輸入網址然後回車一樣,
只需要給庫方法傳入URL以及額外的參數,就可以模擬實現這個過程了。
2.error:異常處理模塊,如果出現請求錯誤,我們可以捕獲這些異常,然後進行重試或其他操作以保證程序
不會意外終止。
3.parse:一個工具模塊,提供了多種URL處理方法。比如:拆分,解析,合並等。
4.robotparser:主要是用來識別網站的robots.txt文件,然後判斷哪些網站可以爬,哪些網站不可以爬,
其實用的很少!

一、request模塊:發送請求

request模塊主要負責構造和發起網絡請求,並在其中添加Headers,Proxy等,我們可以方便地實現請求的發送並得到響應。

利用它可以模擬浏覽器的請求發起過程。

  1. 發起網絡請求。
  2. 添加Headers。
  3. 操作cookie。
  4. 使用代理。

小拓展——使用代理解釋:(在這篇文章中有書面化的講解,可以看看——學了那麼久爬蟲,快來看看這些反爬,你能攻破多少?【對應看看自己修煉到了哪個等級~】)

1.urlopen()

urilib.request模塊提供了最基本的構造HTTP請求的方法,利用它可以模擬浏覽器的一個請求發起的過程,同時它還帶有處理授權驗證(authentication),重定向(redirection),浏覽器Cookies以及其他內容。

小知識點:

  • 測試網站:http://httpbin.org/get
    我們向此網站發送請求的話,會拿到我們發送的請求的信息。

(1)簡單使用:

import urllib.request
response = urllib.request.urlopen("http://www.baidu.com")
print(type(response))
print("*"*50)
print(response.read().decode('utf-8'))

很容易的就完成了百度首頁的抓取,輸出了網頁的源代碼。同時,我們發現這個響應是一個HTTPResponse類型的對象,主要包含read(),readinto(),getheader(name),getheaders()等方法,以及msg,version,status,reason等屬性。例如:調用read()方法可以得到返回的網頁內容;調用getheaders()方法可以得到響應的頭信息;調用getheader(name)方法可以得到響應頭中的name值!

大致演示下某些常用方法的實際使用:

from urllib import request
test_url_get = "http://httpbin.org/get" #測試網站
response=request.urlopen(test_url_get,timeout=10,data=None) #有data即為post請求;無data(None)即為get請求
print("狀態碼:",response.getcode()) #獲得狀態碼
print("響應頭信息:",response.info()) #顯示響應頭信息
print("獲取內容:",response.read()) #取出內容
# 需要注意:(如果你這樣搞,服務端一眼就看出來你圖謀不軌,不是正經的客戶端了!)
# 如果是浏覽器直接訪問測試網站:"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
#如果是爬蟲獲取: "User-Agent": "Python-urllib/3.8"
# 一個小重點: read只能read一次,再read就獲取不到信息咯
print("第二次:",response.read()) #輸出:第二次: b''
# 獲取百度首頁
baidu_url = "http://www.baidu.com"
response_baidu = request.urlopen(baidu_url)
print("百度首頁的內容:",response_baidu.read().decode())

(2)高級使用:如果想給鏈接傳遞一些參數,就要用到urlopen()函數的其他參數!

# 源碼如下:
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
*, cafile=None, capath=None, cadefault=False, context=None):

看函數源碼肯定要看看這個函數對應的三大特性:

  1. 功能:urlopen是一個發送簡單網絡請求的方法,然後返回結果。
  2. 參數:
    url: 必選;可以是一個字符串或一個 Request 對象。
    data: None–GET請求;有數據(字節二進制類型數據/文件對象/可迭代對象)–POST請求(POST請求的話,data數據會放進form表單進行提交);
    timeout:有默認設置;以秒為單位,例:設置timeout=0.1 超時時間為0.1秒(如果超出這個時間就報錯!)
  3. 返回值:urlib庫中的類或者方法,在發送網絡請求後,都會返回一個urllib.response的對象。它包含了請求回來的數據結果。它包含了一些屬性和方法,供我們處理返回的結果。
①data參數

data參數是可選的,如果需要添加該參數,要使用bytes()方法將參數轉化為字節流編碼格式的內容,即bytes類型。並且,此時它的請求方式也不再是GET方式,而是POST方式(我們傳遞的參數出現在form字段中,表明是模擬了表單提交的方式!)。

import urllib.request,urllib.parse
data = bytes(urllib.parse.urlencode({
'word':'hello'}),encoding='utf-8')
response = urllib.request.urlopen('http://httpbin.org/post', data=data)
print(response.read())

使用bytes()方法,將參數轉碼為bytes(字節流)類型。該方法第一個參數需要是str(字符串)類型,需要用urllib.parse模塊裡的urlencode()方法來將參數字典轉化為字符串;第二個參數指定編碼格式。

②timeout參數

timeout參數用於設置超時時間,單位是秒,意思是如果請求超出了設置的這個時間,還麼有得到響應,就會拋出URLError異常,該異常屬於urllib.error模塊,錯誤原因是超時。如果不指定,就會使用全局默認時間。

用武之地:
可以通過設置這個超時時間來控制一個網頁如果長時間未響應, 就跳過它的抓取。這可以利用try except語句來實現。如下:

import socket
import urllib.request
import urllib.error
try:
response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.01)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout): # 判斷異常是socket.timeout類型(意思就是超時異常),從而得出它確實是因為超時而報錯!
print('TIME OUT') # 按照常理來說。0.01秒內基本不可能得到服務器響應,因此輸出了TIME OUT提示。

2.Request()

urlopen()方法可以實現最基本請求的發起,但是不足以構建一個完整的請求。如果請求中需要加入headers等信息,就要使用更強大的Request類來構建!

老樣子,來看看三大特性:

上源碼:
def __init__(self, url, data=None, headers={
},
origin_req_host=None, unverifiable=False,
method=None):
  1. 功能:Request是一個構造完整網絡請求的對象,然後返回請求對象。
  2. 參數
  • url:必選;是一個字符串
  • data:bytes(字節流)類型。如果它是字典,可以先用urllib.parse模塊裡的urlencode()編碼
  • headers:是一個字典,他就是請求頭。我們可以在構造請求時通過headers參數直接構造,也可以通過調用請求實例的add_header()方法添加。
  • origin_req_host:請求方的host名稱或者IP地址
  • unverifiable:表示這個請求是否是無法驗證的,默認是False,意思就是用戶沒有足夠權限來選擇接收這個請求的結果。例如:我們請求一個HTML文檔中的圖片,但是我們麼有自動抓取圖像的權限,這時unverifialbe值就是True
  • method:默認GET,可填寫 POST,PUT,DELETE等
  1. 返回值:一個請求對象

(1)簡單使用

①初體驗
import urllib.request
request = urllib.request.Request('https://www.baidu.com')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))

其實,依然使用的是urlopen()方法來發送這個請求,只不過這次該方法的參數不再是URL,而是一個Request類型的對象。通過構造這個數據結構,一方面我們可以將請求獨立成一個對象,另一方面可以更豐富靈活的配置參數。

②實戰—靈活運用參數
實戰一之請求頭添加!

通過urllib發送的請求會有一個默認的Headers: “User-Agent”:“Python-urllib/3.6”,指明請求是由urllib發送的。所以遇到一些驗證User-Agent的網站時,需要我們自定義Headers把自己偽裝起來。

from urllib import request
img_url = "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=261409204,1345114629&fm=26&gp=0.jpg"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"}
req = request.Request(url=img_url,headers=headers) #構建Request對象 作用:向服務端發送請求時,User-Agent改變成了我們定義的這個,簡單偽裝!
# response = request.urlopen(url=img_url) #普通方法
response = request.urlopen(req) #添加了Hearders的高級一點的方法
data = response.read()
with open("cat.jpg","wb") as f:
f.write(data)
# 我們可以來查看一下我們是否真正改變了請求頭信息
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"}
test_url = "http://httpbin.org/get"
req = request.Request(url=test_url,headers=headers)
res_test = request.urlopen(req)
print(res_test.read()) #在直接使用urlopen時:"User-Agent": "Python-urllib/3.8"
#但是我們現在使用了Request偽裝,現在就是我們所設置的

實戰二之請求頭添加以及POST請求方式的使用!
import urllib.request,urllib.parse
from fake_useragent import UserAgent
url = 'http://httpbin.org/post'
headers = {

'User-Agent':UserAgent().random
}
dict = {

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

(2)高級用法

上面我們雖然可以構造請求,但是對於一些更高級的操作(比如Cookies處理,代理設置等),該怎麼辦?
這就需要更強大的工具Handler!簡而言之,我們可以把它理解為各種處理器,有專門處理登錄驗證的,有處理Cookies的,有處理代理設置的。利用他們,我們幾乎可以做到一切HTTP請求中所有的事情。

第一部分:簡介!

urllib.request模塊裡的BaseHandler類是所有其他Handler 的父類,提供了最基本的方法。下面是舉例的各種繼承了這個BaseHandler類的Handler子類:

類名功能HTTPDefaultErrorHandler用於處理HTTP響應錯誤,錯誤都會拋出HTTPError類型的異常。HTTPRedirectHandler用於處理重定向。HTTPCookieProcessor用於處理Cookies。ProxyHandler用於設置代理,默認代理為空。HTTPPasswordMgr用於管理密碼,它維護了用戶名和密碼的表。HTTPBasicAuthHandler用於管理認證,如果一個鏈接打開時需要認證,那麼可以用它來解決認證問題。

另一個比較重要的類就是OpenerDirector,我們稱為Opener。之前用過urlopen()這個方法,實際上就是urllib為我們提供的一個Opener。
我們引入Opener,就是因為需要實現更高級的功能。之前使用的Request和urlopen()相當於類庫為你封裝好了極其常用的請求方法,利用它們就可以完成基本的請求,但是現在我們需要通過Handler實現更高級的功能,就要深入一層進行配置,使用更底層的實例來完成操作,這就用到了Opener(簡言之:就是利用Handler來構建Opener)。Opener可以使用open()方法,返回的類型和urlopen()一樣。

第二部分:實戰!

  1. 驗證—有些網站在打開時就會彈出提示框,直接提示你輸入用戶名和密碼,驗證成功才能查看頁面。
from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
from urllib.error import URLError
username = 'username'
password = 'password'
url = 'url'
p = HTTPPasswordMgrWithDefaultRealm()
p.add_password(None, url, username, password)
# 首先,實例化HTTPBasicAuthHandler對象,參數是HTTPPasswordMgrWithDefaultRealm對象,它利用add_password()添加進去用戶名和密碼,
# 這樣就建立了一個處理驗證的handler。
auth_handler = HTTPBasicAuthHandler(p)
# 然後,使用這個Handler並使用build_opener()方法構建一個Opener,這個Opener在發送請求時就相當於已經驗證成功了!
opener = build_opener(auth_handler)
try:
# 最後,使用Opener的open()方法打開鏈接,就可以完成驗證了!
result = opener.open(url)
html = result.read().decode('utf-8')
except URLError as e:
print(e.reason)

  1. 對於Cookies的處理。

首先,獲取網站的Cookies。

import http.cookiejar,urllib.request
# 首先,聲明一個CookieJar對象
cookie = http.cookiejar.CookieJar()
# 然後,利用HTTPCookieProcessor構建一個Handler
handler = urllib.request.HTTPCookieProcessor(cookie)
# 最後,利用build_opener構建Opener
opener = urllib.request.build_opener(handler)
# 執行open()函數即可!
response = opener.open('http://www.baidu.com')
for item in cookie:
print(item.name+"="+item.value)


拓展:輸出成文件格式,即以文本形式保存!

import http.cookiejar,urllib.request
filename = 'cookies.txt'
# 此時,CookieJar就需要換成MozillaCookieJar,它在生成文件時會用到,是CookieJar的子類,
# 可以用來處理Cookie和文件相關的事件,比如讀取和保存Cookie,可以將Cookies保存成
# Mozilla型浏覽器的Cookie格式。
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)
“
如果改為
cookie = http.cookiejar.LWPCookirJar(filename)
那麼會保存成libwww-perl(LWP)格式的Cookies文件!
”


那麼,生成了Cookies文件後,怎樣從文件中讀取利用呢?(以LWPCookieJar格式為例)

import http.cookiejar,urllib.request
cookie = http.cookiejar.LWPCookieJar()
# 調用load()方法來讀取本地的Cookies文件,獲取到了Cookies的內容(注意前提一定要生成並保存了),然後獲取Cookies之後使用同樣的方法構建Handler和Opener即可!
cookie.load('cookies.txt', ignore_expires=True, ignore_discard=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))

  1. 設置代理:

運行爬蟲的時候,經常會出現被封IP的情況,這是我們就需要使用ip代理來處理,urllib的IP代理的設置如下:

from urllib import request
url = "http://httpbin.org/ip"
# 代理地址
proxy = {
"http":"域名:端口號"}
# 代理處理器
proxies = request.ProxyHandler(proxy)
# 創建openner對象
openner = request.build_opener(proxies)
res = openner.open(url)
print(res.read().decode())

二、error模塊:處理異常

error模塊主要負責處理異常,如果請求出現錯誤,我們可以用error模塊進行處理。
主要包含URLError和HTTPError

1.URLError

URLError類來自urllib庫的error模塊,它繼承自OSError類,是error異常模塊的基類,由request模塊產生的異常都可以通過捕獲這個類來處理。(屬性reason:返回錯誤的原因。)

from urllib import request,error
try:
response = request.urlopen('http://www.baidu.com/peter')
except error.URLError as e:
print(e.reason)

我們打開一個不存在的頁面,本來該報錯,但是我們捕獲了這個URLError異常。

2.HTTPError

它是URLError的子類,專門用來處理HTTP請求錯誤,比如認證請求失敗等。它有如下三個屬性:

  1. code:返回HTTP狀態碼;
  2. reason:同父類一樣,返回錯誤的原因;
  3. headers:返回請求頭。

因為URLError是HTTPError的父類,所以可以先選擇捕獲子類的錯誤,如果不是HTTPError異常,再去捕獲父類的錯誤:

from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')

拓展:注意!有時候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):
print('TIME OUT !')

三、parse模塊:解析鏈接

urllib庫裡還提供了parse模塊(是一個工具模塊),它定義了處理URL的標准接口,例如:實現URL各部分的抽取、合並以及鏈接轉換。

(1-1)urlparse()

①基本使用:

該方法可以實現URL的識別和分段。

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html;user?id=5#comment')
print(type(result), result)

返回的結果是一個ParseResult類型的對象,urlparse()方法將其拆分成了6個部分:
/前面的就是scheme,代表協議;第一個/符號前面的便是netloc,即域名;後面是path,即訪問路徑;分號;後面的是params,代表參數;問號?後面是查詢條件query,一般用作GET類型的URL;井號#後面的是錨點,用於直接定位頁面內部的下拉位置。(更多詳解請看本篇:一篇萬字博文帶你入坑爬蟲這條不歸路 【萬字圖文】)
可得一個標准的鏈接格式:

scheme://netloc/path;params?query#fragment

②高級使用:

urlparse()的API用法:

# 源碼:
def urlparse(url, scheme='', allow_fragments=True):
# 三個參數講解:
“
1.url:必填項,即待解析的URL;
2.scheme:默認的協議(比如http或https等)。scheme參數只有在URL中不包含scheme信息時才生效。如果URL中有scheme信息,就會返回解析出的scheme。
3.allow_fragments:即是否忽略fragment。如果它被設置為False,fragment部分就會被忽略,會被解析為path,parameters或者query的一部分,而fragment部分為空。
”

舉例看:

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html;user?id=5#comment', allow_fragments=False)
print(result)


假設URL中不包含params和query,再試試:
可以發現,fragment會被解析為path的一部分;
而且,返回結果ParseResult實際上是一個元組,可以用索引順序或者屬性名獲取!

from urllib.parse import urlparse
result = urlparse('http://www.baidu.com/index.html#comment', allow_fragments=False)
print(result)
print(result.scheme, result[0], sep='\n')

(1-2)urlunparse()

urlparse()的對立方法urlunparse(),接收的參數是一個可迭代對象,長度必須是6,否則會報錯!

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

此處的參數data用了列表類型,當然也可以使用其他類型,比如元組或者特定的數據結構!

(2-1)urlsplit()

和urlparse()方法非常相似,但是不再單獨解析params,它只返回5個結果,上面例子中的params會合並到path中。(常用!)如下:

from urllib.parse import urlsplit
result = urlsplit('http://www.baidu.com/index.html;user?id=5#comment')
print(result)
# 它的返回結果是SplitResult,是一個元組類型,可以用屬性及索引獲取!
print('返回結果是:',type(result))
print(result.scheme, result[0], sep='\n')

(2-2)urlunsplit()

與urlunparse()類似,它也是將鏈接各個部分組合成完整鏈接的方法,傳入的參數也是一個可迭代對象,唯一的區別是長度必須是5!

from urllib.parse import urlunsplit
data = ['http', 'www.baidu.com', 'index.html;user', 'a=6', 'comment']
print(urlunsplit(data))

(3)urljoin()

上述的urlunparse()和urlunsplit()方法可以完成鏈接的合並,但是前提是必須有特定長度的對象,鏈接的每一部分都要清晰分開。
這就要用到生成鏈接的另一個方法—urljoin()方法。我們可以提供一個base_url(基礎鏈接)作為第一個參數,將新的鏈接作為第二個參數,該方法會分析base_url的scheme,netloc和path這3個內容並對新鏈接缺失的部分進行補充,最後返回結果。

from urllib.parse import urljoin
print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com', 'https://pythonsite.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://pythonsite.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://pythonsite.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://pythonsite.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))

可以發現,base_url提供了三項內容scheme,netloc和path。如果這三項在新的鏈接裡不存在,就予以補充;如果存在,就使用新鏈接裡的!

一句話總結—從拼接的結果我們可以看出,拼接的時候後面的優先級高於前面的url

(4-1)urlencode()

在發送請求的時候,往往會需要傳遞很多的參數,如果用字符串方法去拼接會比較麻煩,parse.urlencode()方法就是用來拼接url參數的。(將字典格式轉換成url請求參數)
也可以通過parse.parse_qs()方法將它轉回字典

此方法在構造GET請求參數的時候很有用!

# 單個示范
params = parse.urlencode({
"name":"美女","name2":"帥哥","name3":"姐姐"}) #字典格式轉換為url請求參數
print(params)
print("轉換回來:",parse.parse_qs(params)) #url請求參數轉換為字典格式
# 實際使用
test_url2 = "http://httpbin.org/get?{}".format(params)
res2 = request.urlopen(test_url2)
print(res2.read())

(4-2)parse_qs()

有了序列化,必然就有反序列化。可將一串GET請求參數,利用parse_qs()方法將其轉回字典!

from urllib.parse import parse_qs
query = 'name=peter&age=22'
print(parse_qs(query))

(5)parse_qsl()

用於將參數轉化為元組組成的列表。

from urllib.parse import parse_qsl
query = 'name=peter&age=22'
print(parse_qsl(query))

(6-1)quote()

功能:url中只能包含ascii字符,在實際操作過程中,get請求通過url傳遞的參數中會有大量的特殊字符,例如漢字,那麼就需要進行url編碼。
利用parse.unquote()可以反編碼回來。

from urllib.parse import quote
keyword = "壁紙"
url = 'http://www.baicu.com/s?wd=' + quote(keyword)
print(url)

(6-2)unquote()

用於將URL解碼。

from urllib.parse import unquote
url = 'http://www.baicu.com/s?wd=%E5%A3%81%E7%BA%B8'
print(unquote(url))

四、robotparse模塊:分析Robots協議

robotparse模塊主要負責處理爬蟲協議文件,robots.txt.的解析。(君子協定)

(1)Robots協議

  • Robots協議也稱作爬蟲協議,機器人協議,全稱是“網絡爬蟲排除標准”(Robots Exclusion Protocol),用來告訴爬蟲和搜索引擎哪些頁面可以抓取,哪些不可以抓取。通常是一個robots.txt的文本文件,一般放在網站的根目錄下。

比如:百度的robots協議:
http://www.baidu.com/robots.txt即可查看。

  • 當搜索爬蟲訪問一個站點時,他首先會檢查這個站點根目錄下是否有robots.txt文件,如果存在,搜索爬蟲會根據其中定義的爬取范圍來爬取。如果沒有,搜索爬蟲便會訪問所有可直接訪問的頁面。

  • robots.txt文件是一個文本文件,使用任何一個常見的文本編輯器,比如Windows系統自帶的Notepad,就可以創建和編輯它 。robots.txt是一個協議,而不是一個命令。robots.txt是搜索引擎中訪問網站的時候要查看的第一個文件。robots.txt文件告訴蜘蛛程序在服務器上什麼文件是可以被查看的。

舉例:下述Robots協議實現了對所有搜索爬蟲只允許爬取public目錄的功能。

User-agent:*
Disallow: /
Allow: /public/
  1. User-agent描述了搜索爬蟲的名稱,此處設置為*則代表該協議對任何爬取爬蟲有效。可以有多條,至少有一條!
  2. Disallow指定了不允許抓取的目錄,此處設置為/則代表不允許抓取所有的頁面。
  3. Allow一般和Disallow一起使用,一般不單獨使用,用來排除某些限制。此處設置為/public/,代表所有頁面不允許抓取,但可以抓取public目錄。

(2)robotparser

①簡單講解:

  • 可以使用robotparser模塊來解析robots.txt,該模塊提供了一個類RobotFileParser,它可以根據某網站的robots.txt文件來判斷一個爬蟲是否有權限來爬取這個網頁。

  • 使用方法,只需在構造方法裡傳入robots.txt的鏈接即可。當然,也可在聲明的時候不傳入,默認為空,最後再使用set_url()方法設置也可。下述是它的聲明:

# 源碼:
def __init__(self, url=''):
  • 該模塊常用方法:
方法功能set_url()設置robots.txt文件的鏈接;read()讀取robots.txt文件並進行分析,該方法不會返回結果,但對文件進行了讀取操作,這一步必須調用,如果不調用,則接下來的判斷均為False;parser()解析robots.txt文件,傳入的參數是robots.txt某些行的內容,它會按照robots.txt的語法規則來分析這些內容;can_fetch()第一個參數為User_agent,第二個參數為要抓取的url,判斷該搜索引擎是否可抓取該url,返回的結果是True或False;mtime()返回上次抓取和分析robots.txt協議的時間(你可能需要定期檢查來抓取最新的robots.txt);modified()將當前時間設置為上次抓取和分析的時間。

②靈活使用:

from urllib.robotparser import RobotFileParser
# 首先,創建RobotFileParser對象
rp = RobotFileParser()
# 然後,通過set_url()方法設置了robots.txt的鏈接
rp.set_url('http://www.jianshu.com/robots.txt')
rp.read()
# 利用can_fetch()方法判斷了網頁是否可以被抓取
print(rp.can_fetch('*', 'http://www.jianshu.com/p'))
print(rp.can_fetch('*', 'http://www.jianshu.com/p/b67554025d7dsdfsdf'))

同樣可以使用parse()方法執行讀取和分析:

from urllib.robotparser import RobotFileParser
from urllib.request import urlopen
# 首先,創建RobotFileParser對象
rp = RobotFileParser()
rp.parse(urlopen('http://www.jianshu.com/robots.txt').read().decode('utf-8').split('\n'))
# 利用can_fetch()方法判斷了網頁是否可以被抓取
print(rp.can_fetch('*', 'http://www.jianshu.com/p'))
print(rp.can_fetch('*', 'http://www.jianshu.com/p/b67554025d7dsdfsdf'))

第二部分:urllib3庫

urllib3 是一個基於python3的功能強大,友好的http客戶端。越來越多的python應用開始采用urllib3.它提供了很多python標准庫裡沒有的重要功能(線程安全,連接池)。

(1)request方法:

源碼:

request(self, method, url, fields=None, headers=None,**urlopen_kw)
  1. 功能:發送完整的網絡請求
  2. 參數:
    method:請求方法 GET ,POST…
    url:字符串格式
    fields:字典類型 GET請求時轉化為url參數;POST請求時會轉化成form表單數據
    headers:字典類型
  3. 返回值:response對象
import urllib3
import json
http=urllib3.PoolManager() #實例化一個連接池對象 保持socket開啟。這個對象處理了連接池和線程安全的所有細節,所以我們不用自行處理!
test_url = "http://httpbin.org/get"
# 添加頭
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"}
#參數fileds使用:如果是get就將fields這個字典轉換為url參數;如果是post就將fields這個字典轉換為form表單數據。
#對於put和post請求,需要提供字典類型的參數field來傳遞form表單數據。如何獲取JSON響應格式數據內的特定信息,借鑒小拓展部分!
data_dict={
"name1":"allen","name2":"rose"}
res = http.request("GET",test_url,headers=headers,fields=data_dict) #功能:發送完整的網絡請求
print(res.data) #獲取內容
print("狀態碼",res.status) #狀態碼
print("頭部信息",res.headers) #響應頭信息

如果是POST請求,會將fields對應的字典轉換為form表單數據:

小拓展:使用json模塊的loads方法獲取特定信息

在上述代碼加入:

data = res.data.decode() #將字節數據轉換為utf-8格式
print(json.loads(data)["args"]["name1"]) #json.loads()將json數據轉換為字典,這樣可以進行特定信息的獲取
#輸出為:allen

(2)發送json數據:

當我們需要發送json數據時,我們需要在request中傳入編碼後的二進制數據類型的body參數,並制定Content-Type的請求頭:

import json
import urllib3
http=urllib3.PoolManager() #實例化一個連接池對象 保持socket開啟。這個對象處理了連接池和線程安全的所有細節,所以我們不用自行處理!
data = {
'attribute': 'value'}
encoded_data = json.dumps(data).encode('utf-8')
r = http.request('post', 'http://httpbin.org/post',body=encoded_data,
headers={
'Content-Type':' application/json'})
print(json.loads(r.data.decode('utf-8'))['json'])

(3)文件上傳:

對於文件上傳,我們可以模仿浏覽器表單的方式:

import json
import urllib3
http=urllib3.PoolManager() #實例化一個連接池對象 保持socket開啟。這個對象處理了連接池和線程安全的所有細節,所以我們不用自行處理!
with open('test.txt') as f:
file_data = f.read()
r = http.request('post', 'http://httpbin.org/post',
fields={

'filefield': ('test.txt', file_data)
})
print(json.loads(r.data.decode('utf-8'))['files'])

(只做模擬,所以文件是空文件)

(4)二進制數據的上傳:

對於二進制的數據上傳,我們用指定body的方式,並設置Content-Type的請求頭:

import json
import urllib3
http=urllib3.PoolManager() #實例化一個連接池對象 保持socket開啟。這個對象處理了連接池和線程安全的所有細節,所以我們不用自行處理!
with open('girl.jpg', 'rb') as f:
binary_data = f.read()
r = http.request('post', 'http://httpbin.org/post',
body=binary_data,
headers={
'Content-Type': 'image/jpeg'})
print(json.loads(r.data.decode('utf-8'))['files'])

(只做模擬,所以圖片也是空的)

(5)設置代理:

可以利用ProxyManager進行http代理操作:

import urllib3
proxy = urllib3.ProxyManager('http://111.11.111.11:1111')
res = proxy.request('get', 'http://httpbin.org/ip')
print(res.data)

可通過點擊下面——>關注本人運營 公眾號

【可以公眾號裡私聊並標明來自CSDN,會拉你進入技術交流群(群內涉及各個領域大佬級人物,任何問題都可討論~)--->互相學習&&共同進步(非誠勿擾)】

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved