天天看點

urllib子產品學習

已剪輯自:

http://xiaobaibook.com/details/51/

  概念:urllib是Python自帶的一個用于爬蟲的庫,其主要作用就是可以通過代碼模拟浏覽器發送請求。其常被用到的子子產品在Python3中的為urllib.request和urllib.parse,在Python2中是urllib和urllib2。

   使用流程:

  • 指定url
  • 基于urllib的request子子產品發起請求
  • 擷取響應中的資料值
  • 持久化存儲

二.由易到難的爬蟲程式

  1.第一個簡單的爬蟲程式:爬取搜狗首頁的頁面資料

import urllib.request

import urllib.parse

#1.指定url

url = 'https://www.sogou.com/'

#2.發起請求:使用urlopen函數發起請求,該函數傳回一個響應對象

response = urllib.request.urlopen(url=url)

#3.擷取響應對象中的頁面資料:read函數可以擷取響應對象中byte類型的資料值

page_text = response.read()

#4.持久化存儲:将爬取的頁面資料寫入檔案進行儲存

with open('./sogou.html','w',encoding='utf-8') as fp:

    fp.write(page_text.decode())#使用decode将page_text轉成字元串類型

  補充說明:

urlopen函數原型:

    urllib.request.urlopen(url,

data=None, timeout=<object object at 0x10af327d0>, *, cafile=None,

capath=None, cadefault=False, context=None)

在上述案例中我們隻使用了該函數中的第一個參數url。在日常開發中,我們能用的隻有url和data這兩個參數。

url參數:指定向哪個url發起請求

data參數:可以将post請求中攜帶的參數封裝成字典的形式傳遞給該參數(暫時不需要了解,後期會講)

urlopen函數傳回的響應對象,相關函數調用介紹:

response.headers():擷取響應頭資訊

response.getcode():擷取響應狀态碼

response.geturl():擷取請求的url

response.read():擷取響應中的資料值(位元組類型)

  2.二進制資料的爬取:爬取網絡上的某張圖檔資料,且存儲到磁盤

      方法 1:

url = 'https://pic.qiushibaike.com/system/pictures/12112/121121212/medium/ZOAND29U4NKNEWEF.jpg'

#3.擷取響應對象中的圖檔二進制類型的資料

img_data = response.read()

#4.持久化存儲:将爬取的圖檔寫入本地進行儲存

with open('./tupian.png','wb') as fp:

    fp.write(img_data)

      方法 2:

# 函數原型:urllib.request.urlretrieve(url,

filename=None)

# 作用:對url發起請求,且将響應中的資料值寫入磁盤進行存儲

urllib.request.urlretrieve(url=url,filename='./img.png')

    3.url的特性:

url必須為ASCII編碼的資料值。是以我們在爬蟲代碼中編寫url時,如果url中存在非ASCII編碼的資料值,則必須對其進行ASCII編碼後,該url方可被使用。

  案例:爬取使用搜狗根據指定詞條搜尋到的頁面資料(例如爬取詞條為‘周傑倫’的頁面資料)

url = 'https://www.sogou.com/web?query=周傑倫'

data = response.read()

with open('./周傑倫.html','wb') as fp:

    fp.write(data)

print('寫入檔案完畢')

  【注意】上述代碼中url存在非ascii編碼的資料,則該url無效。如果對其發起請求,則會報如下錯誤:

UnicodeEncodeError: 'ascii' codec can't encode

characters in position 15-17: ordinal not in range

  是以必須對url中的非ascii的資料進行ascii的編碼,則該url方可被發起請求:

      方法 1:使用quote函數

url = 'https://www.sogou.com/web?query=%s'

#對url中的非ascii進行編碼.quote函數可以對非ascii的資料值進行ascii的編碼

word = urllib.parse.quote('周傑倫')

#将編碼後的資料值拼接回url中

url = format(url%word)

      方法2: 使用urlencode函數

url = 'https://www.sogou.com/web?'

#将get請求中url攜帶的參數封裝至字典中

param = {

    'query':'周傑倫'

}

#對url中的非ascii進行編碼

