Python之urlib庫的基本使用(填坑)
以下為個人在學習Python過程中做的筆記總結之爬蟲常用庫urllib
前言
urlib庫為python3的HTTP內建請求庫
urilib的四個模組:
- urllib.request:用於獲取網頁的響應內容
- urllib.error:異常處理模組,用於處理異常的模組
- urllib.parse:用於解析url
- urllib.robotparse:用於解析robots.txt,主要用於看哪些網站不能進行爬取,不過少用
一、urllib.request
urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,cadefault=False
url:為請求網址
data:請求時需要傳送的引數
**timeou**t:超時設定,在該時間範圍內返回請求內容就不會報錯
示例程式碼:
from urllib import request
# 請求獲取網頁返回內容
response = request.urlopen('https://book.douban.com/')
# 獲取網頁返回內容
print(response.read().decode('utf-8'))
# 獲取狀態碼
print(response.status)
# 獲取請求頭
print(response.getheaders())
# 對請求頭進行遍歷
for i, j in response.getheaders():
print(i, '=', j)
可以使用上面的程式碼對一些網站進行請求了,但是當需要一些反爬網站時,這就不行了,這時我們需要適當地增加請求頭進行請求,這時就需要使用複雜一點的程式碼了,這時我們需要用到Request物件。
程式碼示例:
# 請求頭
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0' }
requests = request.Request('https://book.douban.com/', headers=headers) # 加入自己的請求頭更加接近瀏覽器
# 進行請求,把Request物件傳入urlopen引數中
response = request.urlopen(requests)
print(response.read().decode('utf-8'))
這個我添加了請求頭進行請求,使機械爬蟲模擬瀏覽器傳送請求,更加接近人為的瀏覽。可以對應一些反爬網站了。
如果網站需要進行登陸,這時需要用到post方法,用上面的也是可以的。程式碼如下:
from urllib import request, parse
# 使用post方法來進行模擬登陸豆瓣
data = {'source': 'None',
'redir': 'https://www.douban.com/',
'form_email': 'user',
'form_password': 'passwd',
'remember': 'on',
'login': '登入'}
# 將data的字典型別轉換為get請求方式
data = bytes(parse.urlencode(data), encoding='utf-8')
requests = request.Request('https://accounts.douban.com/login', headers=headers, data=data, method='POST')
response = request.urlopen(requests)
print(response.read().decode('utf-8'))
這裡我用到了data的引數把登陸需要的引數傳進去,還加了個請求方法Method。
parse.urlencode()後面有進行說明
PS:
兩種 HTTP 請求方法:GET 和 POST
在客戶機和伺服器之間進行請求-響應時,兩種最常被用到的方法是:GET 和 POST。
- GET - 從指定的資源請求資料。
- POST - 向指定的資源提交要被處理的資料
比較 GET 與 POST
簡單來說get就是直接請求可以直接看到傳送的資料,可以被快取;post需要將傳送的資料生成為form表單檔案然後再上傳檔案進行get,傳送的資料無法直接預覽也無法被快取。因此post比get更加安全。
這裡還有另外一種新增請求頭的方法
Request.add_header(): 引數有兩個,分別為請求頭對應的鍵和值,這種方法一次只能新增一個請求頭,新增多個需要用到迴圈或者直接用前面的方法新增多個請求頭
在登陸了網站之後,我們需要用到cookie來儲存登陸資訊,這時就需要獲取cookie了。urllib獲取cookie比較麻煩。
程式碼示例如下:
from http import cookiejar
# 獲取cookie
cookie = cookiejar.CookieJar()
# 獲取助手把cookie傳進去
handler = request.HTTPCookieProcessor(cookie)
# 獲取opener進行請求網站
opener = request.build_opener(handler)
# 請求網頁
response = opener.open('https://book.douban.com/')
# 列印cookie
for item in cookie:
print(item.name, '=', item.value)
單純地列印沒什麼用,我們需要把他存入檔案來儲存,下次使用時再次載入cookie來登陸
儲存cookie為檔案:
from http import cookiejar
# 將cookie儲存在檔案中
filename = 'cookie.txt'
cookie = cookiejar.MozillaCookieJar(filename) # 表示使用Mozilla的cookie方式儲存和讀取
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
opener.open('https://book.douban.com/')
# 儲存檔案
cookie.save(ignore_discard=True, ignore_expires=True)
另一種儲存方法:
from http import cookiejar
cookie = cookiejar.LWPCookieJar(filename) # 表示 Set-Cookie3 檔案格式儲存和讀取
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
opener.open('https://book.douban.com/')
# 儲存檔案
cookie.save(ignore_discard=True, ignore_expires=True)
這兩種儲存格式都是不一樣的,需要儲存的內容一樣。
儲存可以了,這時就需要用到載入了,當然也可以。程式碼如下:
from http import cookiejar
# 從cookie檔案載入到網頁上實現記住登陸
cookie = cookiejar.LWPCookieJar()
# 載入檔案
cookie.load(filename, ignore_discard=True, ignore_expires=True)
handler = request.HTTPCookieProcessor(cookie)
opener = request.build_opener(handler)
opener.open('https://book.douban.com/')
cookie小總結:在操作cookie時,都是分五步,如下:
- 進行導包,至關重要的一步,不導包直接出錯。
- 獲取cookie處理物件,使用cookiejar包
- 建立cookie處理器,使用request.HTTPCookieJarProcessor()
- 利用cookie處理器構建opener,使用request.build_opener()
- 進行請求網站,用opener.open(),這個不能用request.urlopen()
如果有時你在同一ip連續多次傳送請求,會有被封ip的可能,這時我們還需要用到代理ip進行爬取,程式碼如下:
import urllib.request
proxy_handler = urllib.request.ProxyHandler({
'http': 'http://127.0.0.1:9743',
'https': 'https://127.0.0.1:9743'
})
opener = urllib.request.build_opener(proxy_handler)
response = opener.open('http://book.douban.com/',timeout=1)
print(response.read())
可以看到越複雜的請求都需要用到request.build_opener(),這個方法有點重要。
二、urllib.error
將上面的使用代理ip的請求進行異常處理,如下:
from urllib import request, error
try:
proxy_handler = urllib.request.ProxyHandler({
'http': 'http://127.0.0.1:9743',
'https': 'https://127.0.0.1:9743'
})
opener = urllib.request.build_opener(proxy_handler)
response=opener.open('https://movie.douban.com/', timeout=1)
except error.HTTPError as e:
print(e.reason(), e.code(), e.headers())
except error.URLError as e:
print(e.reason)
因為有時這個ip或許也被封了,有可能會丟擲異常,所以我們為了讓程式執行下去進而進行捕捉程式:
- error.URLError: 這個是url的一些問題,這個異常只有一個reason屬性
- error.HTTPError:這個是error.URLError的子類,所以在與上面的混合使用時需要將這個異常放到前面,這個異常是一些請求錯誤,有三個方法,.reason(), .code(), .headers(),所以在捕捉異常時通常先使用這個
三、urllib.parse
解析url:urllib.parse.urlparse(url, scheme=”, allow_fragments=True)
簡單的使用:
from urllib import request, parse
# 解析url
print(parse.urlparse('https://book.douban.com/'))
print(parse.urlparse('https://book.douban.com/', scheme='http'))
print(parse.urlparse('book.douban.com/', scheme='http'))
# 下面是結果
ParseResult(scheme='https', netloc='book.douban.com', path='/', params='', query='', fragment='')
ParseResult(scheme='https', netloc='book.douban.com', path='/', params='', query='', fragment='')
ParseResult(scheme='http', netloc='', path='book.douban.com/', params='', query='', fragment='')
可以看出加了scheme引數和沒加的返回結果是有區別的。而當scheme協議加了,而前面的url也包含協議,一般會忽略後面的scheme引數。
既然後解析url,那當然也有反解析url,就是把元素串連成一個url
from urllib.parse import urlunparse
# 將列表元素拼接成url
data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
print(urlunparse(data))
# 下面是結果
http://www.baidu.com/index.html;user?a=6#comment
urlparse()接收一個列表的引數,而且列表的長度是有要求的,是必須六個引數以上,要不會丟擲異常
ValueError: not enough values to unpack (expected 7, got 6)
urllib.parse.urljoin():這個是將第二個引數的url缺少的部分用第一個引數的url補齊
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', 'https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.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'))
輸出結果:
http://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2
urllib.parse.urlencode():這個方法是將字典型別的引數轉為請求為get方式的字串
from urllib.parse import urlencode
params = {
'name': 'germey',
'age': 22
}
base_url = 'http://www.baidu.com?'
url = base_url + urlencode(params)
print(url)
輸出結果:
http://www.baidu.com?name=germey&age=22
四、總結
還有個urllib.robotparse庫少用,就不說了,如果需要的話再去查官方文件吧。
上面的只是我在學習過程中的總結,如果有什麼錯誤的話,歡迎在留言區指出,還有就是需要檢視更多用法的請檢視官方文件文件https://docs.python.org/3/library/urllib.html
需要程式碼的可以去我的github上面fork,給個star也行!
https://github.com/ldz0/Python-jupyter-notebooks
PS:筆記需要使用jupyter notebook開啟,之前的文章已經介紹了jupyter的簡單安裝和使用,是非常方便記錄筆記的工具。
本文主要參考崔慶才先生《Python3網路爬蟲開發實戰》學習總結而來