urllib庫


urllib是python內置的網絡庫

一.urllib的主要功能

urllib分為4個模塊
1.request:最基本的HTTP請求模塊,可以用來發送HTTP請求,並接收服務端的響應數據。這個過程就像在瀏覽器地址欄輸入URL,然后按下Enter鍵一樣
2.error:異常處理模塊,如果出現請求錯誤,可以捕獲這些異常,然后根據實際情況,進行重試或者直接忽略,或進行其他操作。
3.parse:工具模塊,提供了很多處理URL的API,如拆分、解析、合並
4.robotparser:主要用來識別網站的robots.txt文件,然后判斷哪些文件可以抓取,哪些文件不可以抓取

二:發送請求與獲得響應

1.get請求

import urllib.request

# urlopen可以用http也可以用https
response=urllib.request.urlopen('https://www.jd.com')
# 輸出響應的數據類型
print('response的類型:',type(response))
# 輸出響應狀態碼、響應消息、和HTTP版本
print('status:',response.status,' msg:',response.msg,' version:', response.version)
# 輸出所有的響應頭信息
print('headers:',response.getheaders())
# 輸出Content-Type信息
print('headers.Content-Type',response.getheader('Content-Type'))
# 輸出url鏈接返回響應的HTML代碼
print(response.read().decode('utf-8'))

2.post請求

import urllib.request
# urlencode方法將字典類型的表單轉換為字符串形式的表單
data=bytes(urllib.parse.urlencode({'name':'Bill','age':30}),encoding='utf-8') # data參數是字節類型byte()來處理
response=urllib.request.urlopen('http://httpbin.org/post',data=data) # 發送POST請求用data參數命名
print(response.read().decode('utf-8')) # 這里並不需要顯示指出是POST請求,緣由是這個url是測試HTTP POST請求的網址

3.請求超時

import urllib.request
import socket
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('超時')
print("繼續爬蟲其他的工作")

4.加入User-Agent和Host請求頭和自定義請求頭who

from urllib import request,parse
# 定義要提交HTTP請求的URL
url = 'http://httpbin.org/post'

# 定義HTTP請求頭,其中who是自定義的請求字段
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    'Host':'httpbin.org',


    'who':'Python Scrapy'
}
# 定義表單數據
dict = {
    'name':'Bill',
    'age':30
}
# 將表單數據轉化為bytes形式
data = bytes(parse.urlencode(dict),encoding='utf-8')
# 創建request對象,通過request類的構造方法指定了表單數據和HTTP請求頭
req = request.Request(url = url,data=data,headers=headers,method="POST")
# urlopen函數通過request對象向服務端發送HTTP POST
response=request.urlopen(req)
print(response.read().decode('utf-8'))

5.設置中文請求頭

from urllib import request
from urllib.parse import unquote,urlencode
import base64

# base64.b64encode編碼 ;base64.b64decode解碼

url = 'http://httpbin.org/post'
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    'Host':'httpbin.org',
    'Chinese1':urlencode({'name':'李寧'}),
    # 設置中文HTTP請求頭,用base64編碼格式
    'MyChinese':base64.b64encode(bytes('這是中文HTTP請求頭',encoding='utf-8')),
    'who':'Python Scrapy'
}
dict = {
    'name':'Bill',
    'age':30
}
data = bytes(urlencode(dict),encoding='utf-8')
req = request.Request(url = url,data=data,headers=headers,method="POST")
# 通過add_header方法添加中文HTTP請求頭,url編碼格式
req.add_header('Chinese2',urlencode({"國籍":"中國"}))
response=request.urlopen(req)
# 獲得服務端的響應信息
value = response.read().decode('utf-8')
print(value)
import json
# 將返回值轉化為json對象
responseObj = json.loads(value)
# 解碼url編碼的HTTP請求頭
print(unquote(responseObj['headers']['Chinese1']))
print(unquote(responseObj['headers']['Chinese2']))
print(str(base64.b64decode(responseObj['headers']['Mychinese']),'utf-8'))

6.請求基礎驗證頁面

6.1編寫個支持驗證頁面的Web服務器

這個web服務器可以生成一個表單驗證,只有通過這個驗證才能訪問下一個頁面。表單驗證也是基礎驗證,是HTTP驗證的一種

from flask import Flask  # 
from flask import request
import base64
app = Flask(__name__)
# 判斷客戶端是否提交了用戶名和密碼,如果未提交,設置狀態碼為401,並設置WWW-Authenticate響應頭
# auth:Authorization請求頭字段的值,response:響應對象

def hasAuth(auth,response):
    if auth == None or auth.strip() == "":
        # 設置響應狀態碼為401
        response.status_code = 401
        # 設置響應頭的WWW-Authenticate字段,其中localhost是需要驗證的范圍
        response.headers["WWW-Authenticate"] = 'Basic realm="localhost"'
        # 返回False,表示客戶端未提交用戶名和密碼
        return False
    return True

