1.? 網(wǎng)頁抓取
所謂網(wǎng)頁抓取,就是把URL地址中指定的網(wǎng)絡(luò)資源從網(wǎng)絡(luò)流中抓取出來。在Python中有很多庫可以用來抓取網(wǎng)頁。
在python2中自帶urllib和urllib2。兩個最顯著的不同如下:
<1> urllib 模塊僅可以接受URL,不能創(chuàng)建 設(shè)置headers 的Request 類實例;
<2>!但是 urllib 提供 urlencode 方法用來產(chǎn)生GET查詢字符串,而 urllib2 則沒有。(這是 urllib 和 urllib2 經(jīng)常一起使用的主要原因)
<3>編碼工作使用urllib的urlencode()函數(shù),幫我們將key:value這樣的鍵值對,轉(zhuǎn)換成"key=value"這樣的字符串,解碼工作可以使用urllib的unquote()函數(shù)。注意,不是urllib2.urlencode()
在python3中urllib2被改為urllib庫下request模塊。編碼使用urllib庫下parse模塊
from urllib import parse
# url轉(zhuǎn)碼操作,只有轉(zhuǎn)碼后瀏覽器才會識別該url
kw = {'name': '中國'}
res = parse.urlencode(kw)
print(res)
res2 = parse.unquote(res)
print(res2)
# 結(jié)果如下:
# name=E5%B0%8F%E5%8F%AF
# name=中國
注意:本文采用python3.5的python爬蟲環(huán)境。
2. urlopen發(fā)送請求,返回響應(yīng)案例
# 導(dǎo)入urllib 庫下request模塊
from urllib import request
# 向指定的url發(fā)送請求,并返回服務(wù)器響應(yīng)的類文件對象
response = request.urlopen("http://www.baidu.com")
# 類文件對象支持 文件對象的操作方法,如read()方法讀取文件全部內(nèi)容,返回字符串
html = response.read()
# 打印字符串
print(html)
3. Request構(gòu)造請求對象
# 導(dǎo)入urllib 庫下request模塊
from urllib import request
# url 作為Request()方法的參數(shù),構(gòu)造并返回一個Request對象
req = request.Request("http://www.baidu.com")
# Request對象作為urlopen()方法的參數(shù),發(fā)送給服務(wù)器并接收響應(yīng)
response = request.urlopen(req)
html = response.read()
print(html)
注意:
新建Request實例,除了必須要有 url 參數(shù)之外,還可以設(shè)置另外兩個參數(shù):
<1> data(默認(rèn)空):提交的Form表單數(shù)據(jù),同時 HTTP 請求方法將從默認(rèn)的 "GET"方式 改為 "POST"方式。
<2> headers(默認(rèn)空):參數(shù)為字典類型,包含了需要發(fā)送的HTTP報頭的鍵值對。
4.? User-Agent偽裝成瀏覽器進(jìn)行請求
瀏覽器 就是互聯(lián)網(wǎng)世界上公認(rèn)被允許的身份,如果我們希望我們的爬蟲程序更像一個真實用戶,那我們第一步就是需要偽裝成一個被瀏覽器。用不同的瀏覽器在發(fā)送請求的時候,會有不同的 User-Agent 報頭。
python2中的urllib2默認(rèn)的User-Agent頭為:Python-urllib/x.y (x和y 是Python 主.次 版本號,例如 Python-urllib/2.7)
from urllib import request
def loadPage():
# 包含一個請求報頭的headers, 發(fā)送的請求會附帶瀏覽器身份發(fā)送
headers = {"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)"}
# 構(gòu)造一個請求對象
req = request.Request("http://www.baidu.com/", headers=headers)
# 發(fā)送http請求,返回服務(wù)器的響應(yīng)內(nèi)容
# response響應(yīng)是一個類文件對象
response = request.urlopen(req)
# 類文件對象支持文件操作的相關(guān)方法,比如read():讀取文件所有內(nèi)容,返回字符串
return response.read()
if __name__ == "__main__":
# print __name__
html = loadPage()
# 打印字符串,也就是整個網(wǎng)頁的源碼
print(html)
5. 添加更多的Header信息
在 HTTP Request 中加入特定的 Header,來構(gòu)造一個完整的HTTP請求消息。
可以通過調(diào)用Request.add_header() 添加/修改一個特定的header 也可以通過調(diào)用Request.get_header()來查看已有的header。
# 導(dǎo)入urllib 庫下request模塊
from urllib import request
url = "http://www.baidu.com"
# IE 9.0 的 User-Agent
user_agent = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"}
req = request.Request(url, headers=user_agent)
# 也可以通過調(diào)用Reques.add_header() 添加/修改一個特定的header為長鏈接
req.add_header("Connection", "keep-alive")
# 也可以通過調(diào)用Request.get_header()來查看header信息
# req.get_header(header_name="Connection")
response = request.urlopen(req)
print(response.code)? # 可以查看響應(yīng)狀態(tài)碼
# 網(wǎng)頁源代碼
html = response.read()
print(html)
6. 隨機添加/修改User-Agent
from? urllib import request
import random
def loadPage(url):
# User-Agent 列表
useragent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
]
# 隨機選擇一個User-Agent字符串
# random.choice()這個方法可以從列表中隨機的取出一個元素
user_agent = random.choice(useragent_list)
# 構(gòu)造請求對象
req = request.Request(url)
# 添加一個請求報頭,添加的時候是User-Agent
req.add_header("User-Agent", user_agent)
# 查看指定請求報頭,獲取的時候一定是User-agent
print(req.get_header("User-agent"))
# 發(fā)送請求,返回服務(wù)器響應(yīng)
reseponse = request.urlopen(req)
# 返回響應(yīng)的html內(nèi)容
return reseponse.read()
if __name__ == "__main__":
url = input("請輸入需要抓取的網(wǎng)頁:")
html = loadPage(url)
print(html)
7. GET方式請求,爬取貼吧案例
# 處理HTTP請求,在python2中是urllib2,在python3中是urllib.request
from urllib import request, parse
def loadPage(url, filename):
'''
作用:根據(jù)url發(fā)送請求,獲取服務(wù)器響應(yīng)文件
url:需要爬取的url地址
filename: 文件名
'''
print("[LOG]: 正在下載" + filename)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6"}
# 構(gòu)造請求對象
req = request.Request(url, headers=headers)
# 發(fā)送請求,返回響應(yīng)結(jié)果
response = request.urlopen(req)
if response.getcode() == 200:
return response.read()
else:
print("[ERR]: 下載失敗...")
def writePage(html, filename):
"""
作用:保存服務(wù)器響應(yīng)文件到本地磁盤文件里
html: 服務(wù)器響應(yīng)文件
filename: 本地磁盤文件名
"""
print("[LOG]: 正在寫入" + filename)
with open(filename, "wb") as f:
f.write(html)
def tiebaSpider(url, beginPage, endPage):
"""
作用:負(fù)責(zé)處理url,分配每個url去發(fā)送請求
url:需要處理的第一個url
beginPage: 爬蟲執(zhí)行的起始頁面
endPage: 爬蟲執(zhí)行的截止頁面
"""
for page in range(beginPage, endPage + 1):
pn = (page - 1) * 50
# 對pn進(jìn)行轉(zhuǎn)碼后,才能在瀏覽器中識別
keyword = parse.urlencode({"pn": pn})
fullurl = url + "&" + keyword
print(fullurl)
# 爬取到的數(shù)據(jù)存放的文件名稱
filename = "第" + str(page) + "頁.html"
html = loadPage(fullurl, filename)
writePage(html, filename)
if __name__ == "__main__":
tiebaName = input("請輸入需要爬取的貼吧名:")
beginPage = int(input("請輸入爬取的起始頁:"))
endPage = int(input("請輸入爬取的終止頁: "))
# "https://tieba.baidu.com/f? kw=%E6%9D%8E%E6%AF%85&pn=50"
baseURL = "http://tieba.baidu.com/f?"
keyword = {"kw": tiebaName}
kw = parse.urlencode(keyword)
url = baseURL + kw
tiebaSpider(url, beginPage, endPage)
8. POST方式爬取有道詞典案例
from urllib import request, parse
def loadPage():
# 新版本的有道翻譯的接口
# url = "http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule&sessionFrom=null"
# 老版本的有道翻譯的接口
url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&sessionFrom=null"
# i = bytes(input("請輸入需要翻譯的句子:").encode('utf-8'))
i = input("請輸入需要翻譯的句子:")
# form表單數(shù)據(jù),通過網(wǎng)頁源代碼可查看攜帶的請求參數(shù)
formdata = {
"i": i,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
# "salt" : "1500253430162",
# "sign" : "db86dafb01c61b4c5bc85bc0868ff7f6",
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_CL1CKBUTTON",
"typoResult": "true",
}
# 將數(shù)據(jù)轉(zhuǎn)為url編碼,注意這個地方要加encode()編碼方式,否則會報錯如下:
# "POST data should be bytes or an iterable of bytes. It cannot be of type str.
data = parse.urlencode(formdata).encode(encoding='utf8')
# 請求報頭
headers = {"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)"}
# 構(gòu)造一個請求,因為有data參數(shù),所有請求會從默認(rèn)的get變成post
# 發(fā)送post請求同時,會把data數(shù)據(jù)傳輸?shù)街付ǖ膗rl地址
req = request.Request(url, data=data, headers=headers)
# 發(fā)送請求,返回響應(yīng)
response = request.urlopen(req)
# 去除字符串首尾的空格
content = response.read().strip()
# print content
# 將字符串格式的字典,轉(zhuǎn)換為真正的字典
content = eval(content)
print(content)
# 取出字典的值
print(content["translateResult"][0][0]["tgt"])
if __name__ == "__main__":
loadPage()
# 結(jié)果如下:
# 請輸入需要翻譯的句子:hello
# 返回的json字符串如下:
# {'type': 'EN2ZH_CN',
#? 'translateResult': [[{'src': 'hello', 'tgt': '你好'}]],
#? 'smartResult': {'type': 1, 'entries': ['', 'n. 表示問候, 驚奇或喚起注意時的用語', 'int. 喂;哈羅', 'n. (Hello)人名;(法)埃洛']},
#? 'errorCode': 0,
#? 'elapsedTime': 1 }
# 最終結(jié)果:
# 你好
9. 處理HTTPS請求 SSL證書驗證
現(xiàn)在隨處可見 https 開頭的網(wǎng)站,urllib2可以為 HTTPS 請求驗證SSL證書,就像web瀏覽器一樣,如果網(wǎng)站的SSL證書是經(jīng)過CA認(rèn)證的,則能夠正常訪問,如:https://www.baidu.com/等...
如果SSL證書驗證不通過,或者操作系統(tǒng)不信任服務(wù)器的安全證書,比如瀏覽器在訪問12306網(wǎng)站如:https://www.12306.cn/mormhweb/的時候,會警告用戶證書不受信任。(據(jù)說 12306 網(wǎng)站證書是自己做的,沒有通過CA認(rèn)證)
如果沒有ssl證書認(rèn)證,會報如下錯誤:
urllib.error.URLError:
from urllib import request
import ssl
# 表示忽略網(wǎng)站的不合法證書認(rèn)證
context = ssl._create_unverified_context()
headers = {"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)"}
req = request.Request("https://www.12306.cn/mormhweb/", headers=headers)
# 在urlopen()添加context參數(shù),請求將忽略網(wǎng)站的證書認(rèn)證
response = request.urlopen(req, context=context)
print(response.read())
關(guān)于CA數(shù)據(jù)證書認(rèn)證
CA(Certificate Authority)是數(shù)字證書認(rèn)證中心的簡稱,是指發(fā)放、管理、廢除數(shù)字證書的受信任的第三方機構(gòu),如北京數(shù)字認(rèn)證股份有限公司、上海市數(shù)字證書認(rèn)證中心有限公司等...
CA的作用是檢查證書持有者身份的合法性,并簽發(fā)證書,以防證書被偽造或篡改,以及對證書和密鑰進(jìn)行管理。
現(xiàn)實生活中可以用身份證來證明身份, 那么在網(wǎng)絡(luò)世界里,數(shù)字證書就是身份證。和現(xiàn)實生活不同的是,并不是每個上網(wǎng)的用戶都有數(shù)字證書的,往往只有當(dāng)一個人需要證明自己的身份的時候才需要用到數(shù)字證書。
普通用戶一般是不需要,因為網(wǎng)站并不關(guān)心是誰訪問了網(wǎng)站,現(xiàn)在的網(wǎng)站只關(guān)心流量。但是反過來,網(wǎng)站就需要證明自己的身份了。
比如說現(xiàn)在釣魚網(wǎng)站很多的,比如你想訪問的是www.baidu.com,但其實你訪問的是www.daibu.com”,所以在提交自己的隱私信息之前需要驗證一下網(wǎng)站的身份,要求網(wǎng)站出示數(shù)字證書。
一般正常的網(wǎng)站都會主動出示自己的數(shù)字證書,來確保客戶端和網(wǎng)站服務(wù)器之間的通信數(shù)據(jù)是加密安全的。