python爬蟲常用庫之urllib詳解

sergiojune發表於2018-03-11

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


urlib庫為python3的HTTP內建請求庫


urilib的四個模組:

  • urllib.request:用於獲取網頁的響應內容

  • urllib.error:異常處理模組,用於處理異常的模組

  • urllib.parse:用於解析url

  • urllib.robotparse:用於解析robots.txt,主要用於看哪些網站不能進行爬取,不過少用


1
urllib.request


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

  • url:為請求網址

  • data:請求時需要傳送的引數

  • timeout:超時設定,在該時間範圍內返回請求內容就不會報錯


示例程式碼:

 1from urllib import request
2
3# 請求獲取網頁返回內容
4response = request.urlopen('https://movie.douban.com/')
5# 獲取網頁返回內容
6print(response.read().decode('utf-8'))
7# 獲取狀態碼
8print(response.status)
9# 獲取請求頭
10print(response.getheaders())
複製程式碼


1# 對請求頭進行遍歷
2for k, v in response.getheaders():
3 print(k, '=', v)
複製程式碼



可以使用上面的程式碼對一些網站進行請求了,但是當需要一些反爬網站時,這就不行了,這時我們需要適當地增加請求頭進行請求,這時就需要使用複雜一點的程式碼了,這時我們需要用到Request物件


程式碼示例:

1# 請求頭
2headers = {'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'}
3requests = request.Request('https://movie.douban.com/', headers=headers) # 加入自己的請求頭更加接近瀏覽器
4# 進行請求,把Request物件傳入urlopen引數中
5response = request.urlopen(requests)
6print(response.read().decode('utf-8'))
複製程式碼

這個我新增了請求頭進行請求,使我傳送的請求更加接近瀏覽器的行為。可以對應一些反爬網站了


如果網站需要進行登陸,這時需要用到post方法,用上面的也是可以的。程式碼如下:

 1from urllib import request, parse
2# 使用post方法來進行模擬登陸豆瓣
3data = {'source': 'None',
4 'redir': 'https://www.douban.com/',
5 'form_email': 'user',
6 'form_password': 'passwd',
7 'remember': 'on',
8 'login': '登入'}
9# 將data的字典型別轉換為get請求方式
10data = bytes(parse.urlencode(data), encoding='utf-8')
11requests = request.Request('https://accounts.douban.com/login', headers=headers, data=data, method='POST')
12response = request.urlopen(requests)
13print(response.read().decode('utf-8'))複製程式碼

這裡我用到了data的引數把登陸需要的引數傳進去,還加了個請求方法Method


parse.urlencode()後面有講


這裡還有另外一種新增請求頭的方法

Request.add_header(): 引數有兩個,分別為請求頭對應的鍵和值,這種方法一次只能新增一個請求頭,新增多個需要用到迴圈或者直接用前面的方法新增多個請求頭


在登陸了網站之後,我們需要用到cookie來儲存登陸資訊,這時就需要獲取cookie了。urllib獲取cookie比較麻煩。

程式碼示例如下:

 1from http import cookiejar
2# 獲取cookie
3cookie = cookiejar.CookieJar()
4# 獲取助手把cookie傳進去
5handler = request.HTTPCookieProcessor(cookie)
6# 獲取opener進行請求網站
7opener = request.build_opener(handler)
8# 請求網頁
9response = opener.open('https://movie.douban.com/')
10# 列印cookie
11for c in cookie:
12 print(c.name, '=', c.value)
複製程式碼


單純地列印沒什麼用,我們需要把他存入檔案來儲存,下次使用時再次載入cookie來登陸


儲存cookie為檔案:

1from http import cookiejar
2# 將cookie儲存在檔案中
3filename = 'cookie.txt'
4cookie = cookiejar.MozillaCookieJar(filename) # 表示使用Mozilla的cookie方式儲存和讀取
5handler = request.HTTPCookieProcessor(cookie)
6opener = request.build_opener(handler)
7opener.open('https://movie.douban.com/')
8# 儲存檔案
9cookie.save(ignore_discard=True, ignore_expires=True)
複製程式碼


另一種儲存方法:

1from http import cookiejar
2cookie = cookiejar.LWPCookieJar(filename) # 表示 Set-Cookie3 檔案格式儲存和讀取
3handler = request.HTTPCookieProcessor(cookie)
4opener = request.build_opener(handler)
5opener.open('https://movie.douban.com/')
6# 儲存檔案
7cookie.save(ignore_discard=True, ignore_expires=True)
複製程式碼


這兩種儲存格式都是不一樣的,需要儲存的內容一樣。


儲存可以了,這時就需要用到載入了,當然也可以。程式碼如下:

