導語
2020 年高考報名人數達到 1071 萬人,再創歷史新高。在今年的高考落下帷幕之際,介紹一款基於雲開發 CloudBase的高考錄取分數線查詢小程式,希望能為考生的志願填報提供便利。
資料來源
小程式後臺共收錄近 30w 條資料,包含 2008-2019 年大部分重點高校的各個批次的文理分科錄取分數線以及 2008-2019 採用新課標一卷、新課標二卷、新課標三卷以及部分自主命題省份的、從提前批到高職專科批的大部分錄取分數線,內容較為翔實。
所有資料均採集自各大院校和各高考相關網站,由於資料量巨大,為提高速度,使用了 concurrent.futures (需要 Python3.5+) 模組裡的 ThreadPoolExecutor 來構建執行緒池來併發執行多工。
資料庫採用的是 PgSQL,所有資料均存在新建的 gaokao 資料庫中,其下有兩個表,university(院校的錄取分)和 province(省份的批次線)。
30w 的資料量,多個站點,併發爬取,資料衝突是不可避免的,在執行插入之前,首先過濾掉殘缺不全的資料,比如在插入 university 表時某條資料缺少 pc 欄位,那麼這條記錄就應該被捨棄。
最嚴重的是資料重複,我採用的解決辦法是:先查詢待插入的資料是否已經存在,university 表的主碼是(name, stu, stu_wl, pc, year),因為現實約束一個院校只能在一個年份在一個類別一個批次只能有一個錄取平均分,如果不存在,才執行最後的插入,並 commit 提交事務。
後臺搭建
在拿到 30w 條資料後,我打算後臺採用 Flask+PgSQL 的模式實現,甚至當時已經在某公司的雲伺服器部署好了,但就在小程式端在開發者工具聯調通過之後,小程式上線遇到到一個大麻煩,因為小程式要求線上執行不能通過 ip 地址訪問後臺,必須通過備案的域名訪問,域名購買一個倒不麻煩,只是域名備案比較耗時間,需要一週多時間,而當時距離高考也就不到 5 天,在手足無措之時,無意間看到小程式·雲開發,於是果斷嘗試!
雲開發為開發者提供完整的原生雲端支援和微信服務支援,弱化後端和運維概念,無需搭建伺服器,使用平臺提供的 API 進行核心業務開發,即可實現快速上線和迭代,同時這一能力,同開發者已經使用的雲服務相互相容,並不互斥。
也就是說,只要把資料匯入小程式自帶的後臺,就能通過小程式平臺的 API 訪問到這些資料,以前瞭解過一些第三方雲,沒想到小程式已經整合了這些第三方雲所具有的功能,這波要點個贊。
接下來的後臺的工作就主要是匯入資料。查詢小程式後臺可知,後臺支援匯入 json 或者 csv 格式的資料。於是我就寫了個指令碼,把資料從本地資料庫匯出到 json 檔案中:
import psycopg2
import json
# 連線 pgsql 資料庫,為保證隱私,密碼已隱藏
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)
# indent=2 控制 json 格式的縮排
# ensure_ascii 控制中文的正常顯示
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() 動態修改,把修改後的資料從資料層渲染到檢視層。
同時模仿下例程式碼完成業務邏輯:
// 查詢可能較慢,最好加入載入動畫
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);
// 承載所有讀操作的 promise 的陣列
//初次迴圈獲取雲端資料庫的分次數的promise陣列
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();
//二次迴圈根據獲取的promise陣列的資料長度獲取全部資料push到newResult陣列中
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();