Python爬蟲urllib2庫的基本使用系列(三)

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ù)是加密安全的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,923評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,253評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,801評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,023評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,530評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,494評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,639評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 43,890評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,242評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,626評論 2 370

推薦閱讀更多精彩內(nèi)容