【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

皮克啪的鏟屎官發表於2018-06-11

看完此文章,你將可以獨立完成:

  • 在伺服器上可以寫一個簡單的靜態網頁,並訪問
  • 可以為你的App寫介面,提供Json格式的資料
  • 伺服器定時執行某項任務


上一篇通過訪問公眾號獲取每日最新資訊的文章一出『這一次,他通過公眾號訪問最新的1024資訊資訊』,真的是沒想到,大家的熱情一下那麼高,導致我的伺服器壓力很大。讓我真的是很意外,還是要感謝大家的支援。上回答應過大家,要給大家說一下需要怎樣做,才能夠實現那樣的功能。所以,這回我主要講一下,如何用Tornado來搭建你自己的伺服器。WeRoBot微信自動回覆機器人下一期再說吧。

我敢打包票,85%的人看不完這篇文章。大家的自覺性就是這樣。

授人以魚不如授人以漁,這就是我為什麼要寫這些文章的緣由。今天既然能夠通過公眾號來訪問到小草,那麼明天我就可以通過公眾號上訪問每天新聞的最新訊息,或者我關注的人的微博更新,或者美劇是否又跟新了,或者視訊網站是否又出新視訊了,或者一些論壇是否發了最新的帖子,甚至每天早晨一起來,都可以通過自己的演算法再結合昨天的股票資料,來推測今天大盤的漲跌,這些東西背後的原理其實都差不多的。所以,不要把自己的思路侷限在一點,要擴散,要放開,這樣才會有騷操作的出現。

行了,廢話不閒扯了,來開始說說我們今天的主角:Tornado吧。

啥是Tornado

Tornado就是龍捲風,哦不,這裡說的Tornado是一種 Web 伺服器軟體的開源版本。Tornado 和現在的主流 Web 伺服器框架(包括大多數 Python 的框架)有著明顯的區別:它是非阻塞式伺服器,而且速度相當快。

Tornado需要用Python編寫,所以,這一系列下來,我們都是用的Python來搞事情,就和我之前說的,Python這個語言,最適合搞事情了!

這裡要說一點,Web框架有很多種,不同語言有不同的框架,而且特點都不一樣,就Ptyhon而言,用Python開發的Web框架也有好幾種,Django,Flask,Tornado,這些框架,也是各有各的特點。所以,我想說的是,至於要用那種框架,請結合你自身的需求來選擇。不要盲目的抓起一個隨便用,這樣將來會坑了自己。我們這裡,就是小型服務,自娛自樂用,所以,沒有那麼多顧慮,隨便抓起來一個用就OK,這不,我抓的就Tornado。

把Tornado搞到伺服器上

首先,伺服器或者你本地的機器上面,應該是有Python的,推薦Python3,然後,你的機器應該是有pip的。

我們需要通過pip來安裝Tornado

1# pip install tornado
複製程式碼

安裝完成,如果想測試是否安裝功,只需要進入Python,然後輸入import tornado,如果沒有報錯,就說明安裝成功。

接著,我們來搞一下本地的配置。

本地,我推薦使用PyCharm來做IDE。這個IDE功能還算可以,如果你已經有自己習慣的IDE,可以略過此處。

PyCharm官網上有兩個可下載的版本:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

推薦下載第一個,Professional,這個版本功能很強大,而且支援很多Web框架的外掛。

但是好多人會發現,這個是收費版本的啊,那怎麼用?

別急,破解方法非常簡單,第一次開啟PyCharm的時候,選擇License server啟用,然後填入:http://im.js.cn:8888http://idea.java.sx/http://xidea.online,然後點Activate啟用即可。

接著,我們需要將本地的程式碼和服務端的程式碼要同步起來,做好對映,設定的步驟也很簡單。這裡,我們假設遠端的伺服器地址是39.11.12.123

Tools -> Deployment -> Configuration裡面:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

點選左上角的 + 號:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

出來的對話方塊裡,名字隨便寫,但是下面的要選擇SFTP

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

接著,下面這張圖,第一個紅框裡面填寫遠端伺服器的ip地址:39.11.12.123,第二個填寫你伺服器上的登入賬戶名稱,一般是root,第三個就是密碼。

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

接著第二頁,Mapping裡面,第二個紅框裡面,填寫你本機的工程目錄地址,第三個紅框填寫在伺服器上的工程目錄地址(提前建好)。

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

然後點選OK。接著,在Tools -> Deployment -> Automatic Upload點選打鉤,這樣每次編寫完一個檔案,程式碼就可以自動同步到伺服器上了。

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

