看完此文章,你將可以獨立完成:
- 在伺服器上可以寫一個簡單的靜態網頁,並訪問
- 可以為你的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官網上有兩個可下載的版本:
推薦下載第一個,Professional,這個版本功能很強大,而且支援很多Web框架的外掛。
但是好多人會發現,這個是收費版本的啊,那怎麼用?
別急,破解方法非常簡單,第一次開啟PyCharm的時候,選擇License server啟用,然後填入:http://im.js.cn:8888
或 http://idea.java.sx/
或http://xidea.online
,然後點Activate啟用即可。
接著,我們需要將本地的程式碼和服務端的程式碼要同步起來,做好對映,設定的步驟也很簡單。這裡,我們假設遠端的伺服器地址是39.11.12.123
。
在Tools -> Deployment -> Configuration
裡面:
點選左上角的 +
號:
出來的對話方塊裡,名字隨便寫,但是下面的要選擇SFTP
。
接著,下面這張圖,第一個紅框裡面填寫遠端伺服器的ip地址:39.11.12.123,第二個填寫你伺服器上的登入賬戶名稱,一般是root,第三個就是密碼。
接著第二頁,Mapping裡面,第二個紅框裡面,填寫你本機的工程目錄地址,第三個紅框填寫在伺服器上的工程目錄地址(提前建好)。
然後點選OK。接著,在Tools -> Deployment -> Automatic Upload
點選打鉤,這樣每次編寫完一個檔案,程式碼就可以自動同步到伺服器上了。
每次如果需要同步的話,可以在Tools -> Deployment
選單裡面,選擇Upload to XXXX
就行,或者在需要上傳的檔案圖示點選右鍵,在Deployment
裡面選擇就可以。很方便。上傳成功的樣子大概就是這樣:
好了,我們接下來就要嘗試著編寫我們的程式碼了。但是,對於第一次接觸新框架的你,我們還是先看一下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()
複製程式碼
在這個裡面,最關鍵的,有這麼幾個地方:
- make_app()宣告一個tornado的application,裡面就規定了伺服器接收處理的url路徑。
- 伺服器接收了url,將會把請求交給MainHandler()來做處理。
- MainHandler中的get方法,是用來處理HTTP GET請求的。
- 返回結果,只返回了一個字串。
OK,上面簡單的分析,就是Tornado處理一個網路請求的邏輯。捋順這個邏輯之後,我們接下來就開始簡單的編寫一下我們自己的服務端程式碼吧。
擼碼時刻
明確一下我們的兩個目的:
- 我們的網站能夠訪問資料庫並且顯示在網頁上
- 我們的網站能夠做到給App提供資料介面功能,返回Json格式的資料。
好的,下面我們就起來擼程式碼,不對,是擼起來程式碼。
遵循我們上面所說的,定義url路徑,然後寫Handler。所以,我就先按照這個思路,把工程目錄按照這個樣子建立了一下:
然後,我們在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")
複製程式碼
這裡是兩個及其簡單的實現,我們來看一下效果:
下面這個是訪問錯誤的url出現的頁面,因為我們開了Debug模式,所以頁面長這個樣子:
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.py
和web_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
複製程式碼
下面是效果:
這樣,實現起來是不是超級酷。這裡是實現了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裡面寫就可以。
看到了嗎,就是這麼簡單,最後我們執行起來,效果如下:
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爬蟲一步一步部署到騰訊雲上
這麼硬貨的公眾號,你們不關注一下啊?