1from http import cookiejar
2# 從cookie檔案載入到網頁上實現記住登陸
3cookie = cookiejar.LWPCookieJar()
4# 載入檔案
5cookie.load(filename, ignore_discard=True, ignore_expires=True)
6handler = request.HTTPCookieProcessor(cookie)
7opener = request.build_opener(handler)
8opener.open('https://movie.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進行爬取,程式碼如下:

1proxy = request.ProxyHandler({
2 'https': 'https://106.60.34.111:80'
3})
4opener = request.build_opener(proxy)
5opener.open('https://movie.douban.com/', timeout=1)
複製程式碼

可以看到越複雜的請求都需要用到request.build_opener(),這個方法有點重要,請記住哈


2
urllib.error


將上面的使用代理ip的請求進行異常處理,如下:

 1from urllib import request, error
2try:
3 proxy = request.ProxyHandler({
4 'https': 'https://106.60.34.111:80'
5 })
6 opener = request.build_opener(proxy)
7 opener.open('https://movie.douban.com/', timeout=1)
8except error.HTTPError as e:
9 print(e.reason(), e.code(), e.headers())
10except error.URLError as e:
11 print(e.reason)
複製程式碼

因為有時這個ip或許也被封了,有可能會丟擲異常,所以我們為了讓程式執行下去進而進行捕捉程式


  • error.URLError: 這個是url的一些問題,這個異常只有一個reason屬性

  • error.HTTPError:這個是error.URLError的子類,所以在與上面的混合使用時需要將這個異常放到前面,這個異常是一些請求錯誤,有三個方法,.reason(), .code(), .headers(),所以在捕捉異常時通常先使用這個


3
urllib.parse


解析url:urllib.parse.urlparse(url, scheme='', allow_fragments=True)


簡單的使用:

1from urllib import request, parse
2# 解析url
3print(parse.urlparse('https://movie.douban.com/'))
4print(parse.urlparse('https://movie.douban.com/', scheme='http'))
5print(parse.urlparse('movie.douban.com/', scheme='http'))
6# 下面是結果
7ParseResult(scheme='https', netloc='movie.douban.com', path='/', params='', query='', fragment='')
8ParseResult(scheme='https', netloc='movie.douban.com', path='/', params='', query='', fragment='')
9ParseResult(scheme='http', netloc='', path='movie.douban.com/', params='', query='', fragment='')
複製程式碼

可以看出加了scheme引數和沒加的返回結果是有區別的。而當scheme協議加了,而前面的url也包含協議,一般會忽略後面的scheme引數


既然後解析url,那當然也有反解析url,就是把元素串連成一個url


1from urllib import parse
2# 將列表元素拼接成url
3url = ['http', 'www', 'baidu', 'com', 'dfdf', 'eddffa'] # 這裡至少需要6個元素(我亂寫的,請忽視)
4print(parse.urlunparse(url))
5# 下面是結果
6http://www/baidu;com?dfdf#eddffa
複製程式碼

urlparse()接收一個列表的引數,而且列表的長度是有要求的,是必須六個引數以上,要不會丟擲異常

1Traceback (most recent call last):
2 File "E:/anaconda/python_project/python3_spider/urllib_test.py", line 107, in <module>
3 print(parse.urlunparse(url))
4 File "E:\anaconda\lib\urllib\parse.py", line 454, in urlunparse
5 _coerce_args(*components))
6ValueError: not enough values to unpack (expected 7, got 6)
複製程式碼


urllib.parse.urljoin():這個是將第二個引數的url缺少的部分用第一個引數的url補齊

1# 連線兩個引數的url, 將第二個引數中缺的部分用第一個引數的補齊
2print(parse.urljoin('https://movie.douban.com/', 'index'))
3print(parse.urljoin('https://movie.douban.com/', 'https://accounts.douban.com/login'))
4# 下面是結果
5https://movie.douban.com/index
6https://accounts.douban.com/login
複製程式碼



urllib.parse.urlencode():這個方法是將字典型別的引數轉為請求為get方式的字串

1data = {'name': 'sergiojuue', 'sex': 'boy'}
2data = parse.urlencode(data)
3print('https://accounts.douban.com/login'+data)
4# 下面是結果
5https://accounts.douban.com/loginname=sergiojuue&sex=boy
複製程式碼



4
結語

還有個urllib.robotparse庫少用,就不說了,留給以後需要再去查文件吧。

上面的只是我在學習過程中的總結,如果有什麼錯誤的話,歡迎在留言區指出,還有就是需要檢視更多用法的請檢視文件docs.python.org/3/library/u…


需要程式碼的可以去我的github上面fork,給個star也行!

github:https://github.com/SergioJune/gongzhonghao_code/blob/master/python3_spider/urllib_test.py


學習過程中看的大多是崔慶才大佬的視訊:edu.hellobi.com/course/157





undefined


日常學python

一個專注於python的公眾號

相關文章