param = urllib.parse.urlencode(param)

url += param

with open('./周傑倫1.html','wb') as fp:

  4.通過自定義請求對象,用于僞裝爬蟲程式請求的身份。

   之前在講解http常用請求頭資訊時,我們講解過User-Agent參數,簡稱為UA,該參數的作用是用于表明本次請求載體的身份辨別。如果我們通過浏覽器發起的請求,則該請求的載體為目前浏覽器,則UA參數的值表明的是目前浏覽器的身份辨別表示的一串資料。如果我們使用爬蟲程式發起的一個請求,則該請求的載體為爬蟲程式,那麼該請求的UA為爬蟲程式的身份辨別表示的一串資料。有些網站會通過辨識請求的UA來判别該請求的載體是否為爬蟲程式,如果為爬蟲程式,則不會給該請求傳回響應,那麼我們的爬蟲程式則也無法通過請求爬取到該網站中的資料值,這也是反爬蟲的一種初級技術手段。那麼為了防止該問題的出現,則我們可以給爬蟲程式的UA進行僞裝,僞裝成某款浏覽器的身份辨別。

   上述案例中,我們是通過request子產品中的urlopen發起的請求,該請求對象為urllib中内置的預設請求對象,我們無法對其進行UA進行更改操作。urllib還為我們提供了一種自定義請求對象的方式,我們可以通過自定義請求對象的方式,給該請求對象中的UA進行僞裝(更改)操作。

#封裝自定義的請求頭資訊的字典:

#将浏覽器的UA資料擷取,封裝到一個字典中。該UA值可以通過抓包工具或者浏覽器自帶的開發者工具中擷取某請求,從中擷取UA的值

#注意:在headers字典中可以封裝任意的請求頭資訊

headers={

    'User-Agent' : 'Mozilla/5.0

(Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko)

Chrome/68.0.3440.106 Safari/537.36'

    }

#自定義請求對象,可以在該請求對象中添加自定義的請求頭資訊

request = urllib.request.Request(url=url,headers=headers)

#使用自定義請求對象發起請求

response = urllib.request.urlopen(request)

  5.攜帶參數的post請求:

      案例:百度翻譯發起post請求

#通過抓包工具抓取post請求的url

post_url='https://fanyi.baidu.com/sug'

#封裝post請求參數

data={

"kw":"dog"

data=urllib.parse.urlencode(data)

#自定義請求頭資訊字典

    "User-Agent":

"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like

Gecko) Chrome/67.0.3396.87 Safari/537.36"

#自定義請求對象,然後将封裝好的post請求參數指派給Requst方法的data參數。

#data參數:用來存儲post請求的參數

request=urllib.request.Request(post_url,data=data.encode(),headers=headers)

#自定義的請求對象中的參數(data必須為bytes類型)

response=urllib.request.urlopen(request)

response.read()

三.urllib子產品的進階操作

  1.代理

    - 什麼是代理:代理就是第三方代替本體處理相關事務。例如:生活中的代理:代購,中介,微商......

  - 爬蟲中為什麼需要使用代理?

    

一些網站會有相應的反爬蟲措施,例如很多網站會檢測某一段時間某個IP的通路次數,如果通路頻率太快以至于看起來不像正常訪客,它可能就會會禁止這個IP的通路。是以我們需要設定一些代理IP,每隔一段時間換一個代理IP,就算IP被禁止,依然可以換個IP繼續爬取。

   - 代理的分類:

    正向代理:代理用戶端擷取資料。正向代理是為了保護用戶端防止被追究責任。

    反向代理:代理伺服器提供資料。反向代理是為了保護伺服器或負責負載均衡。

import urllib.request
import urllib.parse

#1.建立處理器對象,在其内部封裝代理ip和端口
handler=urllib.request.ProxyHandler(proxies={'http':'95.172.58.224:52608'})
#2.建立opener對象,然後使用該對象發起一個請求
opener=urllib.request.build_opener(handler)

url='http://www.baidu.com/s?ie=UTF-8&wd=ip'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
}

request = urllib.request.Request(url, headers=headers)