每次如果需要同步的話,可以在Tools -> Deployment選單裡面,選擇Upload to XXXX就行,或者在需要上傳的檔案圖示點選右鍵,在Deployment裡面選擇就可以。很方便。上傳成功的樣子大概就是這樣:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

好了,我們接下來就要嘗試著編寫我們的程式碼了。但是,對於第一次接觸新框架的你,我們還是先看一下Tornad的“Hello World”怎麼寫吧。

 1import tornado.ioloop
 2import tornado.web
 3
 4class MainHandler(tornado.web.RequestHandler):
 5    def get(self):     # 3
 6        self.write("Hello, world")     # 4
 7
 8def make_app():
 9    return tornado.web.Application([
10        (r"/", MainHandler),     # 2
11    ])
12
13if __name__ == "__main__":
14    app = make_app()     # 1
15    app.listen(8888)
16    tornado.ioloop.IOLoop.current().start()
複製程式碼

在這個裡面,最關鍵的,有這麼幾個地方:

  1. make_app()宣告一個tornado的application,裡面就規定了伺服器接收處理的url路徑。
  2. 伺服器接收了url,將會把請求交給MainHandler()來做處理。
  3. MainHandler中的get方法,是用來處理HTTP GET請求的。
  4. 返回結果,只返回了一個字串。

OK,上面簡單的分析,就是Tornado處理一個網路請求的邏輯。捋順這個邏輯之後,我們接下來就開始簡單的編寫一下我們自己的服務端程式碼吧。

擼碼時刻

明確一下我們的兩個目的:

  • 我們的網站能夠訪問資料庫並且顯示在網頁上
  • 我們的網站能夠做到給App提供資料介面功能,返回Json格式的資料。

好的,下面我們就起來擼程式碼,不對,是擼起來程式碼。

遵循我們上面所說的,定義url路徑,然後寫Handler。所以,我就先按照這個思路,把工程目錄按照這個樣子建立了一下:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

然後,我們在main.py這個檔案裡面編寫程式碼如下:

 1class Application(tornado.web.Application):
 2    def __init__(self):
 3        handlers = [
 4            (r"/web/", WebHandle),
 5            (r"/json/", JsonHandle),
 6        ]
 7        # 定義tornado伺服器的配置項,如static/templates目錄位置,debug級別等
 8        settings = dict(
 9            debug=True,
10            static_path=os.path.join(os.path.dirname(__file__), "static"),
11            template_path=os.path.join(os.path.dirname(__file__), "templates")
12        )
13        tornado.web.Application.__init__(self, handlers, **settings)
14
15
16if __name__ == "__main__":
17    print("Tornado server is ready for service\r")
18    Application().listen(8000, xheaders=True)
19    tornado.ioloop.IOLoop.current().start()
複製程式碼

這裡簡單做一下解釋:
我們定義兩個Handler,一個是返回網頁版本的Handler,另一個是返回Json版本的;我們的Application的寫法也和Hellow world例子中寫的不一樣,我們這樣寫,可以自定義很多設定,比如路徑,是否Debug模式之類的。
那麼我們接下來看看連個Handler怎麼寫的:

1# views.json
2class JsonHandle(tornado.web.RequestHandler):
3    def get(self, *args, **kwargs):
4        self.write("json view")
5
6# views.web
7class WebHandle(tornado.web.RequestHandler):
8    def get(self, *args, **kwargs):
9        self.write("web view")
複製程式碼

這裡是兩個及其簡單的實現,我們來看一下效果:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

下面這個是訪問錯誤的url出現的頁面,因為我們開了Debug模式,所以頁面長這個樣子:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

404頁面的問題我們之後會說到。

到這裡位置,我們有一個地方不知道大家發現沒有,十分的不靈活,就是上面url匹配的地方。這裡指定了:http://xxxxxx/json/只能用JsonHandler來處理,但是如果來了http://xxxxxx/json/XX,他就會報錯,頁面未找到。處理這樣的請求,讓我們的服務變得更加強大,更加健壯,我們決定,新加一個url_router,在一定程度上,用它來控制我們的url匹配。

 1"""
 2    url_router.py
 3"""
 4def include(module):
 5    res = import_module(module)
 6    urls = getattr(res, 'urls', res)
 7    return urls
 8
 9
10def url_wrapper(urls):
11    wrapper_list = []
12    for url in urls:
13        path, handles = url
14        if isinstance(handles, (tuple, list)):
15            for handle in handles:
16                pattern, handle_class = handle
17                wrap = ('{0}{1}'.format(path, pattern), handle_class)
18                wrapper_list.append(wrap)
19        else:
20            wrapper_list.append((path, handles))
21    return wrapper_list
複製程式碼