# 根路由
@app.route("/") # 這個函數是把傳輸的密碼可以用編碼的方式寫入headers頭中,這里是Authorization中
def index():
    # 創建響應對象,並指定未輸入用戶名和密碼(單擊"取消”按鈕)或輸入錯誤后的返回內容
    response = app.make_response('username or password error')
    # 輸出所有的HTTP請求頭
    print(request.headers)
    # 得到Authorization請求頭的值
    auth = request.headers.get('Authorization') 
    # 得到Authorization請求頭的值
    print('得到Authorization請求頭的值:',auth) 
    if hasAuth(auth, response):   # 把請求頭的值和response放進這個函數中
        # 將用戶名和密碼按Base64編碼格式解碼,這里按空格拆分成兩個值,第一個是Basic,第二個是Base64編碼后的用戶名和密碼

        auth = str(base64.b64decode(auth.split(' ')[1]),'utf-8')
        # 用戶名和密碼之間用冒號(:)分隔,所以需要將他們拆開
        values = auth.split(':')
        # 獲取用戶名
        username = values[0]
        #獲取密碼
        password = values[1]
        print('username:',username) # 他們又是從哪獲取密碼的呢
        print('password:',password)
        # 判斷用戶名和密碼是否正確,如果正確,返回success
        if username == 'bill' and password == '1234':
            return "success"
    return response


if __name__ == '__main__':
    app.run()

6.2用戶名和密碼通過HTTP請求頭的Authorization字段發送給服務器

from urllib import request
import base64


url = 'http://localhost:5000'
headers = {
    'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    'Host':'localhost:5000',
    'Authorization': 'Basic ' + str(base64.b64encode(bytes('bill:1234','utf-8')),'utf-8'),

}
req = request.Request(url = url,headers=headers,method="GET")
response=request.urlopen(req)
print(response.read().decode('utf-8'))

6.3采用HTTPPasswordMgrWithDefaultRealm對象封裝請求字段數據

from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
from urllib.error import URLError
username = 'bill'
password = '1234'
url = 'http://localhost:5000'

p = HTTPPasswordMgrWithDefaultRealm() # HTTPPasswordMgrWithDefaultRealm對象封裝請求字段數據
# 封裝realm,url、用戶名和密碼
p.add_password('localhost',url,username,password)
auth_handler = HTTPBasicAuthHandler(p) # HTTPBasicAuthHandler用於處理管理認證
# 發送http請求
opener = build_opener(auth_handler)

try:
    result = opener.open(url)
    # 獲取服務端響應數據
    html = result.read().decode('utf-8')
    print(html)
except URLError as e:
    print(e.reason)

7.搭建代理與使用代理(創建ProxyHandler對象,並指定HTTP和HTTPS代理的IP和端口號)

from urllib.error import URLError
from urllib.request import ProxyHandler,build_opener


# 創建ProxyHandler對象,並指定HTTP和HTTPS代理的IP和端口號
proxy_handler = ProxyHandler({
    #'http':'http://103.100.96.174:53281',
    'http':'http://182.86.191.16:24695',
    'https':'https://182.86.191.16:24695'
})

opener = build_opener(proxy_handler)
try:
    #response = opener.open('http://blogjava.net')
    response = opener.open('https://www.jd.com')
    print(response.read().decode('utf-8'))
except URLError as e:
    print(e.reason)

8.Cookie的讀寫過程

8.1用Falsk編寫一個Web服務器

該Web服務器讀取客戶端發送過來的Cookie,以及將Cookie寫入客戶端

from flask import Flask # flask果然是小巧可擴展
from flask import request
app = Flask(__name__)
@app.route("/readCookie")
def readCookie():
    print(request.cookies) # 這個是讀取剛剛寫入的Cookie,或者還可以從用戶的請求中讀取Cookie
    print(request.cookies.get('MyCookie'))
    return "hello world"

@app.route("/writeCookie") # route后跟着的因該是url路徑
def writeCookie():
    response = app.make_response('write cookie')
    response.set_cookie("id", value="12345678") # 這里竟然可以直接寫入cookie
    return response
if __name__ == '__main__':
    app.run()

8.2獲取Cookie

urllib獲取Cookie真的很麻煩
首先讀取Cookie需要創建http.cookiejar.CookieJar類的實例a,然后再創建urllib.request.HTTPCookieProcessor類的實例b,並將a作為實例b的參數傳進。而build_opener的參數就是實例b,所以,當build_open從服務端響應某個數據時就會讀取服務端發送過來的Cookie然后保存在實例a中,返回的是個字典

