這個參數不常用,不過很巧妙的一個參數.
當下載大的文件的時候,建議使用strea模式.
默認情況下是stream=Ffalse,他會立即開始下載文件並存放到內存當中,倘若文件過大就會導致內存不足的情況.
當把get函數的stream參數設置成True時,它不會立即開始下載,當你使用iter_content或iter_lines遍歷內容或訪問內容屬性時才開始下載。需要注意一點:文件沒有下載之前,它也需要保持連接。這里就用到了另一個巧妙的庫了: contextlib.closing
- iter_content:一塊一塊的遍歷要下載的內容
- iter_lines:一行一行的遍歷要下載的內容
使用上面兩個函數下載大文件可以防止占用過多的內存,因為每次只下載小部分數據。
示例代碼:
r = requests.get(url_file, stream=True) f = open("file_path", "wb") for chunk in r.iter_content(chunk_size=512): # 按照塊的大小讀取 # for chunk in r.iter_lines(): # 按照一行一行的讀取 if chunk: f.write(chunk)
更詳細的介紹:
抓取一批URL,並獲取對應URL的<title>標簽中的文字,忽略對應URL網站的封禁問題,這個任務並不是一個特別麻煩的事情。然后實際跑起來,卻發現流量打的很高,超過10Mb/s。
經過排查發現,是因為很多URL,實際是下載鏈接,會觸發文件下載,這些URL對應的html中根本不會包含<title>標簽,那么處理邏輯就很清晰了,先拿到headers,取出Content-Type,判斷是否是
text/html,如果不是,則該Response的body體,就沒有必要讀取了。
這樣的話,只有headers頭被下載了,body中的數據還沒有被下載,這樣就能避免不必要的流量開銷,只有當你使用r.content 的時候,所有body內容才會被下載
實時上還可以使用Response.iter_content() Response.iter_lines()
Response.raw()來自己決定要讀取多少數據
最后要注意的是,使用stream=True以后需要自己執行Response的
關閉操作
好,那么看下我改進后的程序
import logging import threading import redis import requests from lxml.html import fromstring r = redis.Redis(host='127.0.0.1', port=6379, db=10) logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', filename='all.log', filemode='w') def extract(url): logger = logging.getLogger() try: res = requests.get(url, stream=True, timeout=0.5) ctype = res.headers['Content-Type'] ctype = ctype.lower() if ctype.find('text/html') == -1: res.close() return None doc = fromstring(res.content) res.close() item_list = doc.xpath('//head/title') if item_list: res = item_list[0].text_content() res = unicode(res) logger.info('title = %s', res) return res except: return None return None
經過測試,帶寬開銷確實大幅下降了。為requests點個贊,設計者想的太周到了,既允許從hign level去使用它,也可以回到low level去精細化的管理控制連接。
默認情況下requests對URL的訪問是阻塞式的,可以通過使用
1)grequests
2)requests-futures
來實現非阻塞式的訪問
參考連接:
nul1 | https://www.cnblogs.com/nul1/p/9172068.html
萌叔 | http://vearne.cc/archives/120