#使用opener對象發起請求,該請求對應的ip即為我們設定的代理ip
response = opener.open(request)

with open('./daili.html','wb') as fp:
    fp.write(response.read())
               

22

1

import urllib.request      

2

import urllib.parse      

3

4

#1.建立處理器對象,在其内部封裝代理ip和端口      

5

handler=urllib.request.ProxyHandler(proxies={'http':'95.172.58.224:52608'})      

6

#2.建立opener對象,然後使用該對象發起一個請求      

7

opener=urllib.request.build_opener(handler)      

8

9

url='http://www.baidu.com/s?ie=UTF-8&wd=ip'      

10

11

headers = {      

12

   'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',      

13

}      

14

15

request = urllib.request.Request(url, headers=headers)      

16

17

#使用opener對象發起請求,該請求對應的ip即為我們設定的代理ip      

18

response = opener.open(request)      

19

20

with open('./daili.html','wb') as fp:      

21

   fp.write(response.read())      

  2.cookie

   引言:有些時候,我們在使用爬蟲程式去爬取一些使用者相關資訊的資料(爬取張三“人人網”個人首頁資料)時,如果使用之前requests子產品正常操作時,往往達不到我們想要的目的,例如:

import urllib.request
import urllib.parse
#指定url
url = 'http://www.renren.com/289676607/profile'
#自定義請求頭資訊
headers={
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
    }
#自定義請求對象
request = urllib.request.Request(url=url,headers=headers)
#發起請求
response = urllib.request.urlopen(request)

with open('./renren.html','w') as fp:
    fp.write(response.read().decode())           
import urllib.request      
import urllib.parse      
#指定url      
url = 'http://www.renren.com/289676607/profile'      
#自定義請求頭資訊      
headers={      
   'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',      
   }      
#自定義請求對象      
request = urllib.request.Request(url=url,headers=headers)      
#發起請求      
response = urllib.request.urlopen(request)      
with open('./renren.html','w') as fp:      
   fp.write(response.read().decode())      

【注意】上述代碼中,我們爬取到的是登入首頁面,而不是張三的個人首頁也面。why?首先我們來回顧下cookie的相關概念及作用

      -

cookie概念:當使用者通過浏覽器首次通路一個域名時,通路的web伺服器會給用戶端發送資料,以保持web伺服器與用戶端之間的狀态保持,這些資料就是cookie。

- cookie作用:我們在浏覽器中,經常涉及到資料的交換,比如你登入郵箱,登入一個頁面。我們經常會在此時設定30天内記住我,或者自動登入選項。那麼它們是怎麼記錄資訊的呢,答案就是今天的主角cookie了,Cookie是由HTTP伺服器設定的,儲存在浏覽器中,但HTTP協定是一種無狀态協定,在資料交換完畢後,伺服器端和用戶端的連結就會關閉,每次交換資料都需要建立新的連結。就像我們去超市買東西,沒有積分卡的情況下,我們買完東西之後,超市沒有我們的任何消費資訊,但我們辦了積分卡之後,超市就有了我們的消費資訊。cookie就像是積分卡,可以儲存積分,商品就是我們的資訊,超市的系統就像伺服器背景,http協定就是交易的過程。 

經過cookie的相關介紹,其實你已經知道了為什麼上述案例中爬取到的不是張三個人資訊頁,而是登入頁面。那應該如何抓取到張三的個人資訊頁呢?

  思路:

    1.我們需要使用爬蟲程式對人人網的登入時的請求進行一次抓取,擷取請求中的cookie資料

    2.在使用個人資訊頁的url進行請求時,該請求需要攜帶 1

中的cookie,隻有攜帶了cookie後,伺服器才可識别這次請求的使用者資訊,方可響應回指定的使用者資訊頁資料

cookiejar對象:
    - 作用:自動儲存請求中的cookie資料資訊
    - 注意:必須和handler和opener一起使用
