1. urllib庫的簡介
urllib庫是Python中一個最基本的網絡請求庫。可以模擬瀏覽器的行為,向指定的服務器發送一個請求,并可以保存服務器返回的數據。
2. urlopen函數:
在Python3的urllib庫中,所有和網絡請求相關的方法,都被集到urllib.request模塊下面了,以先來看下urlopen函數基本的使用:
from urllib import request
resp = request.urlopen('http://www.baidu.com')
print(resp.read())
實際上,使用瀏覽器訪問百度,右鍵查看源代碼。你會發現,跟我們剛才打印出來的數據是一模一樣的。也就是說,上面的三行代碼就已經幫我們把百度的首頁的全部代碼爬下來了。一個基本的url請求對應的python代碼真的非常簡單。
以下對urlopen函數的進行詳細講解:
-
url
:請求的url
。 -
data
:請求的data
,如果設置了這個值,那么將變成post
請求。 - 返回值:返回值是一個
http.client.HTTPResponse
對象,這個對象是一個類文件句柄對象。有read(size)
、readline
、readlines
以及getcode
等方法。
3. urlretrieve函數:
這個函數可以方便的將網頁上的一個文件保存到本地(媒體文件的下載)。以下代碼可以非常方便的將百度的首頁下載到本地:
from urllib import request
request.urlretrieve('http://www.baidu.com/','baidu.html')
4. urlencode函數:
用瀏覽器發送請求的時候,如果url中包含了中文或者其他特殊字符,那么瀏覽器會自動的給我們進行編碼。而如果使用代碼發送請求,那么就必須手動的進行編碼,這時候就應該使用urlencode
函數來實現。urlencode
可以把字典數據轉換為URL編碼的數據。示例代碼如下:
from urllib import parse
data = {'name':'爬蟲基礎','greet':'hello world','age':100}
qs = parse.urlencode(data)
print(qs)
5. parse_qs函數:
可以將經過編碼后的url參數進行解碼。示例代碼如下:
from urllib import parse
qs = "name=%E7%88%AC%E8%99%AB%E5%9F%BA%E7%A1%80&greet=hello+world&age=100"
print(parse.parse_qs(qs))
6. urlparse和urlsplit:
有時候拿到一個url,想要對這個url中的各個組成部分進行分割,那么這時候就可以使用urlparse
或者是urlsplit
來進行分割。示例代碼如下:
from urllib import request,parse
url = 'http://www.baidu.com/s?username=zhangsan'
result = parse.urlsplit(url)
# result = parse.urlparse(url)
print('scheme:',result.scheme)
print('netloc:',result.netloc)
print('path:',result.path)
print('query:',result.query)
urlparse
和urlsplit
基本上是一模一樣的。唯一不一樣的地方是,urlparse
里面多了一個params
屬性,而urlsplit
沒有這個params
屬性。比如有一個url
為:url = 'http://www.baidu.com/s;hello?wd=python&username=abc#1',
那么urlparse
可以獲取到hello
,而urlsplit
不可以獲取到。url
中的params
也用得比較少。
7. request.Request類:
如果想要在請求的時候增加一些請求頭,那么就必須使用request.Request類來實現。比如要增加一個User-Agent,示例代碼如下:
from urllib import request
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
req = request.Request("http://www.baidu.com/",headers=headers)
resp = request.urlopen(req)
print(resp.read())
8. ProxyHandler處理器(代理設置)
很多網站會檢測某一段時間某個IP的訪問次數(通過流量統計,系統日志等),如果訪問次數多的不像正常人,它會禁止這個IP的訪問。
所以我們可以設置一些代理服務器,每隔一段時間換一個代理,就算IP被禁止,依然可以換個IP繼續爬取。
urllib
中通過ProxyHandler
來設置使用代理服務器,下面代碼說明如何使用自定義opener
來使用代理:
from urllib import request
# 這個是沒有使用代理的
# resp = request.urlopen('http://httpbin.org/get')
# print(resp.read().decode("utf-8"))
# 這個是使用了代理的
handler = request.ProxyHandler({"http":"218.66.161.88:31769"})
opener = request.build_opener(handler)
req = request.Request("http://httpbin.org/ip")
resp = opener.open(req)
print(resp.read())
常用的代理有:
- 西刺免費代理IP:http://www.xicidaili.com/
- 快代理:http://www.kuaidaili.com/
- 代理云:http://www.dailiyun.com/
9. 什么是cookie:
在網站中,http
請求是無狀態的。也就是說即使第一次和服務器連接后并且登錄成功后,第二次請求服務器依然不能知道當前請求是哪個用戶。cookie
的出現就是為了解決這個問題,第一次登錄后服務器返回一些數據(cookie
)給瀏覽器,然后瀏覽器保存在本地,當該用戶發送第二次請求的時候,就會自動的把上次請求存儲的cookie
數據自動的攜帶給服務器,服務器通過瀏覽器攜帶的數據就能判斷當前用戶是哪個了。cookie
存儲的數據量有限,不同的瀏覽器有不同的存儲大小,但一般不超過4KB。因此使用cookie
只能存儲一些小量的數據。
cookie的格式:
Set-Cookie: NAME=VALUE;Expires/Max-age=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
參數意義:
- NAME:cookie的名字。
- VALUE:cookie的值。
- Expires:cookie的過期時間。
- Path:cookie作用的路徑。
- Domain:cookie作用的域名。
- SECURE:是否只在https協議下起作用。
10. 使用cookielib庫和HTTPCookieProcessor模擬登錄:
Cookie
是指網站服務器為了辨別用戶身份和進行Session
跟蹤,而儲存在用戶瀏覽器上的文本文件,Cookie
可以保持登錄信息到用戶下次與服務器的會話。
這里以人人網為例。人人網中,要訪問某個人的主頁,必須先登錄才能訪問,登錄說白了就是要有cookie
信息。那么如果我們想要用代碼的方式訪問,就必須要有正確的cookie
信息才能訪問。解決方案有兩種,第一種是使用瀏覽器訪問,然后將cookie
信息復制下來,放到headers
中。示例代碼如下:
from urllib import request
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
'Cookie': 'anonymid=jacdwz2x-8bjldx; depovince=GW; _r01_=1; _ga=GA1.2.1455063316.1511436360; _gid=GA1.2.862627163.1511436360; wp=1; JSESSIONID=abczwY8ecd4xz8RJcyP-v; jebecookies=d4497791-9d41-4269-9e2b-3858d4989785|||||; ick_login=884e75d4-f361-4cff-94bb-81fe6c42b220; _de=EA5778F44555C091303554EBBEB4676C696BF75400CE19CC; p=61a3c7d0d4b2d1e991095353f83fa2141; first_login_flag=1; ln_uact=970138074@qq.com; ln_hurl=http://hdn.xnimg.cn/photos/hdn121/20170428/1700/main_nhiB_aebd0000854a1986.jpg; t=3dd84a3117737e819dd2c32f1cdb91d01; societyguester=3dd84a3117737e819dd2c32f1cdb91d01; id=443362311; xnsid=169efdc0; loginfrom=syshome; ch_id=10016; jebe_key=9c062f5a-4335-4a91-bf7a-970f8b86a64e%7Ca022c303305d1b2ab6b5089643e4b5de%7C1511449232839%7C1; wp_fold=0'
}
url = 'http://www.renren.com/880151247/profile'
req = request.Request(url,headers=headers)
resp = request.urlopen(req)
with open('renren.html','w') as fp:
fp.write(resp.read().decode('utf-8'))
但是每次在訪問需要cookie
的頁面都要從瀏覽器中復制cookie
比較麻煩。在Python
處理Cookie
,一般是通過http.cookiejar
模塊和urllib
模塊的HTTPCookieProcessor
處理器類一起使用。http.cookiejar
模塊主要作用是提供用于存儲cookie
的對象。而HTTPCookieProcessor
處理器主要作用是處理這些cookie
對象,并構建handler
對象。
11. http.cookiejar模塊:
該模塊主要的類有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。這四個類的作用分別如下:
-
CookieJar
:管理HTTP cookie
值、存儲HTTP
請求生成的cookie
、向傳出的HTTP
請求添加cookie
的對象。整個cookie
都存儲在內存中,對CookieJar
實例進行垃圾回收后cookie
也將丟失。 -
FileCookieJar (filename,delayload=None,policy=None)
:從CookieJar
派生而來,用來創建FileCookieJar
實例,檢索cookie信息并將cookie存儲到文件中。filename
是存儲cookie
的文件名。delayload
為True
時支持延遲訪問訪問文件,即只有在需要時才讀取文件或在文件中存儲數據。 -
MozillaCookieJar (filename,delayload=None,policy=None)
:從FileCookieJar
派生而來,創建與Mozilla瀏覽器cookies.txt
兼容的FileCookieJar
實例。 -
LWPCookieJar (filename,delayload=None,policy=None)
:從FileCookieJar
派生而來,創建與libwww-perl
標準的Set-Cookie3
文件格式兼容的FileCookieJar
實例。
登錄人人網:
利用http.cookiejar
和request.HTTPCookieProcessor
登錄人人網。相關示例代碼如下:
from urllib import request,parse
from http.cookiejar import CookieJar
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
def get_opener():
cookiejar = CookieJar()
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)
return opener
def login_renren(opener):
data = {"email": "123456789@qq.com", "password": "abc123456789"}
data = parse.urlencode(data).encode('utf-8')
login_url = "http://www.renren.com/PLogin.do"
req = request.Request(login_url, headers=headers, data=data)
opener.open(req)
def visit_profile(opener):
url = 'http://www.renren.com/880151247/profile'
req = request.Request(url,headers=headers)
resp = opener.open(req)
with open('renren.html','w') as fp:
fp.write(resp.read().decode("utf-8"))
if __name__ == '__main__':
opener = get_opener()
login_renren(opener)
visit_profile(opener)
12. 保存cookie到本地:
保存cookie
到本地,可以使用cookiejar
的save
方法,并且需要指定一個文件名:
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar = MozillaCookieJar("cookie.txt")
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
req = request.Request('http://httpbin.org/cookies',headers=headers)
resp = opener.open(req)
print(resp.read())
cookiejar.save(ignore_discard=True,ignore_expires=True)
13. 從本地加載cookie:
從本地加載cookie
,需要使用cookiejar
的load
方法,并且也需要指定方法:
from urllib import request
from http.cookiejar import MozillaCookieJar
cookiejar = MozillaCookieJar("cookie.txt")
cookiejar.load(ignore_expires=True,ignore_discard=True)
handler = request.HTTPCookieProcessor(cookiejar)
opener = request.build_opener(handler)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'
}
req = request.Request('http://httpbin.org/cookies',headers=headers)
resp = opener.open(req)
print(resp.read())
14. 處理HTTPS請求 SSL證書驗證
現在隨處可見 https 開頭的網站,urllib可以為 HTTPS 請求驗證SSL證書,就像web瀏覽器一樣,如果網站的SSL證書是經過CA認證的,則能夠正常訪問,如:https://www.baidu.com/ 等...
如果SSL證書驗證不通過,或者操作系統不信任服務器的安全證書,比如瀏覽器在訪問12306網站如:https://www.12306.cn/mormhweb/ 的時候,會警告用戶證書不受信任。(據說 12306 網站證書是自己做的,沒有通過CA認證)
urllib在訪問的時候則會報出SSLError:
from urllib import request
url = "https://www.12306.cn/mormhweb/"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
request = request.Request(url, headers = headers)
response = request.urlopen(request)
print(response.read())
運行結果:
urllib.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)>
所以,如果以后遇到這種網站,我們需要單獨處理SSL證書,讓程序忽略SSL證書驗證錯誤,即可正常訪問。
from urllib import request
# 1. 導入Python SSL處理模塊
import ssl
# 2. 表示忽略未經核實的SSL證書認證
context = ssl._create_unverified_context()
url = "https://www.12306.cn/mormhweb/"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
request = request.Request(url, headers = headers)
# 3. 在urlopen()方法里 指明添加 context 參數
response = request.urlopen(request, context = context)
print(response.read())
關于CA:
CA(Certificate Authority)
是數字證書認證中心的簡稱,是指發放、管理、廢除數字證書的受信任的第三方機構,如北京數字認證股份有限公司、上海市數字證書認證中心有限公司等...
CA
的作用是檢查證書持有者身份的合法性,并簽發證書,以防證書被偽造或篡改,以及對證書和密鑰進行管理。
現實生活中可以用身份證來證明身份, 那么在網絡世界里,數字證書就是身份證。和現實生活不同的是,并不是每個上網的用戶都有數字證書的,往往只有當一個人需要證明自己的身份的時候才需要用到數字證書。
普通用戶一般是不需要,因為網站并不關心是誰訪問了網站,現在的網站只關心流量。但是反過來,網站就需要證明自己的身份了。
比如說現在釣魚網站很多的,比如你想訪問的是www.baidu.com, 但其實你訪問的是www.daibu.com, 所以在提交自己的隱私信息之前需要驗證一下網站的身份,要求網站出示數字證書。
一般正常的網站都會主動出示自己的數字證書,來確保客戶端和網站服務器之間的通信數據是加密安全的。
15. urllib 的異常錯誤處理
在我們用urlopen
或opener.open
方法發出一個請求時,如果urlopen
或opener.open
不能處理這個response
,就產生錯誤。
這里主要說的是URLError
和HTTPError
,以及對它們的錯誤處理。
URLError
URLError
產生的原因主要有:
- 沒有網絡連接
- 服務器連接失敗
- 找不到指定的服務器
我們可以用try except
語句來捕獲相應的異常。下面的例子里我們訪問了一個不存在的域名:
from urllib import request
requset_info = urllib2.Request('http://www.ajkfhafwjqh.com')
try:
request.urlopen(requset_info, timeout=5)
except urllib.URLError, err:
print(err)
運行結果如下:
<urlopen error [Errno 8] nodename nor servname provided, or not known>
urlopen error,錯誤代碼8,錯誤原因是沒有找到指定的服務器。
HTTPError
HTTPError是URLError的子類,我們發出一個請求時,服務器上都會對應一個response應答對象,其中它包含一個數字"響應狀態碼"。
如果urlopen或opener.open不能處理的,會產生一個HTTPError,對應相應的狀態碼,HTTP狀態碼表示HTTP協議所返回的響應的狀態。
注意,urllib可以為我們處理重定向的頁面(也就是3開頭的響應碼),100-299范圍的號碼表示成功,所以我們只能看到400-599的錯誤號碼。
from urllib import request
req = request.Request('http://blog.baidu.com/itcast')
try:
request.urlopen(req)
except urllib.HTTPError, err:
print err.code
print(err)
運行結果如下:
404
HTTP Error 404: Not Found
HTTP Error,錯誤代號是404,錯誤原因是Not Found,說明服務器無法找到被請求的頁面。
通常產生這種錯誤的,要么url不對,要么ip被封。