最近需要對節點到源站自己做個監控,簡單的ping可以檢測到一些東西,但是http請求的檢查也要進行,於是就研究了下pycurl
pycurl是個用c語言實現的python 庫,雖然據說不是那么pythonic,但是卻很高效,它支持的協議居多:
supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, kerberos, HTTP form based upload, proxies, cookies, user+password authentication, file transfer resume, http proxy tunneling and more!
這一堆協議已經很多了,我需要就是http一個,相對urlib來說,這個庫可能更快些。
以下這個腳本是對某一個給定的url進行檢查,並打印出http相應碼,響應大小,建立連接時間,准備傳輸時間,傳輸第一個字節時間,完成時間
#!/usr/bin/python # coding: UTF-8 import StringIO import pycurl import sys import os class Test: def __init__(self): self.contents = '' def body_callback(self,buf): self.contents = self.contents + buf
def test_gzip(input_url): t = Test() #gzip_test = file("gzip_test.txt", 'w') c = pycurl.Curl() c.setopt(pycurl.WRITEFUNCTION,t.body_callback) c.setopt(pycurl.ENCODING, 'gzip') c.setopt(pycurl.URL,input_url) c.perform() http_code = c.getinfo(pycurl.HTTP_CODE) http_conn_time = c.getinfo(pycurl.CONNECT_TIME) http_pre_tran = c.getinfo(pycurl.PRETRANSFER_TIME) http_start_tran = c.getinfo(pycurl.STARTTRANSFER_TIME) http_total_time = c.getinfo(pycurl.TOTAL_TIME) http_size = c.getinfo(pycurl.SIZE_DOWNLOAD) print 'http_code http_size conn_time pre_tran start_tran total_time' print "%d %d %f %f %f %f"%(http_code,http_size,http_conn_time,http_pre_tran,http_start_tran,http_total_time)
if __name__ == '__main__': input_url = sys.argv[1] test_gzip(input_url) |
腳本運行效果
xu:~/curl$ python pycurl_test.py http://daxuxu.info/ http_code http_size conn_time pre_tran start_tran total_time 200 8703 0.748147 0.748170 1.632642 1.636552 |
pycurl 的一些響應信息:
(參考: http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html )
pycurl.NAMELOOKUP_TIME 域名解析時間
pycurl.CONNECT_TIME 遠程服務器連接時間
pycurl.PRETRANSFER_TIME 連接上后到開始傳輸時的時間
pycurl.STARTTRANSFER_TIME 接收到第一個字節的時間
pycurl.TOTAL_TIME 上一請求總的時間
pycurl.REDIRECT_TIME 如果存在轉向的話,花費的時間
pycurl.EFFECTIVE_URL
pycurl.HTTP_CODE HTTP 響應代碼
pycurl.REDIRECT_COUNT 重定向的次數
pycurl.SIZE_UPLOAD 上傳的數據大小
pycurl.SIZE_DOWNLOAD 下載的數據大小
pycurl.SPEED_UPLOAD 上傳速度
pycurl.HEADER_SIZE 頭部大小
pycurl.REQUEST_SIZE 請求大小
pycurl.CONTENT_LENGTH_DOWNLOAD 下載內容長度
pycurl.CONTENT_LENGTH_UPLOAD 上傳內容長度
pycurl.CONTENT_TYPE 內容的類型
pycurl.RESPONSE_CODE 響應代碼
pycurl.SPEED_DOWNLOAD 下載速度
pycurl.SSL_VERIFYRESULT
pycurl.INFO_FILETIME 文件的時間信息
pycurl.HTTP_CONNECTCODE HTTP 連接代碼
pycurl.HTTPAUTH_AVAIL
pycurl.PROXYAUTH_AVAIL
pycurl.OS_ERRNO
pycurl.NUM_CONNECTS
pycurl.SSL_ENGINES
pycurl.INFO_COOKIELIST
pycurl.LASTSOCKET
pycurl.FTP_ENTRY_PATH
########################################################
前幾天看完《Python簡明教程》,預想練手,想起同事的一個 ruby 代碼,嘗試改寫成 python,順便看看兩個語言的簡練程度。下面是原始的 ruby 代碼:
#!/usr/bin/env ruby require 'rubygems' require 'net/http' urls = ["http://icyleaf.com"] .times do urls.each do |url| start_at = Time.now Net::HTTP.get URI.parse(url) end_at = Time.now diff = end_at - start_at if diff <</span> . then color_code = elsif diff > . then color_code = else color_code = end puts "#{url}\n time: \033[#{color_code}m#{diff}\033[0m seconds" end end |
改寫 python 的同時,考慮腳本的靈活性准備增加兩個參數,第一個是請求測試次數,第二個是請求測試的 URL,而 python 默認提供了argparse 庫,可以很方便的生成 --help 的幫助和解析傳遞的參數:
#!/usr/bin/env python import urllib2 import time import sys import argparse def benchmark(url, count): for i in range(count): s = time.time() r = urllib2.urlopen(urllib2.Request(url)) e = time.time() diff = e - s if diff <</span> 0.3: color_code = elif diff > 0.8: color_code = else: color_code = print '# %d' % (i + ) print '\tStauts: %s' % r.getcode() print '\tTime: \033[%dm%f\033[0m second(s)' % (color_code, diff) def main(argv): parser = argparse.ArgumentParser(description='url request time test') parser.add_argument('URL', help='request url') parser.add_argument('-t', '--time', action='store', dest='count', type=int, default=, help='request times') args = parser.parse_args(argv) benchmark(args.URL, args.count) if __name__ == '__main__': main(sys.argv[:]) |
當然,我主要是為了練手 python 才去寫的,ruby 本身也有 optparse 庫用於解析參數,但是需要自己手寫生成 --help 的輸出,而且需要對每個參數做相應的 callback。
效果如下:
Posted by icyleaf on 2012-08-02
#########################################################
用Python編一個抓網頁的程序是非常快的,下面就是一個例子:
importurllib2
html=urllib2.urlopen('http://www.server110.com').read() |
但是在實際工作中,這種寫法是遠遠不夠的,至少會遇到下面幾個問題:
網絡會出錯,任何錯誤都可能。例如機器宕了,網線斷了,域名出錯了,網絡超時了,頁面沒有了,網站跳轉了,服務被禁了,主機負載不夠了…
服務器加上了限制,只讓常見瀏覽器訪問
服務器加上了防盜鏈的限制
某些2B網站不管你HTTP請求里有沒有Accept-Encoding頭部,也不管你頭部具體內容是什么,反正總給你發gzip后的內容
URL鏈接千奇百怪,帶漢字的也罷了,有的甚至還有回車換行
某些網站HTTP頭部里有一個Content-Type,網頁里有好幾個Content-Type,更過分的是,各個Content-Type還不一樣,最過分的是,這些Content-Type可能都不是正文里使用的Content-Type,從而導致亂碼
網絡鏈接很慢,乘分析幾千個頁面的時間,建議你可以好好吃頓飯去了
Python本身的接口有點糙
好吧,這么一大籮筐問題,我們來一個個搞定。
錯誤處理和服務器限制
首先是錯誤處理。由於urlopen本身將大部分錯誤,甚至包括4XX和5XX的HTTP響應,也搞成了異常,因此我們只需要捕捉異常就好了。同時,我們也可以獲取urlopen返回的響應對象,讀取它的HTTP狀態碼。除此之外,我們在urlopen的時候,也需要設置timeout參數,以保證處理好超時。下面是代碼示例:
|
importurllib2 importsocket
try: f=urllib2.urlopen('http://www.server110.com',timeout=10) code=f.getcode() ifcode<</span>200orcode>=300: #你自己的HTTP錯誤處理 exceptException,e: ifisinstance(e,urllib2.HTTPError): print'http error: {0}'.format(e.code) elifisinstance(e,urllib2.URLError)andisinstance(e.reason,socket.timeout): print'url error: socket timeout {0}'.format(e.__str__()) else: print'misc error: '+e.__str__() |
如果是服務器的限制,一般來說我們都可以通過查看真實瀏覽器的請求來設置對應的HTTP頭部,例如針對瀏覽器的限制我們可以設置User-Agent頭部,針對防盜鏈限制,我們可以設置Referer頭部,下面是示例代碼:
importurllib2
req=urllib2.Request('http://www.server110.com', headers={"Referer":"http://www.baidu.com", "User-Agent":"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.68 Safari/534.24" }) html=urllib2.urlopen(url=req,timeout=10).read() |
有的網站用了Cookie來限制,主要是涉及到登錄和限流,這時候沒有什么通用的方法,只能看能否做自動登錄或者分析Cookie的問題了。
URL與內容處理
URL里奇形怪狀的格式只能個別分析,個別處理,逮到一個算一個。例如針對URL里可能有漢字,相對路徑,以及回車換行之類的問題,我們可以先用urlparse模塊的urljoin函數處理相對路徑的問題,然后去掉亂七八糟的回車換行之類的玩意,最后用urllib2的quote函數處理特殊字符編碼和轉義的問題,生成真正的URL。
當然,在使用的過程中你會發現Python的urljoin和urlopen都有些問題,因此具體的代碼我們在后面再給。
對於那些不管三七二十一就把gzip內容丟過來的,我們直接分析它的內容格式,不要管HTTP頭部就好了。代碼是這樣的:
importurllib2 importgzip,cStringIO
html=urllib2.urlopen('http://www.server110.com').read() ifhtml[:6]=='\x1f\x8b\x08\x00\x00\x00': html=gzip.GzipFile(fileobj=cStringIO.StringIO(html)).read() |