有了router,我們的main檔案和handler檔案都應該修改一下,在views.json和views.web目錄下,分別建立json_urls.pyweb_urls.py

 1"""
 2    main.py
 3"""
 4class Application(tornado.web.Application):
 5    def __init__(self):
 6        # >>>> 不一樣的地方開始
 7        handlers = url_wrapper([
 8            (r"/json/", include('views.json.json_urls')),
 9            (r"/web/", include('views.web.web_urls')),
10        ])
11        # 不一樣的地方結束 <<<<
12        # 定義tornado伺服器的配置項,如static/templates目錄位置,debug級別等
13
14"""
15    json_urls.py
16"""        
17urls=[
18    (r'', JsonHandle)
19]
20
21"""
22    web_urls.py
23"""
24urls = [
25    (r"", WebHandle)
26]
複製程式碼

這樣寫,雖然看上去比較亂一些,但是相當靈活。能夠使我們的url變得豐富。比如,如果我想新增一個檢視全部json檔案的url,那麼我只需要在json_urls裡面,新增一個(r'/all', GetAllHandler)即可,然後在json_view.py裡面實現GetAllHandler就可以了。這樣做完,我們的伺服器就可以同時處理http://xxxxxx/json/http://xxxxxx/json/all兩個url了,而且是不同的handler處理。

此時此刻,我們大概的框架基本搭建完成。下面就來主要實現一下handler裡面的功能吧。

因為我們要實現的是從資料庫裡面讀取了資料在顯示到網頁上,所以,這裡我們用到了PyMongo這個庫。這個庫是Python專門用來操作MongoDB的,炒雞簡單好用。

我們先來完成Json部分。

Json格式的返回實現

我們要在JsonHandler中,實現get()方法。這裡,我們首先從資料庫中讀取出來資料,然後,得將資料轉換成dict()格式,因為PyMongo讀取出來的資料,不能夠直接轉成Json,因為裡面有個叫Object_id的東西,所以,這裡我們就手動轉一下。然後,把資料用self.write(json.dumps({"data": {"block": return_data, "curTime": cur_time}}))的形式返回回去就好。結構相當簡單,大致程式碼如下:

 1class JsonHandle(tornado.web.RequestHandler):
 2    def get(self):
 3        # 從資料庫中讀取資料
 4        self.client = pymongo.MongoClient("mongodb://39.11.12.123/", 27017)
 5        self.db = self.client["DailyProject"]
 6        self.table = self.db["table"]
 7        result = self.table.find()
 8        # 得到當前時間
 9        time = datetime.datetime.now()
