作者 | 月小水長
責編 | 伍杏玲
剛高考完的學生們,心情並未放鬆下來,畢竟分數還沒下來。
今年的高考報名人數達到了 1031 萬的新高,作為一名三年前參考高考的準程序猿,加班加點從零開始做了一款高考查分小程序,算是一名老學長送給學弟學妹們的高考禮。關於小程序的介紹,可以參考我上一篇文章 歷年高考錄取分數線查詢,今天主要談談技術原理和實現細節。
數據來源
小程序後臺共收錄近 30萬條數據,包含 2008-2017 年所有重點高校的各個批次的文理分科錄取分數線以及 2008-2018 所有採用新課標一卷、新課標二卷、新課標三卷以及部分自主命題省份的從提前批到高職專科批的錄取分數線,勉強稱得上內容翔實。
所有數據均採集自各大院校和各高考相關網站,由於數據量巨大,為提高速度,使用了 concurrent.futures (需要 Python3.5+) 模塊裡的 ThreadPoolExecutor 來構建線程池來並發執行多任務。
資料庫採用的是 PgSQL,一款號稱世界上最強大的開源資料庫產品,所有數據均存在新建的 gaokao 資料庫中,其下有兩個表:university (院校的錄取分)和 province (省份的批次線)
university 表:
province 表:
30萬的數據量,多個站點,並發爬取,數據衝突是不可避免地,在執行插入之前,首先過濾掉殘缺不全的數據,比如在插入 university 表時某條數據缺少 pc 欄位,那麼這條記錄就應該被捨棄。
最嚴重的是數據重複,我採用的解決辦法是:先查詢待插入的數據是否已經存在, university 表的主碼是(name,stu,stu_wl,pc,year),因為現實約束一個院校只能在一個年份在一個類別一個批次只能有一個錄取平均分,如果不存在,才執行最後的插入,並 commit 提交事務。
後臺搭建
在 30w 條數據拿到後,我打算後臺採用 Flask+PgSQL 的模式實現,甚至在後臺在阿里雲伺服器部署好,小程序端在開發者工具聯調通過之後,小程序上線遇到到一個大麻煩,因為小程序要求線上運行不能通過 IP 地址訪問後臺,必須通過備案的域名訪問,域名購買一個倒不麻煩,只是域名備案比較耗時間,需要一周多時間,而當時距離高考也就不到 5 天,在手足無措之時,無意間看到小程序雲開發,關於小程序雲開發,官網的介紹是:
開發者可以使用雲開發開發微信小程序、小遊戲,無需搭建伺服器,即可使用雲端能力。
雲開發為開發者提供完整的原生雲端支持和微信服務支持,弱化後端和運維概念,無需搭建伺服器,使用平臺提供的 API 進行核心業務開發,即可實現快速上線和迭代,同時這一能力,同開發者已經使用的雲服務相互兼容,並不互斥。
也就是說,只要把數據導入小程序自帶的後臺,就能通過小程序平臺的 API 訪問到這些數據,以前了解過第三方的 LeanCloud 雲 和 Bomb 雲,沒想到小程序現在集成了這些功能,不得不佩服一下騰訊。
也就是,接下來的後臺的工作是主要是導入數據,查詢小程序後臺可知,後臺支持導入 JSON 或者 CSV 格式的數據。於是我就寫了個腳本,把數據從本地資料庫導出到 JSON 文件中:
import psycopg2
import json
conn = psycopg2.connect(database="gaokao", user="postgres", password="*******", host="127.0.0.1", port="5432")
cur = conn.cursor()
cur.execute('select stu_loc,year,stu_wl,pc,control from province')
result = []
query_res = cur.fetchall()
for i in query_res:
item = {}
item['stu_loc'] = i[0]
item['year'] = i[1]
item['wl'] = i[2]
item['pc'] = i[3]
item['score'] = i[4]
result.append(item)
with open("province.json", 'w', encoding="utf-8") as f:
f.write(json.dumps(result, indent=2, ensure_ascii=False))
這裡還有個坑需要說明一下,小程序後臺要求的 JSON 格式和我們平常意義上的 JSON 格式還有點區別,首先,JSON 的所有內容不能被 [ 和 ] 包括起來,而且每個被 {} 所包括得數據項之間不能有逗號。
選用 NotePad++ 打開原來的 JSON 文件,使用替換功能就能解決,把 [ 和 ] 替換成空格,把 },替換成 } 即可。
修改之後,在小程序後臺通過導入該 JSON 文件,後臺搭建就基本完成了。
小程序端編寫
關於小程序端的編寫,我主要談談兩點經驗,第一是頁面的編寫,比如下面這個界面。
最開始想實現這樣的效果,完全沒有思路,最後在從自定義模態彈窗那得到了思路,一開始地區院校這個下拉框對應的布局是隱藏的,在 WXML文件中通過 hidden=true 控制,一點擊 地區/院校 下拉框,就把 hidden 置為 false,如果開始有其他下拉框對應的布局的 hidden 屬性是 false 的話,同時要把這些布局的 hidden 屬性置為 true 來隱藏其他布局。
當然,這裡的 true or false 需要在 JS 文件裡通過 setData() 動態修改,把修改後的數據從數據層渲染到視圖層。
第二是關於小程序雲開發的原生 Bug,查詢後臺時一次只能最多只能查詢到 20 條數據,要實現一次得到所有匹配的結果,需要解決兩個問題:
第一個問題很自然而然就能想到,第一次查到 20 條數據後,第二次跳過前 20 條再取 20 條,第三次跳過前 40 條再取 20 條,以此類推。
還有一個更為致命的問題,查詢後臺的 API 獲取結果的回調函數的異步,也就是說,為了保證獲得完整數據,第二次查詢需要寫在第一次查詢的回調裡,第三次查詢需要寫在第二次查詢的回調裡,而且你還不能顯式地知道要查詢多少次,需要寫多少層這樣的嵌套,以及煩人的同名變量覆蓋問題,這就是所謂的異步地獄。為了解決這個問題,需要我們編寫代碼把這個異步方法轉成同步的,具體做法是:
先在所要添加功能的JS頁面中導入 runtime.js 文件,同時把runtime.js文件放入相應文件夾:
const regeneratorRuntime = require("../runtime");
runtime.js 下載地址:
https://github.com/inspurer/CampusPunchcard/blob/master/runtime.js
同時模仿下例代碼完成業務邏輯:
wx.showLoading({
title: '加載中',
})
const countResult = await db.collection('province').where({
stu_loc: name,
pc: pici,
}).count()
const total = countResult.total
const batchTimes = Math.ceil(total / MAX_LIMIT)
for (let i = 0; i < batchTimes; i++) {
const promise = await db.collection('province').where({
stu_loc: name,
pc: pici,
}).skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
for (let j = 0; j < promise.data.length; j++) {
var item = {};
item.code = i * MAX_LIMIT + j;
item.name = promise.data[j].stu_loc;
item.year = promise.data[j].year;
item.wl = promise.data[j].wl;
item.pc = promise.data[j].pc;
item.score = promise.data[j].score;
console.table(promise.data)
newResult.push(item)
}
}
if (newResult.length != 0) {
that.setData({
hasdataFlag: true,
resultData: newResult
})
} else {
that.setData({
hasdataFlag: false,
resultData: newResult
})
}
wx.hideLoading()
以上就是我本次開發的一些心得體會,歡迎批評指正。
作者簡介:月小水長(ID:inspurer),某 985 計算機學院在校生,熟悉 C++、Java、Python 等多種語言,有大型軟體項目開發經驗,致力於安卓、計算機視覺、爬蟲、數據可視化開發,同時也是業餘的前端愛好者。
6月29-30日,2019以太坊技術及應用大會特邀以太坊創始人V神與以太坊基金會核心成員,以及海內外知名專家齊聚北京,聚焦前沿技術,把握時代機遇,深耕行業應用,共話以太坊2.0新生態。掃描下方二維碼,即享優惠購票!
熱 文 推 薦
戳他了解更多↓↓↓
☞邊緣計算將吞掉雲計算!
☞程式設計師代碼排錯的正確姿勢,竟是從老中醫學的?
☞漫畫:Java 那麼多鎖,能鎖住滅霸嗎?
☞開源「大地震」下,華為如何複製 Google 模式?
☞回報率850%? 這個用Python優化的比特幣交易機器人簡直太燒腦了...
☞谷歌用1.2萬個模型「推翻」現有無監督研究成果!斬獲ICML 2019最佳論文
☞24式,加速你的Python
☞17 歲的程式設計師告訴你關於編程的 7 個重要教訓!
☞「是!網際網路從此沒有 BAT!」