1. 程式人生 > >Python之urlib庫的基本使用(填坑)

Python之urlib庫的基本使用(填坑)

以下為個人在學習Python過程中做的筆記總結之爬蟲常用庫urllib

前言

urlib庫為python3的HTTP內建請求庫

urilib的四個模組:

  1. urllib.request:用於獲取網頁的響應內容
  2. urllib.error:異常處理模組,用於處理異常的模組
  3. urllib.parse:用於解析url
  4. urllib.robotparse:用於解析robots.txt,主要用於看哪些網站不能進行爬取,不過少用

一、urllib.request

urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,cadefault=False

,context=None)

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
在客戶機和伺服器之間進行請求-響應時,兩種最常被用到的方法是:GETPOST

  • 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時,都是分五步,如下:

  1. 進行導包,至關重要的一步,不導包直接出錯。
  2. 獲取cookie處理物件,使用cookiejar包
  3. 建立cookie處理器,使用request.HTTPCookieJarProcessor()
  4. 利用cookie處理器構建opener,使用request.build_opener()
  5. 進行請求網站,用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網路爬蟲開發實戰》學習總結而來