天天看點

Python爬蟲(二)urllib庫的使用

了解了爬蟲的基本原理後,接下來我們就可以爬取網頁内容。網頁其實是由HTML代碼和JS、CSS等組成的。urllib是python提供的HTTP請求庫,它有許多子產品供我們爬取使用。

urllib.request

首先使用urlopen打開一個url,可以擷取頁面的源代碼。

import urllib.request

response=urllib.request.urlopen("https://www.baidu.com/")
print(response.read())  
           

結果如下所示:

Python爬蟲(二)urllib庫的使用

urlopen函數的為

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

其中url為通路的網址;data為通路時需要傳輸的資料;timeout為逾時時間,當超過此時間伺服器未響應時即為通路失敗;cafile和capath為HTTP請求指定一組受信的CA憑證,在此先不管它。除了第一個參數之外,後四個參數都可以不設定。

除此之外,urlopen函數可以直接接受一個Request對象:

class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False)   
           

同樣地,隻有第一個參數不可省略。是以上述代碼可以換成:

import urllib.request

request=urllib.request.Request("https://www.baidu.com/")
response=urllib.request.urlopen(request)
print(response.read())  
           

然而有的網頁是動态網頁,需要根據傳的資料做出相應的響應。HTTP請求傳送資料時有POST和GET兩種方式。GET方式是直接以連結形式通路,連結中包含了所有的參數,可以直覺地檢視自己送出的内容,但是當資料中包含密碼時就涉及到了安全問題。POST相反,它不會在網址上顯示所有的參數。

下面展示這兩種資料傳送的用法。

POST:

import urllib.request
import urllib.parse
params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
print(f.read().decode('utf-8'))
           

GET:

import urllib.request
import urllib.parse

params = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
params = params.encode('utf-8')
f = urllib.request.urlopen("http://www.musi-cal.com/cgi-bin/query", params)
print(f.read().decode('utf-8'))   
           

request headers

在前面講到建構Request對象時可以設定headers。那麼headers是幹什麼用的呢。

下面是我使用Chrome浏覽器通路百度的一個request headers。

Python爬蟲(二)urllib庫的使用

可以看到它包含但不僅限于這些内容:

-  Accept:浏覽器端可以接受的媒體類型。
-  Accept-Encoding:浏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支援壓縮,支援什麼壓縮方法(gzip,deflate)  
-  Accept-Language:浏覽器申明自己接收的語言。  
-  Host:請求報頭域主要用于指定被請求資源的Internet主機和端口号,它通常從HTTP URL中提取出來的。  
-  Referer:告訴伺服器我是從哪個頁面連結過來的。  
-  User-Agent:告訴HTTP伺服器, 用戶端使用的作業系統和浏覽器的名稱和版本。
           

在使用python進行爬蟲的時候,可以會遇到一些網站的反爬蟲措施。一般伺服器會根據User-Agent的值來判斷請求是否從浏覽器發出。如果沒有對headers進行設定,User-Agent會聲明自己為python腳本,對于有反爬蟲措施的伺服器,它就會拒絕爬蟲程式通路。而通過設定headers就可以僞裝成浏覽器進行通路。

下面為設定headers的例子。

import urllib.request

def parse(url):
    headers = {'Accept': '*/*',
               'Accept-Language': 'en-US,en;q=0.8',
               'Cache-Control': 'max-age=0',
               'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36',
               'Connection': 'keep-alive',
               'Referer': 'http://www.baidu.com/'
               }
    req = urllib.request.Request(url, None, headers)
    response = urllib.request.urlopen(req)
    page_source = (response.read())
    return page_source
           

設定代理

一些網站的反爬蟲措施會檢測一段時間内某個ip的通路次數,如果通路頻率過高,它會禁止此ip的通路。是以我們可以設定一個代理伺服器,隔一定的時間更換一個代理。

Python3中提供了ProxyHandler設定代理伺服器,廢話不多說,我們還是通過一個例子來看看如何使用代理。

import urllib.request

proxy_handler = urllib.request.ProxyHandler({"http" : "117.87.176.131"})
null_handler = urllib.request.ProxyHandler({})

tag = True 
if tag:  
    opener = urllib.request.build_opener(proxy_handler)
else:
    opener = urllib.request.build_opener(null_handler)

request = urllib.request.Request("http://www.baidu.com/")

response = opener.open(request)

# urllib.request.install_opener(opener)
# response = urllib.request.urlopen(request)

print(response.read())     
           

程式先建構了兩個代理handler,一個有代理IP,一個沒有代理IP。通過urllib2.build_opener()方法建立OpenerDirector執行個體,該對象按給定參數順序處理這些Handler對象。然後使用OpenerDirector.open()方法發送請求才可以使用自定義的代理。這裡要注意的一點是直接使用urlopen是不使用自定義代理的,必須先使用install_opener()方法将OpenerDirector對象作為預設的全局opener,之後不管是使用opener.open()還是urlopen() 發送請求,都将使用自定義代理。

urllib.error

urllib.error定義了由urllib.request引起的異常的異常類。基本異常類是URLError,它是繼承自IOError。

  1. exception urllib.error.URLError

這個産生的原因可能是網絡無連接配接、連接配接不到伺服器或者是伺服器不存在,我們可以使用try-except捕獲異常并且分析異常原因。

from urllib import request
from urllib import error

req=request.Request("https://www.xxx.com/")
try:
	response=request.urlopen(req)
except error.URLError as e:
	print(e.reason)   
           

我們通路了一個不存在的網址,輸出結果為:

Python爬蟲(二)urllib庫的使用
  1. exception urllib.error.HTTPError

URLError的一個子類,當使用urlopen()發送請求時,伺服器會有一個應答對象response,同時包含着一個狀态碼。當不能對請求進行處理時,它會産生一個HTTPError,對應一個狀态碼表示響應的狀态。

from urllib import request
from urllib import error

req=request.Request("http://blog.csdn.net/dxk.html")
try:
	response=request.urlopen(req)
except error.HTTPError as e:
	print(e.code)   
           

結果如下:

404
           

這表示請求的資源沒有在伺服器上找到。

由于HTTPError是URLError的子類,是以當我們需要同時捕捉這兩個異常時,我們要特别注意将父類異常URLError寫在子類異常HTTPError的後面。如果URLError放在前面,出現HTTP異常會先響應URLError,這樣HTTPError就捕獲不到錯誤資訊了。

我們看下面這個例子:

from urllib import request
from urllib import error

req=request.Request("http://blog.csdn.net/dxk.html")
try:
	response=request.urlopen(req)
except error.HTTPError as e:
	print(e.code)
	print("HTTPError")
except error.URLError as e:
	print(e.reason)
	print("URLError")
           

如果捕捉到了HTTPError,則輸出code,不再處理URLError。若沒有發生HTTPError,則捕捉URLError異常,并輸出原因。

結果如下:

404  
HTTPError