cookiejar使用流程:
    - 建立一個cookiejar對象
      import http.cookiejar
      cj = http.cookiejar.CookieJar()
    - 通過cookiejar建立一個handler
      handler = urllib.request.HTTPCookieProcessor(cj)
    - 根據handler建立一個opener
      opener = urllib.request.build_opener(handler)
    - 使用opener.open方法去發送請求,且将響應中的cookie存儲到openner對象中,後續的請求如果使用openner發起,則請求中就會攜帶了cookie           
cookiejar對象:      
   - 作用:自動儲存請求中的cookie資料資訊      
   - 注意:必須和handler和opener一起使用      
cookiejar使用流程:      
   - 建立一個cookiejar對象      
     import http.cookiejar      
     cj = http.cookiejar.CookieJar()      
   - 通過cookiejar建立一個handler      
     handler = urllib.request.HTTPCookieProcessor(cj)      
   - 根據handler建立一個opener      
     opener = urllib.request.build_opener(handler)      
   - 使用opener.open方法去發送請求,且将響應中的cookie存儲到openner對象中,後續的請求如果使用openner發起,則請求中就會攜帶了cookie      
import urllib.request
import urllib.parse
import http.cookiejar
cj = http.cookiejar.CookieJar() #請求中的cookie會自動存儲到cj對象中
#建立處理器對象(攜帶cookiejar對象的)
handler=urllib.request.HTTPCookieProcessor(cj)
#建立opener對象 (攜帶cookiejar對象)
opener=urllib.request.build_opener(handler)

#要讓cookiejar擷取請求中的cookie資料值
url='http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201873958471'
#自定義一個請求對象,讓該對象作為opener的open函數中的參數
data={
    "email":"[email protected]",
"icode":"",
"origURL":"http://www.renren.com/home",
    "domain":"renren.com",
"key_id":"1",
"captcha_type":"web_login",
    "password":"40dc65b82edd06d064b54a0fc6d202d8a58c4cb3d2942062f0f7dd128511fb9b",
"rkey":"41b44b0d062d3ca23119bc8b58983104",
  
 'f':"https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DpPKf2680yRLbbZMVdntJpyPGwrSk2BtpKlEaAuKFTsW%26wd%3D%26eqid%3Deee20f380002988c000000025b7cbb80"
}
data=urllib.parse.urlencode(data).encode()
request=urllib.request.Request(url,data=data)
opener.open(request)

#擷取目前使用者的二級子頁面
s_url='http://www.renren.com/289676607/profile'
#該次請求中就攜帶了cookie
resonse=opener.open(s_url)

with open('./renren.html','wb') as fp:
    fp.write(resonse.read())           

35

import urllib.request      
import urllib.parse      
import http.cookiejar      
cj = http.cookiejar.CookieJar() #請求中的cookie會自動存儲到cj對象中      
#建立處理器對象(攜帶cookiejar對象的)      
handler=urllib.request.HTTPCookieProcessor(cj)      
#建立opener對象 (攜帶cookiejar對象)      
opener=urllib.request.build_opener(handler)      
#要讓cookiejar擷取請求中的cookie資料值      
url='http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201873958471'      
#自定義一個請求對象,讓該對象作為opener的open函數中的參數      
data={      
   "email":"[email protected]",      
"icode":"",      
"origURL":"http://www.renren.com/home",      
   "domain":"renren.com",      
"key_id":"1",      
"captcha_type":"web_login",      
   "password":"40dc65b82edd06d064b54a0fc6d202d8a58c4cb3d2942062f0f7dd128511fb9b",      
"rkey":"41b44b0d062d3ca23119bc8b58983104",      

23

'f':"https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DpPKf2680yRLbbZMVdntJpyPGwrSk2BtpKlEaAuKFTsW%26wd%3D%26eqid%3Deee20f380002988c000000025b7cbb80"      

24

}      

25

data=urllib.parse.urlencode(data).encode()      

26

request=urllib.request.Request(url,data=data)      

27

opener.open(request)      

28

29

#擷取目前使用者的二級子頁面      

30

s_url='http://www.renren.com/289676607/profile'      

31

#該次請求中就攜帶了cookie      

32

resonse=opener.open(s_url)      

33

34

with open('./renren.html','wb') as fp:      
   fp.write(resonse.read())