爬蟲開發python工具包介紹 (1)
本文來自網易雲社群
作者:王濤
本文大綱:
簡易介紹今天要講解的兩個爬蟲開發的python庫
詳細介紹 requests庫及函式中的各個引數
詳細介紹 tornado 中的httpcilent的應用
總結
目標:瞭解python中常用的快速開發爬蟲的工具包。
基礎: python的基礎語法(2.7)
Here we go!
簡易爬蟲:我把一次性程式碼稱為簡易爬蟲,這些爬蟲是定製化的,不能通用。不像爬蟲框架,通過配置就可以實現一個新的抓取需求。對於入門的盆友來講,本篇文章基本可以滿足你的需求。如果有對框架感興趣的盆友,瞭解了本文的tornado,對於你瞭解pyspider這個框架也是有好處的。(Pyspdier使用了tornado框架)
一、簡介requests與tornado
隨著大資料、人工智慧、機器學習的發展,python語言的程式設計地位保持著持續提升。其它方面的功能暫且不表(因為我不知道),我們來談談python在爬蟲方面的表現。
1、requests 基礎
相信想快速上手爬蟲的人都會選擇python,並且選擇requests庫,請問獲取百度首頁原始碼要幾步? 答:三步 第一步:下載和安裝python 第二步:pip 安裝 requests庫 第三步:執行 python -c 'import requests; print requests.get("http://www.baidu.com").content'
python -c 'import requests; print requests.get("http://www.baidu.com").content'<!DOCTYPE html> <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div <div <div <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span ><input id=kw name=wd value maxlength=255 autocomplete=off autofocus></span><span ><input type=submit id=su value=百度一下 ></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews <a href=http://www.hao123.com name=tj_trhao123 <a href=http://map.baidu.com name=tj_trmap <a href=http://v.baidu.com name=tj_trvideo <a href=http://tieba.baidu.com name=tj_trtieba <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" >登入</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon style="display: block;">更多產品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>關於百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必讀</a> <a href=http://jianyi.baidu.com/ <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
2、requests高效抓取
高效抓取,那我們把序列改成並行。一談併發,大家就想到多執行緒,多程序。
但是大家知道,由於Ptyhon的直譯器在執行的時候用了一把大鎖GIL保證直譯器(或者是python vm)在解釋執行的時候只有一個執行緒得到排程,所以CPython事實上是偽多執行緒的,也就是本質上還是單執行緒。 注: GIL存在於CPython中,Jython沒有這個限制(http://www.jython.org/jythonbook/en/1.0/Concurrency.html)
為了程式簡單,就直接多執行緒運行了,畢竟Python自帶的大多數資料結構是執行緒安全的(list,dict,tuple等),你可以不用考慮競爭給程式碼帶來的複雜性。
協程這個概念在很多程式語言中都已經支援了。python中通過yield關鍵字來實現協程,今天再給大家介紹一款基於協程的非同步非阻塞框架 tornado. 使用它來實現網路請求,相比於多執行緒的requests更高效。
3、tornado簡介
在介紹tornado之前,我們簡單介紹一下協程的概念。
3.1 協程
在單執行緒的前提條件下: 面向過程的程式設計中,我們會把一些程式碼塊封裝成一個函式,這個函式的特點:一個入口,一個出口.當我們呼叫一個函式時,會等它結束了才能繼續執行後續的程式碼。 而協程在單執行緒的條件下,一個函式可以多次進入,多次返回,我們在呼叫協程函式的時候,可以在它的中斷點暫時返回去執行其它的協程函式。(這有點像多執行緒,某一執行緒阻塞,CPU會排程其它執行緒)。 下面給一段程式碼看一下執行效果,邏輯很簡單,我們把show_my_sleep向IOLoop中添加了四次,每次入參不同。 show_my_sleep中列印資訊,休眠,列印資訊。根據結果,我們可以看到show_my_sleep函式在yield 語句進入休眠,暫時交出了執行權,等休眠結束後,從yield語句後開始繼續執行。
import randomfrom tornado.ioloop import IOLoopfrom tornado import [email protected] show_my_sleep(idx): interval = random.uniform(5,20) print "[{}] is going to sleep {} seconds!".format(idx, interval) yield gen.sleep(interval) # 此處會作為中斷點,交出程式碼執行權 print "[{}] wake up!!".format(idx)def main(): io_loop = IOLoop.current() io_loop.spawn_callback(show_my_sleep, 1) # 下一次迴圈的時候排程這個函式 io_loop.spawn_callback(show_my_sleep, 2) io_loop.spawn_callback(show_my_sleep, 3) io_loop.spawn_callback(show_my_sleep, 4) io_loop.start()if __name__ == "__main__": main()
結果:
[1] is going to sleep 5.19272014406 seconds![2] is going to sleep 9.42334286914 seconds![3] is going to sleep 5.11032311172 seconds![4] is going to sleep 13.0816614451 seconds![3] wake up!![1] wake up!![2] wake up!![4] wake up!!
3.2 Tornado 簡介
Tornado 是基於Python實現的非同步網路框架,它採用非阻塞IO,可以支援成千上萬的併發訪問量, 所以非常適合於長輪詢和Websocket, 以及其它需要持久連線的應用場景。Tornado 主要包含四個部分:- web框架,包括了RequestHandler(它可以用來建立WEB應用和各種支援的類)- 客戶端、服務端側的HTTP實現(包括HttpServer 和AsyncHttpClient)- 包含了 IOLoop和IOStream 的非同步網路庫,它們作為HTTP元件的內建塊並且可以用來實現其它協議。- 協程庫(tornado.gen),它使非同步程式碼寫起來比鏈式回撥更直接。Tornado WEB框架和HTTP server 在一起可以作為WSGI的全棧替代。 在WSGI容器裡可以使用Tornado web框架,也可以用Http server 作為其它WSGI框架的容器,不過任意一種組合都是有缺陷的。 為了充分發揮tornado的優勢 ,你需要使用tornado 的web框架和http server.
我們在這裡主要借用tornado的 httpclient和協程庫,來實現單執行緒下併發網路請求。 Here, show you the code!
import tracebackfrom tornado.ioloop import IOLoopfrom tornado import genfrom tornado.curl_httpclient import CurlAsyncHTTPClientfrom tornado.httpclient import [email protected] fetch_url(url): """抓取url""" try: c = CurlAsyncHTTPClient() # 定義一個httpclient req = HTTPRequest(url=url) # 定義一個請求 response = yield c.fetch(req) # 發起請求 print response.body IOLoop.current().stop() # 停止ioloop執行緒 except: print traceback.format_exc()def main(): io_loop = IOLoop.current() io_loop.spawn_callback(fetch_url, "http://www.baidu.com") # 新增協程函式到Ioloop迴圈中 io_loop.start()if __name__ == "__main__": main()
4、tornado併發
這裡簡單講,就是通過向ioloop中添加回調,來實現多個回撥的並行呼叫。
def main(): io_loop = IOLoop.current() io_loop.spawn_callback(fetch_url, "http://www.baidu.com") # 下一次迴圈的時候排程這個函式 ''' io_loop.spawn_callback(fetch_url, url1) ... ... io_loop.spawn_callback(fetch_url, urln) ''' io_loop.start()if __name__ == "__main__": main()
簡單介紹過兩個應用包後,來詳細介紹一下關鍵函式及引數。
二、requests 關鍵函式及引數
我們利用requests開發爬蟲時,主要會用到 get,post 方法,另外,為了應對反爬蟲策略,會新增一些自定義的http頭資訊,我們從這個應用角度介紹一下requests的兩個關鍵函式get和post。 函式定義:
def get(url, params=None, **kwargs): """Sends a GET request. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. :return: :class:`Response <Response>` object :rtype: requests.Response """ kwargs.setdefault('allow_redirects', True) return request('get', url, params=params, **kwargs)
def post(url, data=None, json=None, **kwargs): """Sends a POST request. :param url: URL for the new :class:`Request` object. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) json data to send in the body of the :class:`Request`. :param \*\*kwargs: Optional arguments that ``request`` takes. :return: :class:`Response <Response>` object :rtype: requests.Response """ return request('post', url, data=data, json=json, **kwargs)
我們可以看到,requests的get,post方法都會 呼叫 request函式,request函式定義如下:
def request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None, verify=None, cert=None, json=None): """Constructs a :class:`Request <Request>`, prepares it and sends it. Returns :class:`Response <Response>` object. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) json to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. :param files: (optional) Dictionary of ``'filename': file-like-objects`` for multipart encoding upload. :param auth: (optional) Auth tuple or callable to enable Basic/Digest/Custom HTTP Auth. :param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) <timeouts>` tuple. :type timeout: float or tuple :param allow_redirects: (optional) Set to True by default. :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol or protocol and hostname to the URL of the proxy. :param stream: (optional) whether to immediately download the response content. Defaults to ``False``. :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :rtype: requests.Response """
網易雲免費體驗館,0成本體驗20+款雲產品!
更多網易研發、產品、運營經驗分享請訪問網易雲社群。