本文為《爬著學Python》系列第十二篇文章。
本來打算周更的,鴿了好幾個星期。原因一個是面向?qū)ο竽瞧獙嵲谟悬c難寫,另一個是我去弄了個Web項目-Qotes,GitHub地址。之前跟著書和實驗樓的在線課試過幾次Flask,這次是真正的實戰(zhàn)。
體驗還算不錯。
于是本專題說好了講爬蟲的,結果第一個大實踐內(nèi)容是web項目實戰(zhàn)。本文就算在介紹Flask框架之前先給個實例,以后根據(jù)此實例展開講細節(jié)吧。
Qotes開發(fā)初衷
再貼一下鏈接Qotes。
其實項目的開發(fā)初衷在網(wǎng)站中寫了。我本來在簡書寫這個專題是為了整理知識的,因為簡書的Markdown編輯器很好用。但是我后來發(fā)現(xiàn)一個問題,簡書采取了筆記類應用的文檔管理方式,就是一個筆記簿里面可以加一篇篇的筆記,但是筆記里面不能再加筆記,也不能在筆記簿里面再創(chuàng)建筆記簿。這給我?guī)砹撕艽蟮睦_

我解釋一下我困擾的地方在哪里。
- 可以看到我的文集里面小說分了正文和設定兩個部分,要是可以把這兩個文集放在一個文件夾“小說”中不就和上面的其他文集邏輯一致了嗎,但是這做不到,除非我把小說設定的一系列文章混在小說正文文集的頭或者尾部。
- 文集內(nèi)部也會存在問題。比如說“日記本”里面是我的一些雜亂的零碎的隨時筆記。但是單一的結構讓我很難將這些筆記進行整理。比如《mongodb tips》這里面其實可以分好幾篇文章,但是如果分開來,那么又和該文集中的其他文章邏輯不一致了。
總結起來就是:1文集不能包含2文章不能包含。
我后來想過別的方案。最靠譜的是這個:用Typora在Github上寫學習筆記。
但是總覺得有些大材小用了,而且Github打開的速度不太理想(大多數(shù)時候正常,有時要2s左右,很難受),更大問題在于Github的private repository需要收費,收費就算了還不方便。功能更符合這個應用場景的GitHub pages又被墻,體驗實在算不上好。于是還是決定用簡書。于是還是有些不舒服。
后來覺得還是自己寫一個吧。于是我就試著寫了。
Qotes的特點
卡片
首先要解決的是文章和文集的包含問題。最后我的解決方案是這樣的:
- 所有的筆記都是卡片形式存在
- 所有卡片在數(shù)據(jù)庫中地位是同樣的,保存在同一個collection中
- 卡片可以互相包含,每個卡片不一定有子卡片,但一定會有父結點
- 如果父結點是用戶,那么該卡片就可視為卡片集
- 當然,每個卡片都可以看成是某卡片集的子卡片集
總結起來就是,每個卡片既是卡片又是卡片集。
在數(shù)據(jù)庫中等價就好像是樹結點在內(nèi)存中散布但是通過鏈接來形成樹結構,唯一的不同是root根結點有的時候是用戶,而且樹結構一般鏈接記錄子結點信息,但是本實例中卡片通過父結點信息完成結構的鏈接。這樣做的原因是:
- 為了方便查詢,給相關字段設置索引以后,查詢速度倒比根據(jù)某個結點的子結點表中的id一個個查詢來得更快(理論上)
- 為了簡化IO操作。每個結點只能有一個父結點但是可以有許多個子結點。如果記錄子結點信息要對列表進行操作,而且卡片所屬關系的時候會涉及新舊父結點兩個卡片,當前邏輯下只需要把該卡片的父結點信息改掉就可以。
這樣做也帶來一些問題。比如說一個卡片不能屬于多個卡片,但是我所參考的傳統(tǒng)樹結構也是不支持一個子結點有多個父結點的。我后來開發(fā)過程中想了下,整體思路上其實和Linux的文件系統(tǒng)一切皆文件的設計有些類似,但是實現(xiàn)方式上有一些區(qū)別。
更多的實現(xiàn)細節(jié)在以后的文章中分析吧。
Markdown
另一個特點就是Markdown了。首先語類上基本上要比簡書全,但是不如GFM。當然原則是保持Markdown的精神,用純文本快速構建文檔結構。因此卡片的標題直接就是卡片內(nèi)容中的<h1></h1>亦即# title,二級標題h2和## subtitle作為卡片內(nèi)容概要。這些基本內(nèi)容作為卡片預覽時候的信息。

比如說這個卡片編輯的時候不用寫標題, 標題和概要會自動生成。