import http.cookiejar
import  urllib.request
# 創建CookieJar對象
cookie = http.cookiejar.CookieJar()
# 創建HTTPCookieProcessor對象
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler) # build_opener從服務器端獲取響應數據時就會讀取服務端發送過來的Cookie
# 給http://www.baidu.com發送請求,並獲得響應數據
response = opener.open('http://www.baidu.com') # open()中傳入url就可以獲取響應了
print('------http://www.baidu.com--------')
# 輸出服務器端發送的所有Cookie
for item in cookie:
    print(item.name + '=' + item.value)
# 下面的代碼用同樣的方式訪問CookieServer,並輸出返回的Cookie
print('------http://127.0.0.1:5000/writeCookie--------')
cookie = http.cookiejar.CookieJar()
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://127.0.0.1:5000/writeCookie')
for item in cookie:
    print(item.name + '=' + item.value)

8.3讀取Cookie並把Cookie保存起來

可以用MozillaCookieJar類和LWPCookieJar類再獲得Cookie的同時講Cookie分別保存成Mozilla瀏覽器格式和libwww-perl(LWP)格式,再創建MozillaCookieJar類和LWPCookieJar類的實例過程中需要傳入Cookie文件名。其他的使用方法與.CookieJar類基本一樣

import http.cookiejar
import urllib.request
filename1 = 'cookies5.txt' 
filename2 = 'cookies4.txt'
# 創建MozillaCookieJar對象
cookie1 = http.cookiejar.MozillaCookieJar(filename1) # 這個可以自動創建文件
# 創建LWPCookieJar對象
cookie2 = http.cookiejar.LWPCookieJar(filename2)


handler1 = urllib.request.HTTPCookieProcessor(cookie1)
handler2 = urllib.request.HTTPCookieProcessor(cookie2)
opener1 = urllib.request.build_opener(handler1)
opener2 = urllib.request.build_opener(handler2)
opener1.open('http://www.baidu.com')
opener2.open('http://www.baidu.com')
# 將Cookie保存成MozillaCookieJar格式
cookie1.save(ignore_discard=True,ignore_expires=True)
# 將Cookie保存成LWPCookieJar格式
cookie2.save(ignore_discard=True,ignore_expires=True)

8.4創建一個Cookie.txt文件,並自定義2個Cookie,然后通過load方法裝在Cookie.txt文件中的Cookie

import http.cookiejar
import urllib.request
filename = 'cookies.txt'
cookie = http.cookiejar.LWPCookieJar()
# 裝載cookies.txt文件,由於使用了LWPCookieJar讀取Cookie,所以Cookie文件必須是LWP格式
cookie.load('cookies.txt',ignore_discard=True,ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://127.0.0.1:5000/readCookie')
print(response.read().decode('utf-8'))

三.異常處理

1.URLError

from urllib import request,error # 異常類都在urllib的error模塊中定義

try:
    response = request.urlopen('http://www.jd123.com/test.html')
except error.URLError as e:
    print(e.reason) # Not Found


try:
    response = request.urlopen('https://geekori.com/abc.html')
except error.URLError as e:
    print(e.reason) # Not Found


try:
    response = request.urlopen('https://geekori123.com/abc.html')
except error.URLError as e:
    print(e.reason) # [Errno 11001] getaddrinfo failed

    
try:
    response = request.urlopen('https://bbbccc.com',timeout=2)
except error.URLError as e:
    print(e.reason) # [Errno 11001] getaddrinfo failed

2.HTTPError

from urllib import request,error
import socket
try:
    response = request.urlopen('http://www.jd123.com/test.html')
except error.HTTPError as e:
    print(type(e.reason))
    print(e.reason,e.code,e.headers)
try:
    response = request.urlopen('https://www.baidu.com',timeout=0.01)
except error.HTTPError as e:
    print('=====')
    print('error.HTTPError:', e.reason) # 這個語句沒有拋出
except error.URLError as e:
    # <class 'socket.gaierror'> gaierror后端拋出的異常這個報錯直接是域名錯了,url不存在
    print(type(e.reason)) 
    print('error.URLError:', e.reason) # error.URLError: timed out
    if isinstance(e.reason,socket.timeout):
        print('超時錯誤')

四。解析鏈接

1.拆分與合並URL

urlparse函數用於拆分URL,也就是將URL分解成不同的部分
urlunparse合並函數

from urllib.parse import urlparse,urlunparse

# urlparse用於拆分URL
result = urlparse('https://search.jd.com/Searchprint;hello?keyword=Python從菜鳥到高手&enc=utf-8#comment')

print(type(result),result)
print('--------分割線----------')
# 將url拆成6部分
print('scheme:',result.scheme) # scheme: https
print('netloc:',result.netloc) # netloc: search.jd.com
print('path:',result.path)  #path: /Searchprint
print('params:',result.params) # params: hello
print('query:',result.query) # query: keyword=Python從菜鳥到高手&enc=utf-8
print('fragment:',result.fragment) # fragment: comment
print('-----------------')
result = urlparse('search.jd.com/Searchprint;hello?keyword=Python從菜鳥到高手&enc=utf-8#comment',scheme='ftp',allow_fragments=False)

print('scheme:',result.scheme) # scheme: ftp
print('fragment:',result.fragment) # 表示false則表示忽略fragment部分,默認參數值是True

print('----------------')
data = ['https','search.jd.com','Searchprint','world','keyword=Python從菜鳥到高手&enc=utf-8','comment']
# urlunparse函數合並url不同的部分
print(urlunparse(data)) # https://search.jd.com/Searchprint;world?keyword=Python從菜鳥到高手&enc=utf-8#comment

2.另一種拆分與合並URL

urlsplit函數與urlparse函數類似,只是將path與params看作一個整體,也就是urlsplit函數會將url拆分為5部分。
urlunsplit函數與urlunparse函數類似,只不過需要指定一個包含5個元素的可迭代對象,而urlunparse函數需要包含6個元素的可迭代對象。

from urllib.parse import urlsplit,urlunsplit

# 將Url拆成5部分
result = urlsplit('https://search.jd.com/Searchprint;hello?keyword=Python從菜鳥到高手&enc=utf-8#comment')

print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)# 少了params 與parse()函數相比
print('query:',result.query)
print('fragment:',result.fragment)
print('-----------------')
# 將Url拆成5部分,並指定默認的scheme,以及不考慮fragment部分
result = urlsplit('search.jd.com/Searchprint;hello?keyword=Python從菜鳥到高手&enc=utf-8#comment',scheme='ftp',allow_fragments=False)

