urllib:Python爬蟲必學(xué)的網(wǎng)絡(luò)庫,收藏這一篇就Go了
網(wǎng)絡(luò)庫urllib
urllib庫是Python3內(nèi)置的HTTP請求庫,不需要單獨(dú)安裝,默認(rèn)下載的Python就已經(jīng)包含了該庫。
urllib庫有4個(gè)模塊:
request:最基本的HTTP請求模塊,可以用法發(fā)送HTTP請求,并接收服務(wù)器的響應(yīng)數(shù)據(jù),這個(gè)過程就像在瀏覽器地址欄輸入U(xiǎn)RL一樣。
parse:工具模塊,提供了很多處理URL的API,如拆分、解析、合并等。
robotparser:用來識別網(wǎng)站的robots.txt文件,然后判斷哪些網(wǎng)站可以抓取,哪些不能抓取。
error:異常處理。如果出現(xiàn)請求錯(cuò)誤,可以捕獲這些異常,然后根據(jù)代碼的需要,進(jìn)行處理。
下面,我們來分別介紹urllib庫這4個(gè)模塊。
request
request模塊,包含發(fā)送請求,獲得響應(yīng),Cookie,代理等相關(guān)API。這里,我們分別來介紹其使用的方式。
發(fā)送GET請求
首先,我們一般進(jìn)行爬蟲程序編寫時(shí),開始都需要發(fā)送請求,然后獲得響應(yīng)進(jìn)行處理。比如通過GET請求獲取網(wǎng)頁源代碼,示例如下:
import urllib.request response=urllib.request.urlopen("https://www.csdn.net/") print(response.read().decode("UTF-8"))
如上面代碼所示,運(yùn)行之后,我們會得到網(wǎng)頁html源代碼。
這里response是一個(gè)HTTPResponse對象,調(diào)用它多種方法及其屬性,就可以進(jìn)行多樣的處理。
比如,我們這里來獲取CSDN首頁,調(diào)用其屬性及其方法,示例如下:
import urllib.request response = urllib.request.urlopen("https://www.csdn.net/") print("status:", response.status, " msg:", response.msg, " version:", response.version) print(response.getheaders())
這里,我們輸出了CSDN首頁的響應(yīng)狀態(tài)碼,響應(yīng)消息以及HTTP版本的屬性,同時(shí)也打印其完整的響應(yīng)頭信息。
發(fā)送POST請求
默認(rèn)request.urlopen()函數(shù)發(fā)送的GET請求,如果需要發(fā)送POST請求,我們需要使用data命令參數(shù),該參數(shù)類型是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"))
運(yùn)行之后,如果請求成功就會返回一大堆字符串?dāng)?shù)據(jù)。
請求超時(shí)處理
在實(shí)際的爬蟲項(xiàng)目中,我們都需要考慮一個(gè)關(guān)鍵的問題,那就是請求超時(shí)。如果請求網(wǎng)址因?yàn)殚L時(shí)間沒有響應(yīng),那么卡在這里往往浪費(fèi)大量的資源。
所以,我們需要設(shè)置一個(gè)超時(shí)時(shí)間,如果在限定的時(shí)間沒有響應(yīng),就應(yīng)該重新抓取,或者不在抓取。示例代碼如下:
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("超時(shí)處理") finally: print("繼續(xù)爬蟲的工作")
爬蟲偽裝
現(xiàn)在只要是知名的網(wǎng)站,都具有定義的防爬蟲技術(shù)。所以,我們在開發(fā)爬蟲程序時(shí),需要將自己的請求偽裝成瀏覽器。
不過,urlopen()并沒有header請求頭參數(shù),我們需要使用request.Request()進(jìn)行構(gòu)造請求,示例代碼如下:
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"))
這里,我們將爬蟲程序偽裝成從火狐瀏覽器進(jìn)行的請求。
當(dāng)然,Request類還有許多其他的信息,下表是博主列出來供讀者參考使用的。
需要注意的是最后一個(gè)參數(shù)method,如果你在程序中,即指定了請求是GET,同時(shí)也有data表單數(shù)據(jù),那么默認(rèn)表單不會提交給服務(wù)器。
代理
其實(shí),服務(wù)器除了根據(jù)請求頭進(jìn)行判斷是否為爬蟲之外,最簡單判別爬蟲的方式,就是判斷是否是同一個(gè)IP短時(shí)間大量訪問。
如果同一個(gè)IP短時(shí)間大量訪問,那么可以直接判別為爬蟲程序。而這個(gè)時(shí)候,我們可以通過代理進(jìn)行偽裝。當(dāng)然,使用代理時(shí)應(yīng)該不斷更換代理服務(wù)器。
示例代碼如下:
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('https://www.csdn.net/') print(response.read().decode('UTF-8')) except urllib.error.URLError as e: print(e.reason)
運(yùn)行之后,與前文一樣返回CSDN首頁的源代碼。不過,需要注意的是,博主在寫博客時(shí),這個(gè)代碼IP是有效的,但如果你讀到這篇博文,可能已經(jīng)失效了,你需要自己取找免費(fèi)的代理服務(wù)器取測試。
獲取Cookie
一般來說,當(dāng)我們使用爬蟲登錄網(wǎng)站之后,會使用服務(wù)器返回的Cookie進(jìn)行操作,有了這個(gè)Cookie,服務(wù)器就知道我們已經(jīng)登錄過。
那么如果獲取Cookie數(shù)據(jù)呢?示例如下:
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('https://www.csdn.net/') for item in cookie: print("name=", item.name, " value=", item.value) except urllib.error.URLError as e: print(e.reason)
如上面代碼所示,我們可以通過http.cookiejar庫進(jìn)行操作,這里會返回cookie的鍵值對,并輸出打印,效果如下:
如果本身網(wǎng)站的Cookie時(shí)效比較長的話,那么一次爬蟲之后,我們可以將其保存在使用,這個(gè)時(shí)候會用到MozillaCookieJar類與LWPCookieJar類。
這2個(gè)類會在獲取Cookie的同一時(shí)間,將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('https://www.csdn.net/') cookie.save(ignore_discard=True,ignore_expires=True) except urllib.error.URLError as e: print(e.reason)
運(yùn)行之后,會生成一個(gè)cookies.txt文件,具體內(nèi)容如下:
而LWPCookieJar類的使用方式與上面MozillaCookieJar類似,這里不在贅述。至于讀取通過cookie.load()方法進(jìn)行加載Cookie。
Parse
在實(shí)際的爬蟲處理中,我們經(jīng)常會遇到各種的編碼問題。而這些問題就可以通過Parse進(jìn)行解決處理。
中文的編碼與解碼
比如,在某些網(wǎng)址的請求頭中,有些網(wǎng)站請求頭會含有中文。但默認(rèn)不進(jìn)行任何處理肯定會報(bào)UnicodeEncodeError錯(cuò)誤。
讀者可以通過上面的代碼,在headers字典中添加一個(gè)鍵值對,值是中文試試。下面,我們需要對其進(jìn)行編碼,示例如下:
import urllib.parse import base64 value = urllib.parse.urlencode({'name': '安踏'}) print("編碼后的中文:", value) print("解碼后的中文:", urllib.parse.unquote(value)) #base64編碼與解碼 base64Value=base64.b64encode(bytes('學(xué)習(xí)網(wǎng)絡(luò)urllib庫',encoding="UTF-8")) print("編碼后的中文:", base64Value) print("解碼后的中文:", str(base64.b64decode(base64Value),"UTF-8"))
運(yùn)行之后,效果如下:
需要注意的是,urlencode只能對url參數(shù)進(jìn)行編碼,比如這里對headers請求頭進(jìn)行了編碼。而網(wǎng)站中的數(shù)據(jù),有的是通過base64進(jìn)行編碼的,這個(gè)時(shí)候,我們也需要掌握base64的解碼與編碼。
quote與unquote
quote函數(shù)也是一個(gè)編碼函數(shù),但它與urlencode不同,urlencode只能對URL進(jìn)行編碼。而quote可以給任意字符串進(jìn)行編碼。
至于unquote是編碼的逆過程(解碼),示例如下:
from urllib.parse import quote, unquote url = 'https://www.baidu.com/s?wd=' + quote("王者榮耀") print(url) url = unquote(url) print(url)
運(yùn)行之后,效果如下:
URL解析
在前面介紹的時(shí)候,我們也說了parse模塊可以分解,分析,合并URL。下面,我們來通過這些函數(shù),舉一些列子,讀者就會明白了。
from urllib import parse url = 'https://www.csdn.net/' split_result=parse.urlsplit(url); print(split_result) result = parse.urlparse(url) print('scheme:', result.scheme) # 網(wǎng)絡(luò)協(xié)議 print('netloc:', result.netloc) # 域名 print('path:', result.path) # 文件存放路徑 print('query:', result.query) # 查詢字符 print('fragment:', result.fragment) # 拆分文檔中的特殊貓
運(yùn)行之后,效果如下:
連接URL
對于URL來說,有時(shí)候我們需要將URL的多個(gè)部分進(jìn)行組合。比如,我們在爬取某一個(gè)網(wǎng)站下圖片時(shí),其默認(rèn)的前部分基本都相同,可能只是ID不同。
那么這個(gè)時(shí)候,我們需要將前部分URL與ID進(jìn)行拼接。示例代碼如下:
import urllib.parse print(urllib.parse.urljoin('https://blog.csdn.net','liyuanjinglyj')) print(urllib.parse.urljoin('https://blog.csdn.net','https://www.baidu.com')) print(urllib.parse.urljoin('https://blog.csdn.net','index.html')) print(urllib.parse.urljoin('https://blog.csdn.net/index.php','?name=30'))
運(yùn)行之后,效果如下:
如果第2個(gè)參數(shù)不是一個(gè)完整的URL,會將第2個(gè)參數(shù)的值加到第1個(gè)參數(shù)的后面,自動(dòng)添加斜杠"/”。
如果第2個(gè)參數(shù)是一個(gè)完整的URL,那么直接返回第2個(gè)URL。
參數(shù)轉(zhuǎn)換(parse_qs與parse_qsl)
parse_qs函數(shù)將多個(gè)參數(shù)拆成字典的形式,key是參數(shù)名,value是參數(shù)值。
而parse_qsl函數(shù)返回一個(gè)列表,每個(gè)元素是一個(gè)包含2個(gè)元素值的元組,第1個(gè)元素表示key,第2個(gè)元素表示value。
示例如下:
from urllib.parse import parse_qs, parse_qsl query = "name=李元靜&age=29" print(parse_qs(query)) print(parse_qsl(query))
運(yùn)行之后,效果如下:
2個(gè)函數(shù)僅返回的參數(shù)形式不同,內(nèi)容基本都是匹配的。同時(shí),它們2個(gè)函數(shù)不管對什么類型,轉(zhuǎn)換結(jié)果都是字符串。
Robots協(xié)議
Robots協(xié)議也稱為爬蟲協(xié)議、機(jī)器人協(xié)議,它的全名是網(wǎng)絡(luò)爬蟲排除標(biāo)準(zhǔn)(Robots Exclusing Protocol)。
用來告訴爬蟲和搜索引擎哪些頁面可以抓取,哪些不可以抓取。該協(xié)議通常放在一個(gè)名為robots.txt的文本文件中,它位于網(wǎng)站的根目錄下。
特別注意,robots.txt文件僅僅是為了告訴開發(fā)中哪些數(shù)據(jù)可以抓取,哪些數(shù)據(jù)不可以,并不能阻止開發(fā)中進(jìn)行爬蟲操作。但作為一個(gè)有良知的程序員,應(yīng)盡量尊重這些規(guī)則。
Robots協(xié)議的定義規(guī)則
robots.txt的文本文件一般有3個(gè)值:
User-agent:如果它為*,表示對所有爬蟲有效
Disallow:哪個(gè)目錄下的資源不能抓取,比如Disallow:/index/,那么禁止爬取index文件中的所有資源。如果為/,表示該網(wǎng)站都不能抓取。
Allow:表示哪個(gè)目錄下的資源可以抓取。比如Allow:/,表示所有網(wǎng)站都可以抓取。值與Disallow一樣,只是反意義。
當(dāng)然,User-agent還可以禁止某些爬蟲抓取,比如禁止百度爬蟲抓取:User-agent:BaiduSpider。常用的爬蟲名如下表:
Robots協(xié)議的解析
當(dāng)然,我們獲取Robots協(xié)議,肯定是為了獲取哪些資源不可爬。但是,直接通過字符串進(jìn)行解析雖然也能完成,不過稍微復(fù)雜了點(diǎn)。
而urllib庫的robotparser模塊提供了相應(yīng)的API進(jìn)行解析,也就是RobotFileParser類。示例如下:
from urllib.robotparser import RobotFileParser #一種解析方式 robot = RobotFileParser() robot.set_url('https://www.csdn.net/robots.txt') robot.read() print(robot.can_fetch('*', 'https://blog.csdn.net/rank/list')) #另一種解析方式 robot = RobotFileParser('https://www.csdn.net/robots.txt') print(robot.can_fetch('*', 'https://blog.csdn.net/rank/list'))
這里,can_fetch方法用來判斷該網(wǎng)址根據(jù)Robots協(xié)議是否可以抓取。這里判斷的是CSDN排行榜能否抓取,返回了True。
error
異常處理在上面的代碼中,我們已經(jīng)用過了。比如在請求超時(shí)的講解之中,我們用到了URLError。但是它還有一個(gè)子類HTTPError。
HTTPError
它主要用于HTTP請求錯(cuò)誤的處理,比如400,404等錯(cuò)誤。
HTTPError有3個(gè)重要的參數(shù):
code:服務(wù)器返回的狀態(tài)碼,如404等
reason:返回錯(cuò)誤的原因
headers:返回請求頭
不過,HTTPError不能捕獲超時(shí)錯(cuò)誤,如果你需要處理超時(shí)錯(cuò)誤,那么只能通過URLError進(jìn)行捕獲。
HTML Python 爬蟲
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。