說起來簡書的文章需要標題,文章里面又可以用<h1></h1>其實困擾了我很久。如果不寫,文章內(nèi)容沒有一級標題直接用二級標題是不符合Markdown規(guī)范的,但是如果寫了,從網(wǎng)頁來看文章有兩個標題其實是不符合HTML規(guī)范的。在markdown文件中不存在這個問題在于文件名并不算在文件內(nèi)容中,所以markdown文件可以完全符合markdown規(guī)范填寫,給且僅給一個一級標題。
您也別笑我迂腐,HTML規(guī)范改到HTML5以后強調(diào)了HTML只負責內(nèi)容分級,把布局和格式交給CSS和JS來做。其實就是這種精神,而Markdown和HTML一樣是標記語言,老前輩都在學習進步,見賢思齊是應該的。
另外值得說的就是首頁的快速編輯模式。

右邊的這個是一個編輯器。是的,除了輸入內(nèi)容和提交按鈕什么都沒有,實時輸入,實時轉(zhuǎn)化成HTML預覽,和Typora類似。這個其實也是Markdown的精神,簡化輸入的同時完成格式。
技術選型
后端,為了快速開發(fā),選擇了Flask。為了方便迭代,數(shù)據(jù)庫選擇了MongoDB。當然,還有別的原因,比如我比較熟悉Flask,我想嘗試一下NoSQL。
前端沒什么好說的,Bootstrap簡直是我的救星。中間學了半天Javascript和jQuery寫了幾個簡單的交互。兩種Markdown編輯器都是從Github找的開源編輯器,說實話找這兩個編輯器的時間比我自己寫前面提到的幾個腳本的時間要長。
其實我有考慮在RESTful API寫完,把前端改成AJAX以后,UI優(yōu)化一下,可能會用Golang重新寫一遍這個項目。就目前來說Flask完全夠支持業(yè)務,雖然沒做壓力測試,但是我設置的最大同時連接數(shù)才1000,不出編碼上的太大的意外肯定是足以應付的。這不是我對自己的技術自信,是MongoDB實在太強大了。想重構代碼不過就是我想試一試Golang的體驗。
其實選MongoDB是我心血來潮一時之間的決定。Flask的MongoDB外部庫ODM沒一個好用的,真是心累,可能是我被SQLAlchemy和flask-sqlalchemy慣壞了吧。總之我后來直接用pymongo自己寫簡單映射了,代碼可能不太好看但是效果還可以,中間不斷改的時候發(fā)現(xiàn)可迭代性非常理想。在這里夸一下pymongo的文檔,簡直是模范,太羨慕了,是我見過的除了PythonHOWTOs以外體驗最好的文檔。我現(xiàn)在回來看,如果用MySQL并不會更適合我的數(shù)據(jù)結構設計不說,開發(fā)可能還是會更麻煩的。
目前的一些坑
private card還沒做。這是我目前最緊急的TODO。
由于備案的關系,我索性關閉了用戶之間交流信息的接口來規(guī)避風險,設計上是一般用戶只能創(chuàng)建自己的卡片,只能瀏覽自己的以及我的卡片。這樣基本算最大程度上利用個人博客能做的業(yè)務了。用戶可以把它當小工具,但是就備案性質(zhì)來說它還是個人博客,沒有越界。
關注功能也索性直接擱置了,評論功能是從一開始就沒打算做。別人的筆記,哪怕公開的,但就別BB了。當然,如果想體驗比較完整的功能也可以發(fā)郵件給我,郵箱去站點找吧,貼在這里怕被爬到時候一堆垃圾郵件。簡書的反爬實在太雞肋了,限制數(shù)量的做法未免有點自欺欺人。
大寫加粗的移動端體驗不佳。這個其實對我來說不算太大的問題。我從來沒用過簡書app編輯文章,原因可能是SE的屏幕太小了。而且作為幾年的WP用戶,我已經(jīng)習慣不用手機完成電話、短信和看新聞、天氣以外的功能了(我真的不是在黑windowphone)。電腦完成工作更方便,手機碼不了代碼,我的手機上除了簡書裝了不怎么用,OneNote也是不常用的。以前用WP還會用,iOS端OneNote簡直蠢,還不如打開電腦操作。
但是我仔細一思索,對于我不是問題但是對于其他人不一定適用。雖然Bootstrap對移動端的顯示已經(jīng)盡力了,但是我寫的主要的交互方式拖拽在手機瀏覽器上真的很難有用武之地,只能希望什么時候我能像個勞模還會樂意做個app吧。
于是這篇文章其實和Flask關系不大
不是這樣的。這篇文章只是先介紹一下實踐項目。中間還是有非常多的經(jīng)驗值得交流的。Flask和MongoDB的協(xié)同工作就很值得講,應用部署方面各種坑也是值得分享的。比如說阿里從今年8月開始封禁ECS出25端口就浪費了我很多時間來排查問題。比如centos用yum裝supervisor是有多么難受,比如flask-wtform WTForms和flask-bootstrap quick_form到底該如何取舍。至于光頭哥Flask Web Development那本書里面的各種時代坑就不談了。
鏈接
- Qotes
- 項目GitHub地址
- 其他等更新技術相關文章的時候根據(jù)不同內(nèi)容再寫吧。