10        cur_time = str(time.year) + "-" + str(time.month) + "-" + str(time.day)
11        # 篩選出合適的資料
12        temp_posts = []
13        posts = []
14        for item in result:
15            temp_posts.append(item)
16            temp_posts.sort(key=lambda k: (k['post_time'][-5:]), reverse=True)
17        for item in temp_posts:
18            if item['post_day_time'] == cur_time:
19                posts.append(item)
20        # 將資料轉換成dict()型別,方便轉換成Json
21        return_data = []
22        for item in posts:
23            temp_dic = {'postId': item['post_id'], 'postTitle': item['post_title'],
24                        'postPartUrl': item['post_part_url']}
25            return_data.append(temp_dic)
26        # 返回Json格式的資料
27        self.write(json.dumps({"data": {"block": return_data, "curTime": cur_tim
複製程式碼

下面是效果:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

這樣,實現起來是不是超級酷。這裡是實現了get方法,你也可以實現post方法來處理HTTP POST請求。具體的邏輯還是要根據你具體的業務來編寫。反正最後用json.dumps返回就可以了。

小技巧:如果你返回的json格式都差不多,可以抽離出來,寫一個模板,以後返回結果直接將資料傳給模板就好。不需要在每個方法都寫一遍json的格式,那樣如果修改起來,會很費事兒。

Web格式的返回實現

Web返回結果,我們這裡就用到了html的東西。首先,我們得在template裡面建一個index.html檔案。然後,在WebHandler中,最後返回結果寫成:self.render("index.html", info=posts, today=cur_time)這樣就可以了。這裡簡單說一下,第一個引數,是你template裡面對應的html檔案。第二個引數和第三個引數,是你需要傳給前端的資料。名字隨便叫,但是要和html裡面保持一致。

Handler的程式碼大致如下:

 1class WebHandle(tornado.web.RequestHandler):
 2    def get(self):
 3        self.client = pymongo.MongoClient("mongodb://39.11.12.123/", 27017)
 4        self.db = self.client["DailyProject"]
 5        self.table = self.db["table"]
 6        result = self.table.find()
 7        time = datetime.datetime.now()
 8        cur_time = str(time.year) + "-" + str(time.month) + "-" + str(time.day)
 9        temp_posts = []
10        posts = []
11        for item in result:
12            temp_posts.append(item)
13            temp_posts.sort(key=lambda k: (k['post_time'][-5:]), reverse=True)
14        for item in temp_posts:
15            if item['post_day_time'] == cur_time:
16                posts.append(item)
17        self.render("index.html", info=posts, today=cur_time)
複製程式碼

由於名字要和前端一一對應,所以,前端的程式碼如下:

 1<body>
 2
 3<h1>技術討論  {{today}}</h1>
 4
 5{% for element in info %}
 6<div class="post_block">
 7
 8   <p class="post_id">{{element['post_id']}}</p>
 9      <a class="post_url" href="{{element['post_url']}}" data-url="{{element['post_url']}}" target="_blank">{{element['post_title']}}</a>
10   <p class="post_time">{{element['post_time']}}</p>
11
12</div>
13{% end %}
14
15</body>
複製程式碼

注意,後端傳過來的today對應的html裡面的{{today}},info則對應的for迴圈裡面的info。這種for迴圈,語法有點像DoT.js。別慌,這種前端的寫法就那麼幾種,並不是很難,看懂例子怎麼寫,就照貓畫虎的往自己的html裡面寫就可以。

看到了嗎,就是這麼簡單,最後我們執行起來,效果如下:

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋

404頁面的處理

處理404頁面,只需要在main.py檔案的url中,加入一個(r".*", BaseHandle),然後在BaseHandler裡面,返回一個你已經寫好的404.html就好了。炒雞簡單。

最後很關鍵的,怎麼跑起來程式!

最後,程式碼寫好了,我們需要把我們的程式跑起來。

首先,將你的工程部署到你的伺服器上,通過前文所講的部署方法,成功傳上去檔案。

然後,登入到你的伺服器,進入工程指定的資料夾。

由於我們的啟動程式是在main.py裡面寫的,所以,這裡只需要輸入指令:

1# sudo python main.py &
複製程式碼

就可以讓你的Tornado後臺執行了!千萬別忘了後面還有個&

如果要關閉你的執行程式,則需要輸入:

1# ps -ef | grep main.py
複製程式碼

來查詢你Tornado所在的程式,通過kill指令關閉就可。

1# kill -9 <程式號>
複製程式碼

後記不後記

看到沒有,這樣就可以了。一個例子雖然很簡單,但是這裡可以擴充套件的地方有很多。很多同學肯定苦惱於不知道該怎麼寫服務端的程式碼,那麼這篇文章所講的東西可以很好的帶你入門,並且入門還前進了一小步,因為並不是簡簡單單的只給你寫hello world。

前面還提到了可以用Tornado來定時執行任務,這個東西我就不再這裡說了,如果想更多交流的話,請關注『皮克啪的鏟屎官』,點選下方的『進群交流』,來一起在群裡討論。

這些所講的內容的程式碼,我也給大家共享出來,同樣是關注『皮克啪的鏟屎官』,回覆『tornado』,即可獲得下載地址。

最後給大家吐槽一句,爬蟲的文章,你們看一篇就夠了,因為爬蟲這個東西,真的不是啥真金白銀的技術活,這個東西,根本體現不出來你的技術,說白了就是個工具。沒啥技術含量。那些爬來爬去爬美女爬帥哥的文章程式碼,我估計你寫了執行一遍就完事兒了,根本不會再次執行它。因為它給你爬的資料沒用啊。根本不像我之前的爬蟲,我的爬蟲,我把思路給大家講好,而且,我的爬蟲是實實在在的在服務端執行的。爬蟲就是為提供資料,並不是什麼高深的技術,而且工作崗位,爬蟲都是現成的,根本輪不到你寫。

推薦閱讀

【Python實戰】手把手超詳細教程教你Scrapy爬達蓋爾社群,有彩蛋
【Python實戰】用Scrapy編寫“1024網站種子吞噬爬蟲”,送福利
【Python實戰】用程式碼來訪問1024網站,送福利
【Python實戰】用Scrapyd把Scrapy爬蟲一步一步部署到騰訊雲上

這麼硬貨的公眾號,你們不關注一下啊?

【Python實戰】跟我一步一步來,用Tornado來實現你的伺服器夢想,純乾貨教學,有彩蛋


相關文章