添加超時跳過功能
首先, 我簡單地將
<code>urlop = urllib.request.urlopen(url)/<code>
改為
<code>urlop = urllib.request.urlopen(url, timeout = 2)/<code>
運行後發現, 當發生超時, 程序因為exception中斷. 於是我把這一句也放在try .. except 結構裡, 問題解決.
支持自動跳轉
在爬 http://baidu.com 的時候, 爬回來一個沒有什麼內容的東西, 這個東西告訴我們應該跳轉到 百度一下,你就知道 . 但是我們的爬蟲並不支持自動跳轉, 現在我們來加上這個功能, 讓爬蟲在爬 baidu.com 的時候能夠抓取 百度一下,你就知道 的內容.
首先我們要知道爬 http://baidu.com 的時候他返回的頁面是怎麼樣的, 這個我們既可以用 Fiddler 看, 也可以寫一個小爬蟲來抓取. 這裡我抓到的內容如下, 你也應該嘗試一下寫幾行 python 來抓一抓.
<code>/<code>
看代碼我們知道這是一個利用 html 的 meta 來刷新與重定向的代碼, 其中的0是等待0秒後跳轉, 也就是立即跳轉. 這樣我們再像上一次說的那樣用一個正則表達式把這個url提取出來就可以爬到正確的地方去了. 其實我們上一次寫的爬蟲已經可以具有這個功能, 這裡只是單獨拿出來說明一下 http 的 meta 跳轉.
偽裝瀏覽器正規軍
前面幾個小內容都寫的比較少. 現在詳細研究一下如何讓網站們把我們的Python爬蟲當成正規的瀏覽器來訪. 因為如果不這麼偽裝自己, 有的網站就爬不回來了. 如果看過理論方面的知識, 就知道我們是要在 GET 的時候將 User-Agent 添加到header裡.
如果沒有看過理論知識, 按照以下關鍵字搜索學習吧 :D
HTTP 報文分兩種: 請求報文和響應報文
請求報文的請求行與首部行
GET, POST, HEAD, PUT, DELETE 方法
我用 IE 瀏覽器訪問百度首頁的時候, 瀏覽器發出去的請求報文如下:
GET 百度一下,你就知道 HTTP/1.1
<code>Accept: text/html, application/xhtml+xml, */*Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like GeckoAccept-Encoding: gzip, deflate/<code>
<code>DNT: 1Connection: Keep-AliveCookie: BAIDUID=57F4D171573A6B88A68789EF5DDFE87:FG=1; uc_login_unique=ccba6e8d978872d57c7654130e714abd; BD_UPN=11263145; BD/<code>
然後百度收到這個消息後, 返回給我的的響應報文如下(有刪節):
<code>HTTP/1.1 200 OKDate: Mon, 29 Sep 2014 13:07:01 GMTContent-Type: text/html; charset=utf-8Connection: Keep-AliveVary: Accept-EncodingCache-Control: privateCxy_all: baidu+8b13ba5a7289a37fb380e0324ad688e7Expires: Mon, 29 Sep 2014 13:06:21 GMTX-Powered-By: HPHPServer: BWS/1.1BDPAGETYPE: 1BDQID: 0x8d15bb610001fe79BDUSERID: 0Set-Cookie: BDSVRTM=0; path=/Set-Cookie: BD_HOME=0; path=/Content-Length: 80137<link><link><link><link><link><link><link><link><title>百度一下,你就知道/<title><style> ……….這裡省略兩萬字……………. </script></body></html></code></pre><p>如果能夠看懂這段話的第一句就OK了, 別的可以以後再配合 Fiddler 慢慢研究. 所以我們要做的就是在 Python 爬蟲向百度發起請求的時候, 順便在請求裡面寫上 User-Agent, 表明自己是瀏覽器君.</p><p><strong>在 GET 的時候添加 header 有很多方法, 下面介紹兩種方法.</strong></p><p>第一種方法比較簡便直接, 但是不好擴展功能, 代碼如下:</p><pre><code>import urllib.request url = '百度一下,你就知道'req = urllib.request.Request(url, headers = { 'Connection': 'Keep-Alive', 'Accept': 'text/html, application/xhtml+xml, */*', 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'})oper = urllib.request.urlopen(req)data = oper.read()print(data.decode())第二種方法使用了 build_opener 這個方法, 用來自定義 opener, 這種方法的好處是可以方便的拓展功能, 例如下面的代碼就拓展了自動處理 Cookies 的功能.import urllib.requestimport http.cookiejar # head: dict of headerdef makeMyOpener(head = { 'Connection': 'Keep-Alive', 'Accept': 'text/html, application/xhtml+xml, */*', 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'}): cj = http.cookiejar.CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) header = [] for key, value in head.items(): elem = (key, value) header.append(elem) opener.addheaders = header return opener oper = makeMyOpener()uop = oper.open('百度一下,你就知道', timeout = 1000)data = uop.read()print(data.decode())上述代碼運行後通過 Fiddler 抓到的 GET 報文如下所示:GET 百度一下,你就知道 HTTP/1.1Accept-Encoding: identityConnection: closeHost: 百度一下,你就知道User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like GeckoAccept: text/html, application/xhtml+xml, */*Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3</code></pre><p>可見我們在代碼裡寫的東西都添加到請求報文裡面了.</p><p><strong>保存抓回來的報文</strong></p><p>順便說說文件操作. Python 的文件操作還是相當方便的. 我們可以講抓回來的數據 data 以二進制形式保存, 也可以經過 decode() 處理成為字符串後以文本形式保存. 改動一下打開文件的方式就能用不同的姿勢保存文件了. 下面是參考代碼:</p><pre><code>def saveFile(data): save_path = 'D:\\temp.out' f_obj = open(save_path, 'wb') # wb 表示打開方式 f_obj.write(data) f_obj.close() # 這裡省略爬蟲代碼# ... # 爬到的數據放到 dat 變量裡# 將 dat 變量保存到 D 盤下saveFile(dat)</code></pre>"/<style>/<code>
閱讀更多 程序猿南鶴 的文章