print('scheme:',result.scheme) # scheme可選參數,如果url沒有帶協議(https、http\ftp等),那么scheme參數的值就會作為默認協議。該參數默認值為空字符串
print('fragment:',result.fragment)

print('----------------')
data = ['https','search.jd.com','Searchprint;world','keyword=Python從菜鳥到高手&enc=utf-8','comment']

print(urlunsplit(data)) # 合並url可以把

3.連接URL

from urllib.parse import urljoin

"""
urljoin函數的第一個參數是base_url,是一個基URL,
只能設置scheme、netloc和path;第二個參數是url,
如果第二個參數不是一個完整的url,則會將第二個參數的值加到第一個參數后面,自動添加/
如果第二個參數是完整的url就直接返回他

"""
# 輸出https://www.jd.com/index.html
print(urljoin('https://www.jd.com','index.html'))
# 輸出https://www.taobao.com
print(urljoin('https://www.jd.com','https://www.taobao.com'))
# 輸出https://www.taobao.com/index.html
print(urljoin('https://www.jd.com/index.html','https://www.taobao.com/index.html'))
# 輸出https://www.jd.com/index.php?name=Bill&age=30
print(urljoin('https://www.jd.com/index.php','?name=Bill&age=30'))
# 輸出https://www.jd.com/index.php?name=Bill
print(urljoin('https://www.jd.com/index.php?value=123','?name=Bill'))

4.url編碼(urlencode)

urljoin函數連接來組成完整的URL

from urllib.parse import urlencode,urljoin
# urlencode參數對URL進行編碼,對中文的url非常有用
# 中文轉碼后的格式是%xx 其中xx表示的是2位16進制數
params = {
    'name':'王軍',
    'country':'China',
    'age':30
}

base_url = 'https://www.google.com?'
#url = base_url + urlencode(params)
url = urljoin(base_url,'?' + urlencode(params))
print(url)

5.解碼與編碼(quote和unquote)

from urllib.parse import quote,unquote
keyword = '李寧'
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)
url = unquote(url)
print(url)

五。Robots協議

待補(具體詳情)

1.RobotFileParser解析robots.txt文件

from urllib.robotparser import RobotFileParser # 解析robots.txt文件
from urllib import request
robot = RobotFileParser()


url = 'https://www.jianshu.com/robots.txt'
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
    'Host': 'www.jianshu.com',
}
req = request.Request(url=url, headers=headers)

# 抓取robots.txt文件的內容,並提交給parse方法進行分析
robot.parse( request.urlopen(req).read().decode('utf-8').split('\n'))
# 輸出True
print(robot.can_fetch('*','https://www.jd.com')) # can_fetch方法判斷網站中的某一個url更具Robots協議是否有權抓取
# 輸出True
print(robot.can_fetch('*','https://www.jianshu.com/p/92f6ac2c350f'))
# 輸出False
print(robot.can_fetch('*','https://www.jianshu.com/search?q=Python&page=1&type=note'))


免責聲明!

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



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