urllib庫是Python內置的HTTP請求庫,它包含了4個模塊:php
request:最基本的HTTP請求模塊,用來模擬發送請求html
error:異常處理模塊。出現請求錯誤後,咱們能夠捕獲異常,而後進行下一步的操做。python
parse:工具模塊。提供了不少URL處理方法。瀏覽器
robotparse:主要用來識網站的robots.text文件,用的比較少服務器
urllib的request模塊能夠幫助咱們方便的發送請求並獲得響應。下面咱們來看一下用法:socket
urlopen()參數有url、data、timeout、context、cafile、capath、cadefault,咱們只詳細瞭解三個,他們比較重要:工具
url:要請求的網址post
下面咱們以Python官網爲例,咱們來把它抓取下來:測試
import urllib.request
response = urllib.request.urlopen("https://www.python.org/")
print(response)
咱們充滿期待的想要看到網頁內容,卻發現是這樣:fetch
顯而易見,咱們獲得的結果是一個HTTPResponse類的對象,咱們想要獲得它的內容得使用它的read()方法,另外read()獲得的是bytes類型的數據,咱們將它解碼,方便觀看
import urllib.request
response = urllib.request.urlopen("https://www.python.org/") print(response.read().decode("UTF-8"))
此次獲得的結果,如咱們所願,不就是網頁的源代碼嘛:
下來咱們說一下這個HTTPResponse類型的對象,他有不少屬性和方法,
https://www.cnblogs.com/kissdodog/archive/2013/02/06/2906677.html 這個博客中有詳細說明,我就很少提了^_^
data:data參數是可選的,但必須給bytes類型,一旦設置data參數,請求的方式就會變成POST,若是不設置則默認爲GET
咱們經過代碼看一下:
import urllib.parse import urllib.request
data = bytes(urllib.parse.urlencode({"word":"hello"}),encoding="UTF-8") #咱們藉助urllib庫的parse中的urlencode方法將字典轉化爲字符串 response = urllib.request.urlopen("http://httpbin.org/post",data=data) #這個網站用來作HTTP請求測試,咱們用來進行POST請示測試 print(response.read().decode("UTF-8"))
咱們來看一下結果:
咱們能夠很清楚的看到咱們傳進的data參數出如今了form裏面,咱們知道只有POST請求才會把數據封裝到form表單中。data設置的參數會被封裝到form表單中,封裝到請求頭中傳遞給服務器。
timeout:設置超時時間,單位爲秒,當超過這個時間,請求沒有獲得響應,就會拋出異常。
import urllib.request
response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.1) #咱們將這個時間設置的短一點,請求不可能這麼快獲得響應,這樣就會拋出異常 print(response.read())
果真不出所料:
可是,咱們都知道Python有try except異常處理機制,咱們在這裏能夠用到
import socket import urllib.request import urllib.error
try: response = urllib.request.urlopen("http://httpbin.org/get",timeout=0.1) except urllib.error.URLError as e: if isinstance(e.reason,socket.timeout): print("超時了")
因此咱們經過設置timeout,一旦超時進行異常處理,這樣能很好的提升爬取速率
context:用來指定SSL設置。
cafile:CA證書。
capath:CA證書的路徑。
cadefault:如今已棄用,默認值爲false。
咱們能夠利用urlopen()方法來發送請求,可是urlopen()中幾個參數並不能構成一個完整的請求。咱們須要藉助Request類構建對象,對象中能夠攜帶Headers等信息,從而實現完整的請求。
咱們先來看一下Request的用法:
import urllib.request
request = urllib.request.Request("https://python.org") print(type(request)) response = urllib.request.urlopen(request) #咱們仍然使用urlopen()來發送請求,可是方法的參數不是URL,而是一個request對象,咱們將參數獨立成對象,能夠靈活的進行參數配置。 print(response.read().decode("UTF-8"))
下面咱們看一下Request對象的參數有哪些:
url:用於請求的URL,必傳參數,其餘都是選傳。
data:必須傳bytes類型的。若是是字典,咱們能夠藉助urllib.parse模塊中的urlencode()進行編碼。
headers:請求頭,是一個字典。咱們能夠直接構造,也能夠經過調用add_header()方法添加
ps:請求頭中最常改的就是User-Agent,默認的User-Agent爲Python-urllib,咱們能夠經過修改它來僞造瀏覽器。好比咱們要假裝爲火狐瀏覽器,能夠設置爲:
Mozilla/4.0 (compatible; MSIE 5.5; Window NT)
origin_req_host:請求放的host名稱或IP地址
unverifiable:用戶用沒有權限來選擇接收請求的結果。默認爲False,若是沒有權限賊爲True,不然爲False。
method:請求使用的方法,如GET、POST和PUT
話很少說,咱們來看一下:
from urllib import request,parse url = "http://httpbin.org/post" headers = { "User-Agent":"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT", "Host":"httpbin.org" } dict = { "name":"Germey" } data = bytes(parse.urlencode(dict),encoding="UTF-8") req = request.Request(url=url,data=data,headers=headers,method="POST") response = request.urlopen(req) print(response.read().decode("UTF-8"))
明顯的看到,咱們成功的設置了headers,method,data。
headers信息咱們還可使用add_headers來添加
from urllib import request,parse url = "http://httpbin.org/post" dict = { "name":"Germey" } data = bytes(parse.urlencode(dict),encoding="UTF-8") req = request.Request(url=url,data=data,method="POST") req.add_header("User-Agent","Mozilla/4.0 (compatible; MSIE 5.5; Windows NT") #add_headers(key,value) response = request.urlopen(req) print(response.read().decode("UTF-8"))
咱們已經瞭解了怎樣發送請求,可是請求不是每次都能獲得響應,可能會出現異常,不去處理它程序可能所以停掉,因此頗有必要去處理異常。
urllib的error模塊定義了由request模塊產生的異常。若是出現問題,request模塊會拋出error模塊中定義的異常。
1.URLError
URLError類來自error模塊,是error異常模塊的基類,由request模塊產生的異常均可以經過捕獲這個類來處理,它的屬性reason能夠返回錯誤緣由。
from urllib import request,error try: response = request.urlopen("https://cuiqingcai.com/index.htm") #咱們打開的頁面不存在 except error.URLError as e: print(e.reason)
咱們打開的頁面不存在,按理說會報錯,可是咱們將異常捕獲,並輸出異常得緣由,經過這樣的操做,咱們能夠避免程序異常終止,並且異常獲得了很好的處理。
2.HTTPError
他是URLError的子類,專門用來處理HTTP請求錯誤。他有三個屬性:
code:返回HTTP狀態碼,好比404表示頁面不存在,500表示服務器內部出錯
reason:返回錯誤的緣由
headers:返回請求頭
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")
一樣的網頁,咱們經過捕獲HTTPError來得到reson,code和headers屬性。由於URLError是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 try: response = request.urlopen("http://cuiqingcai.com/index.htm",timeout=0.01) except error.HTTPError as e: print(e.reason,e.code,e.headers,sep="\n") print(type(e.reason)) except error.URLError as e: print(e.reason) print(type(e.reason)) else: print("Request Successfully")
咱們能夠很清楚的看到e.reson是一個socket.timeout對象,咱們簡單的記住就好了,請求超時後,返回的錯誤爲timed out,是socket.timeout類的對象
學了處理異常後,經過捕獲異常並處理,可使咱們的程序更加的穩健。
前面提到了urllib庫裏的parse模塊,他提供了不少處理連接的方法,實現了URL各部分的抽取、合併、以及連接轉化。
該方法能夠實現URL的識別和分段,共有三個參數,返回的結果是一個元組咱們能夠經過屬性或索引拿到相應的值
urlstring:待解析的URL,必選項
scheme:默認的協議,若是連接沒有帶協議會解析出默認協議,不然解析出連接帶的協議
allow_fragments:是否忽略fragment,若是爲false,fragment部分就會被忽略,會被解析成path、params或query的一部分
urlparse()會把URL解析成6個部分,標準的連接格式以下:
scheme://netloc/path;params?query#fragment
協議://域名/訪問路徑;參數?查詢條件#錨點
查詢條件通常用做GET類型的URL,錨點用於定位頁面內部的下拉位置
from urllib.parse import urlparse result = urlparse("http://www.baidu.com/index.html;user?id=5#comment") print(type(result),result,sep="\n")
該方法接受一個可迭代對象,長度必須是6,對可迭代對象進行遍歷,構造URL
from urllib.parse import urlunparse data = ["http","www.baidu.com","index.html","user","a=6","comment"] print(urlunparse(data))
該方法與urlparse()方法相似,只不過它再也不單獨解析params,params合併到path中,返回的也是個元組,長度爲5
from urllib.parse import urlsplit result = urlsplit("http://www.baidu.com/index.html;user?a=6#comment") print(result)
參照urlunparse(),可是長度爲5
有兩個參數,第一個爲base_url(基礎連接),第二個爲new_url(新的連接),該方法會解析base_url的scheme,netloc和path,若是新的連接存在就是用新的連接的部分,若是不存在就補充。
from urllib.parse import urljoin print(urljoin("http://www.baidu.com","FAQ.html")) print(urljoin("http://www.baidu.com","https://cuiqingcai.com/FAQ.html")) print(urljoin("http://www.baidu.com/about.html","https://cuiqingcai.com/FAQ.html")) print(urljoin("http://www.baidu.com/about.html","http://cuiqingcai.com/FAQ.html?question=2")) print(urljoin("http://www.baidu.com?wd=abc","https://cuiqingcai.com/index.php")) print("http://www.baidu.com","?category=2#comment") print("www.baidu.com","?category=2#comment") print("www.baidu.com#comment","?category=2")
在構造GET請求的參數時,能夠將字典類型的數據轉化爲GET請求參數
from urllib.parse import urlencode params = { "name":"heesch", "age":18 } base_url = "http://baidu.com?" url = base_url + urlencode(params) print(url)
與urlencode()相反,將GET請求的參數化爲字典的形式
from urllib.parse import parse_qs query = "name=heesch&age=18" print(parse_qs(query))
他是將GET請求的參數轉化爲元組組成的列表
from urllib.parse import parse_qsl query = "name=heesch&age=18" print(parse_qsl(query))
將內容轉化爲URL編碼的格式,URL中帶有中文參數時,有時可能會致使亂碼的問題。
from urllib.parse import quote keyword = "壁紙" url = "http://www.baidu.com" + quote(keyword) print(url)
與quote相反,將URL進行解碼
from urllib.parse import unquote url = "http://www.baidu.com%E5%A3%81%E7%BA%B8" print(unquote(url))
Robots協議也稱做爬蟲協議、機器人協議,用來告訴爬蟲和搜索引擎哪些頁面能夠爬取,哪些不能夠抓取。一般是一個叫作robots.txt的文本文件,放在網站的根目錄下
當搜索爬蟲訪問一個站點是,首先會檢查這個站點的根目錄下是否存在robots.txt文件,若是存在,搜索爬蟲則會根據其中定義的爬取範圍來爬取。若是沒有,則搜索爬蟲會訪問全部可直接訪問的站點。
下面看一個robots.text樣例:
User-agent:* Disallow:/ Allow:/public/
User-agent:設置爬蟲的名稱,* 表示該協議對任何爬蟲都有效
Dislalow:制定了不容許抓取的目錄,若爲/ 表示不容許抓取任何頁面
Allow:和Disallow一塊兒出現,限制訪問路徑
瞭解完Robots協議以後,咱們可使用robotparser模塊來解析robot.txt。該模塊提供一個類RobotFileParse,它能夠根據robots.txt文件來判斷一個爬蟲是否有權限來爬取這個頁面
聲明:urllib.robotparser.RobotFileParser(url="")
這個類經常使用的方法:
set_url():設置robots.txt文件的連接。若是建立RobotFileParse對象時傳入連接,就不須要用這個方法進行設置了
read():讀取robots.text文件並分析,不會返回結果,可是執行了讀取操做,若是不執行,下面的方法判斷都爲false
parse():用來解析robots.txt文件
can_fetch():有兩個參數,第一個是User-agent,第二個是要抓取的URL,返回的結果爲True或False,用來判斷是否能抓取這個頁面
mtime():返回上次抓取robot.txt的時間,對於長時間分析或抓取的搜索爬蟲須要按期檢查
modified():將當前時間設置爲上次抓取和分析robots.txt的時間
咱們先用can_fetch()方法來判斷網頁是否能抓取:
from urllib.robotparser import RobotFileParser rp = RobotFileParser() rp.set_url("http://jianshu.com/robot.txt") rp.read() print(rp.can_fetch("*","http://www.jianshu.com/p/b67554025d7d")) print(rp.can_fetch("*","http://www.jianshu.com/search?q=python&page=1&type=collections"))
兩個false,都不讓抓取。
咱們再用parse()方法來判斷網頁是否能抓取:
from urllib.robotparser import RobotFileParser from urllib import request,parse url = "https://www.jianshu.com/robots.txt" headers = { "User-Agent":"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT", } rp = RobotFileParser() req = request.Request(url=url,headers=headers,method="GET") rp.parse(request.urlopen(req).read().decode("UTF-8").split("\n")) print(rp.can_fetch("*","http://www.jianshu.com/p/b67554025d7d")) print(rp.can_fetch("*","http://www.jianshu.com/search?q=python&page=1&type=collections"))
咱們學完robotparser模塊後,咱們能夠方便的判斷哪些一面能夠抓取,哪些頁面不能抓取。