終于要開始寫爬蟲代碼了
我們首先了解一下 Urllib 庫,它是 Python 內置的 HTTP 請求庫,也就是說我們不需要額外安裝即可使用,它包含四個模塊:
第一個模塊 request,它是最基本的 HTTP 請求模塊,我們可以用它來模擬發送一請求,就像在瀏覽器里輸入網址然后敲擊回車一樣,只需要給庫方法傳入 URL 還有額外的引數,就可以模擬實作這個程序了,
第二個 error 模塊即例外處理模塊,如果出現請求錯誤,我們可以捕獲這些例外,然后進行重試或其他操作保證程式不會意外終止,
第三個 parse 模塊是一個工具模塊,提供了許多 URL 處理方法,比如拆分、決議、合并等等的方法,
第四個模塊是 robotparser,主要是用來識別網站的 robots.txt 檔案,然后判斷哪些網站可以爬,哪些網站不可以爬的,其實用的比較少,
在這里重點對前三個模塊進行下講解,
一、發送請求
使用 Urllib 的 request 模塊我們可以方便地實作 Request 的發送并得到 Response
1、urlopen()
urllib.request 模塊提供了最基本的構造 HTTP 請求的方法,利用它可以模擬瀏覽器的一個請求發起程序,同時它還帶有處理authenticaton(授權驗證),redirections(重定向),cookies(瀏覽器Cookies)以及其它內容,
我們來感受一下它的強大之處,以 Python 官網為例,我們來把這個網頁抓下來:
import urllib.request
?response = urllib.request.urlopen('https://www.python.org')
print(response.read().decode('utf-8'))
運行結果如下:

接下來我們看下它回傳的到底是什么,利用 type() 方法輸出 Response 的型別,
import urllib.request?
response = urllib.request.urlopen('https://www.python.org')
print(type(response))
輸出結果如下:
<class 'http.client.HTTPResponse'>
通過輸出結果可以發現它是一個 HTTPResposne 型別的物件,它主要包含的方法有 read()、readinto()、getheader(name)、getheaders()、fileno() 等方法和 msg、version、status、reason、debuglevel、closed 等屬性,
得到這個物件之后,我們把它賦值為 response 變數,然后就可以呼叫這些方法和屬性,得到回傳結果的一系列資訊了,
例如呼叫 read() 方法可以得到回傳的網頁內容,呼叫 status 屬性就可以得到回傳結果的狀態碼,如 200 代表請求成功,404 代表網頁未找到等,
下面再來一個實體感受一下:
import urllib.request
?
response = urllib.request.urlopen('https://www.python.org')
print(response.status)
print(response.getheaders())
print(response.getheader('Server'))
運行結果如下:
200
[('Server', 'nginx'), ('Content-Type', 'text/html; charset=utf-8'), ('X-Frame-Options', 'SAMEORIGIN'), ('X-Clacks-Overhead', 'GNU Terry Pratchett'), ('Content-Length', '47397'), ('Accept-Ranges', 'bytes'), ('Date', 'Mon, 01 Aug 2016 09:57:31 GMT'), ('Via', '1.1 varnish'), ('Age', '2473'), ('Connection', 'close'), ('X-Served-By', 'cache-lcy1125-LCY'), ('X-Cache', 'HIT'), ('X-Cache-Hits', '23'), ('Vary', 'Cookie'), ('Strict-Transport-Security', 'max-age=63072000; includeSubDomains')]
nginx
可見,三個輸出分別輸出了回應的狀態碼,回應的頭資訊,以及通過呼叫 getheader() 方法并傳遞一個引數 Server 獲取了 headers 中的 Server 值,結果是 nginx,意思就是服務器是 nginx 搭建的,
利用以上最基本的 urlopen() 方法,我們可以完成最基本的簡單網頁的 GET 請求抓取,
如果我們想給鏈接傳遞一些引數該怎么實作呢?我們首先看一下 urlopen() 函式的API:
urllib.request.urlopen(url, data=https://www.cnblogs.com/bigzql/p/None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
可以發現除了第一個引數可以傳遞 URL 之外,我們還可以傳遞其它的內容,比如 data(附加資料)、timeout(超時時間)等等,
下面我們詳細說明下這幾個引數的用法:
data引數
data 引數是可選的,如果要添加 data,它要是位元組流編碼格式的內容,即 bytes 型別,通過 bytes() 方法可以進行轉化,另外如果傳遞了這個 data 引數,它的請求方式就不再是 GET 方式請求,而是 POST,
下面用一個實體來感受一下:
import urllib.parse
import urllib.request
?
data = https://www.cnblogs.com/bigzql/p/bytes(urllib.parse.urlencode({'word': 'hello'}), encoding='utf8')
response = urllib.request.urlopen('http://httpbin.org/post', data=https://www.cnblogs.com/bigzql/p/data)
print(response.read())
在這里我們傳遞了一個引數 word,值是 hello,它需要被轉碼成bytes(位元組流)型別,其中轉位元組流采用了 bytes() 方法
第一個引數需要是 str(字串)型別,需要用 urllib.parse 模塊里的 urlencode() 方法來將引數字典轉化為字串,
第二個引數指定編碼格式,在這里指定為 utf8,
timeout引數
timeout 引數可以設定超時時間,單位為秒,意思就是如果請求超出了設定的這個時間還沒有得到回應,就會拋出例外,如果不指定,就會使用全域默認時間,它支持 HTTP、HTTPS、FTP 請求,
因此我們可以通過設定這個超時時間來控制一個網頁如果長時間未回應就跳過它的抓取,利用 try except 陳述句就可以實作這樣的操作,代碼如下:
import socket
import urllib.request
import urllib.error
?
try:
response = urllib.request.urlopen('http://httpbin.org/get', timeout=0.1)
except urllib.error.URLError as e:
if isinstance(e.reason, socket.timeout):
print('TIME OUT')
其他引數
還有 context 引數,它必須是 ssl.SSLContext 型別,用來指定 SSL 設定,
cafile 和 capath 兩個引數是指定 CA 證書和它的路徑,這個在請求 HTTPS 鏈接時會有用,
cadefault 引數現在已經棄用了,默認為 False,
以上講解了 urlopen() 方法的用法,通過這個最基本的函式可以完成簡單的請求和網頁抓取,如需更加詳細了解,可以參見官方檔案:
https://docs.python.org/3/library/urllib.request.html,
2. Request
由上我們知道利用 urlopen() 方法可以實作最基本請求的發起,但這幾個簡單的引數并不足以構建一個完整的請求,如果請求中需要加入 Headers 等資訊,我們就可以利用更強大的 Request 類來構建一個請求,
首先我們用一個實體來感受一下 Request 的用法:
import urllib.request
request = urllib.request.Request('https://python.org')
response = urllib.request.urlopen(request)
print(response.read().decode('utf-8'))
可以發現,我們依然是用 urlopen() 方法來發送這個請求,只不過這次 urlopen() 方法的引數不再是一個 URL,而是一個 Request 型別的物件,通過構造這個這個資料結構,一方面我們可以將請求獨立成一個物件,另一方面可配置引數更加豐富和靈活,
下面我們看一下 Request 都可以通過怎樣的引數來構造,它的構造方法如下:
class urllib.request.Request(url, data=https://www.cnblogs.com/bigzql/p/None, headers={}, origin_req_host=None, unverifiable=False, method=None)
第一個 url 引數是請求 URL,這個是必傳引數,其他的都是可選引數,
第二個 data 引數如果要傳必須傳 bytes(位元組流)型別的,如果是一個字典,可以先用 urllib.parse 模塊里的 urlencode() 編碼,
第三個 headers 引數是一個字典,這個就是 Request Headers 了,你可以在構造 Request 時通過 headers 引數直接構造,
也可以通過呼叫 Request 實體的 add_header() 方法來添加, Request Headers 最常用的用法就是通過修改 User-Agent 來偽裝瀏覽器,默認的 User-Agent 是 Python-urllib,我們可以通過修改它來偽裝瀏覽器,
第四個 origin_req_host 引數指的是請求方的 host 名稱或者 IP 地址,
第五個 unverifiable 引數指的是這個請求是否是無法驗證的,默認是False,意思就是說用戶沒有足夠權限來選擇接收這個請求的結果,例如我們請求一個 HTML 檔案中的圖片,但是我們沒有自動抓取影像的權限,這時 unverifiable 的值就是 True,
第六個 method 引數是一個字串,它用來指示請求使用的方法,比如GET,POST,PUT等等,
寫個例子:
from urllib import request, parse
?
url = 'http://httpbin.org/post'
headers = {
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
'Host': 'httpbin.org'
}
dict = {
'name': 'Germey'
}
data = https://www.cnblogs.com/bigzql/p/bytes(parse.urlencode(dict), encoding='utf8')
req = request.Request(url=url, data=https://www.cnblogs.com/bigzql/p/data, headers=headers, method='POST')
response = request.urlopen(req)
print(response.read().decode('utf-8'))
在這里我們通過四個引數構造了一個 Request,url 即請求 URL,在headers 中指定了 User-Agent 和 Host,傳遞的引數 data 用了 urlencode() 和 bytes() 方法來轉成位元組流,另外指定了請求方式為 POST,
通過觀察結果可以發現,我們成功設定了 data,headers 以及 method,
另外 headers 也可以用 add_header() 方法來添加,
req = request.Request(url=url, data=https://www.cnblogs.com/bigzql/p/data, method='POST')
req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)')
如此一來,我們就可以更加方便地構造一個 Request,實作請求的發送啦,
二、 處理例外
我們了解了 Request 的發送程序,但是在網路情況不好的情況下,出現了例外怎么辦呢?這時如果我們不處理這些例外,程式很可能報錯而終止運行,所以例外處理還是十分有必要的,
Urllib 的 error 模塊定義了由 request 模塊產生的例外,如果出現了問題,request 模塊便會拋出 error 模塊中定義的例外,
主要有這兩個處理例外類,URLError,HTTPError
下面寫個例子:
from urllib import request, error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.URLError as e:
print(e.reason)
我們打開一個不存在的頁面,照理來說應該會報錯,但是這時我們捕獲了 URLError 這個例外,運行結果如下:
Not Found
from urllib import request,error
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, seq='\n')
運行結果:
Not Found
404
Server: nginx/1.4.6 (Ubuntu)
Date: Wed, 03 Aug 2016 08:54:22 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Vary: Cookie
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Link: <http://cuiqingcai.com/wp-json/>; rel="https://api.w.org/"
HTTPError,它有三個屬性,
-
code,回傳 HTTP Status Code,即狀態碼,比如 404 網頁不存在,500 服務器內部錯誤等等,
-
reason,同父類一樣,回傳錯誤的原因,
-
headers,回傳 Request Headers,
因為 URLError 是 HTTPError 的父類,所以我們可以先選擇捕獲子類的錯誤,再去捕獲父類的錯誤,所以上述代碼更好的寫法如下:
from urllib import request, error
?
try:
response = request.urlopen('http://cuiqingcai.com/index.htm')
except error.HTTPError as e:
print(e.reason, e.code, e.headers, sep='\n')
except error.URLError as e:
print(e.reason)
else:
print('Request Successfully')
這樣我們就可以做到先捕獲 HTTPError,獲取它的錯誤狀態碼、原因、Headers 等詳細資訊,如果非 HTTPError,再捕獲 URLError 例外,輸出錯誤原因,最后用 else 來處理正常的邏輯,這是一個較好的例外處理寫法,
三、 實體抓取豆瓣電影排行榜
import urllib.parse
import urllib.request
url='https://movie.douban.com/'
herders={
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
'Referer':'https://movie.douban.com/',
'Connection':'keep-alive'}
?
req=urllib.request.Request(url,headers=herders)
response=urllib.request.urlopen(req)
html=response.read().decode('utf8')
print(html)
結果
<!DOCTYPE html>
<html lang="zh-CN" >
<head>
...
<title>豆瓣電影</title>
?
<meta name="keywords" content="電影、經典電影、熱映、電視劇、美劇、影評、電影院、電影票、排行、推薦"/>
<meta name="description" content="豆瓣電影提供最新的電影介紹及評論包括上映影片的影訊查詢及購票服務,你可以記錄想看、在看和看過的電影電視劇,順便打分、寫影評,根據你的口味,豆瓣電影會推薦好電影給你," />
....
以下省略
四、分析Robots協議
利用urllib的robotparser模塊,我們可以實作網站Robots協議的分析
1Robots協議
Robots協議也稱為爬蟲協議、機器人協議,它的全名叫做網路爬蟲排除標準(Robots Exclusion Protocol),用來告訴爬蟲和搜索引擎哪些網頁可以抓取,哪些不可以抓取,它通常是一個robots.txt的文本檔案,一般放在網站的根目錄下,
當搜索爬蟲訪問一個站點時,它首先會檢查這個站點根目錄下是否存在robots.txt檔案,如果存在,搜索爬蟲會根據其中定義的爬去范圍來爬取,如果沒有找到,搜索爬蟲會訪問所有可直接訪問的頁面
我們來看下robots.txt的樣例:
User-agent: *
Disallow: /
Allow: /public/
它實作了對所有搜索爬蟲只允許爬取public目錄的功能,將上述內容保存為robots.txt檔案放在網站根目錄下,和網站的入口檔案(index.html)放在一起
User-agent描述了搜索爬蟲的名稱,將其設定為*則代表協議對任何爬蟲有效,如設定為Baiduspider則代表規則對百度爬蟲有效,如果有多條則對多個爬蟲受到限制,但至少需要指定一條
一些常見的搜索爬蟲名稱:
BaiduSpider 百度爬蟲 www.baidu.com
Googlebot Google爬蟲 www.google.com
360Spider 360爬蟲 www.so.com
YodaoBot 有道爬蟲 www.youdao.com
ia_archiver Alexa爬蟲 www.alexa.cn
Scooter altavista爬蟲 www.altavista.com
Disallow指定了不允許抓取的目錄,如上例中設定的/則代表不允許抓取所有的頁面
Allow一般和Disallow一起使用,用來排除單獨的某些限制,如上例中設定為/public/則表示所有頁面不允許抓取,但可以抓取public目錄
設定示例:
#禁止所有爬蟲
User-agent: *
Disallow: /
?
#允許所有爬蟲訪問任何目錄,另外把檔案留空也可以
User-agent: *
Disallow:
?
#禁止所有爬蟲訪問某那些目錄
User-agent: *
Disallow: /home/
Disallow: /tmp/
?
#只允許某一個爬蟲訪問
User-agent: BaiduSpider
Disallow:
User-agent: *
Disallow: /
2 robotparser
rebotparser模塊用來決議robots.txt,該模塊提供了一個類RobotFileParser,它可以根據某網站的robots.txt檔案來判斷一個抓取爬蟲時都有權限來抓取這個網頁
urllib.robotparser.RobotFileParser(url='')
robotparser類常用的方法:
set_url():用來設定robots.txt檔案的連接,如果在創建RobotFileParser物件是傳入了連接,就不需要在使用這個方法設定了
read():讀取reobts.txt檔案并進行分析,它不會回傳任何內容,但執行那個了讀取和分析操作
parse():用來決議robots.txt檔案,傳入的引數是robots.txt某些行的內容,并安裝語法規則來分析內容
can_fetch():該方法傳入兩個引數,第一個是User-agent,第二個是要抓取的URL,回傳的內容是該搜索引擎是否可以抓取這個url,結果為True或False
mtime():回傳上次抓取和分析robots.txt的時間
modified():將當前時間設定為上次抓取和分析robots.txt的時間
#!/usr/bin/env python
#coding:utf8
from urllib.robotparser import RobotFileParser
rp = RobotFileParser() #創建物件
rp.set_url('https://www.cnblogs.com/robots.txt') #設定robots.txt連接,也可以在創建物件時指定
rp.read() #讀取和決議檔案
print(rp.can_fetch('*','https://i.cnblogs.com/EditPosts.aspx?postid=9170312&update=1'))
希望各位同學以后在寫爬蟲時候可以遵循 Robot 協議,為了自己的安全著想,

IT入門 感謝關注 | 練習地址:www.520mg.com/it
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/167652.html
標籤:Python
上一篇:提升Python性能的7個習慣
下一篇:桶排序的簡單實作
