將百度萬年曆存入自己的資料庫
阿新 • • 發佈:2021-09-16
前言
最近有需要研究陰曆和陽曆互相轉換的問題。因此找到兩個庫carbon和solarlunar
但是感覺計算出來的總是不太放心,而且也會佔用計算資源。我的想法是通過介面獲取現成的陰曆和陽曆資料,存到本地資料庫,這樣查詢的時候一步到位。
方案
我通過百度搜索
萬年曆
,抓取網頁請求得到百度的一個介面正好可以獲取萬年曆的資訊,還是挺全面的。
因此我寫程式碼實現了將百度萬年曆的資料獲取,然後存入資料庫的程式碼。
下面是calendar.go
的程式碼,主要使用gorm
建立表,以及寫入拉取的資料。
package calendar import ( "encoding/json" "errors" "fmt" "net/http" "net/url" "strconv" "strings" "sync" "time" "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" "gorm.io/driver/mysql" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) type ( PerpetualCalendar struct { //Status string `json:"status"` //T string `json:"t"` //SetCacheTime string `json:"set_cache_time"` Data []PerpetualCalendarData `json:"data"` } PerpetualCalendarData struct { //ExtendedLocation string `json:"ExtendedLocation"` //OriginQuery string `json:"OriginQuery"` //SiteID int `json:"SiteId"` //StdStg int `json:"StdStg"` //StdStl int `json:"StdStl"` //SelectTime int `json:"_select_time"` //UpdateTime string `json:"_update_time"` //Version int `json:"_version"` //Appinfo string `json:"appinfo"` //CambrianAppid string `json:"cambrian_appid"` //DispType int `json:"disp_type"` //Fetchkey string `json:"fetchkey"` //Key string `json:"key"` //Loc string `json:"loc"` //Resourceid string `json:"resourceid"` //RoleID int `json:"role_id"` //Showlamp string `json:"showlamp"` //Tplt string `json:"tplt"` //URL string `json:"url"` Almanac []PerpetualCalendarAlmanac `json:"almanac"` } PerpetualCalendarAlmanac struct { Id int `gorm:"primarykey"` // 自增主鍵 Animal string `json:"animal" gorm:"column:animal;not null;size:4"` // 生肖 Suit string `json:"suit" gorm:"column:suit;not null"` // 宜 Avoid string `json:"avoid" gorm:"column:avoid;not null"` // 忌 CnDay string `json:"cnDay" gorm:"column:cnDay;not null;size:4"` // 星期 Day int `json:"day,string" gorm:"column:day;not null;uniqueIndex:Solar"` // 陽曆日 Month int `json:"month,string" gorm:"column:month;not null;uniqueIndex:Solar"` // 陽曆月 Year int `json:"year,string" gorm:"column:year;not null;uniqueIndex:Solar"` // 陽曆年 GzDate string `json:"gzDate" gorm:"column:gzDate;not null;size:8"` // 干支日 GzMonth string `json:"gzMonth" gorm:"column:gzMonth;not null;size:8"` // 干支月 GzYear string `json:"gzYear" gorm:"column:gzYear;not null;size:8"` // 干支年 IsBigMonth string `json:"isBigMonth" gorm:"-"` // json取資料,忽略gorm IsBigMonthBool bool `gorm:"column:isBigMonth;not null;default:0"` // 是否為陰曆大月 LDate string `json:"lDate" gorm:"column:lDate;not null;size:4"` // 陰曆日,漢字 LMonth string `json:"lMonth" gorm:"column:lMonth;not null;size:4"` // 陰曆月,漢字,帶'閏'字表示閏月 LunarDate int `json:"lunarDate,string" gorm:"column:lunarDate;not null;index:Lunar"` // 陰曆日,數字 LunarMonth int `json:"lunarMonth,string" gorm:"column:lunarMonth;not null;index:Lunar"` // 陰曆月,數字 LunarYear int `json:"lunarYear,string" gorm:"column:lunarYear;not null;index:Lunar"` // 陰曆年,數字 ODate time.Time `json:"oDate" gorm:"column:oDate;not null"` // ODate.Local(),陽曆當天0點 Term string `json:"term,omitempty" gorm:"column:term;not null"` // 如'除夕','萬聖節','三伏'等 Desc string `json:"desc,omitempty" gorm:"column:desc;not null"` // 如'臘八節','下元節'等 Type string `json:"type,omitempty" gorm:"column:type;not null;size:2"` // 各種和節日有關的型別 Value string `json:"value,omitempty" gorm:"column:value;not null"` // 如'國際殘疾人日'等 Status int `json:"status,string,omitempty" gorm:"column:status;not null;default:0"` // 0 工作日,1 休假,2 上班,3 週末 } ) func (PerpetualCalendarAlmanac) TableName() string { return "perpetualCalendarAlmanac" } // GetPerpetualCalendar 返回[前一個月,本月,後一個月]的資料 func GetPerpetualCalendar(year, mouth int) ([]PerpetualCalendarAlmanac, error) { u := url.Values{} u.Add("tn", "wisetpl") u.Add("format", "json") u.Add("resource_id", "39043") // 這個用瀏覽器請求後得到的 u.Add("query", fmt.Sprintf("%d年%d月", year, mouth)) u.Add("t", strconv.FormatInt(time.Now().UnixMilli(), 10)) urls := "https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?" + u.Encode() resp, err := http.Get(urls) // 百度這個介面可能現在請求速度,所以可能報錯 if err != nil { return nil, err } defer resp.Body.Close() var ret PerpetualCalendar // 需要將gbk轉為utf8 err = json.NewDecoder(transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())).Decode(&ret) if err != nil { return nil, err } if len(ret.Data) != 1 { // 該陣列目前只會有一個 return nil, errors.New("get Data error") } for i, v := range ret.Data[0].Almanac { // 賦值大月 ret.Data[0].Almanac[i].IsBigMonthBool = v.IsBigMonth == "1" if v.Status == 0 && (v.CnDay == "六" || v.CnDay == "日") { ret.Data[0].Almanac[i].Status = 3 // 不是特殊型別,且為週末則賦值 } } return ret.Data[0].Almanac, nil } func SaveCalendar(dsnSrc string) error { ts := time.Now() defer func() { fmt.Println(time.Since(ts)) }() iDsn := strings.Index(dsnSrc, ":") if iDsn < 0 { return errors.New("dsn error") } var gormOpen gorm.Dialector switch strings.ToLower(dsnSrc[:iDsn]) { case "mysql": gormOpen = mysql.Open(dsnSrc[iDsn+1:]) case "sqlite": gormOpen = sqlite.Open(dsnSrc[iDsn+1:]) default: return errors.New("just support mysql or sqlite") } db, err := gorm.Open(gormOpen, &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { return err } res := db.Exec("DROP table if exists " + PerpetualCalendarAlmanac{}.TableName()) if res.Error != nil { return res.Error } // 每次重建資料表 err = db.AutoMigrate(PerpetualCalendarAlmanac{}) if err != nil { return err } // 起止時間按照百度萬年曆得到 start := time.Date(1900, time.February, 1, 0, 0, 0, 0, time.Local) end := time.Date(2050, time.December, 1, 0, 0, 0, 0, time.Local) wg := sync.WaitGroup{} // 由於每次查詢包含前一個月,當月,下個月,因此每次都增加3個月進行查詢 for ; start.Before(end); start = start.AddDate(0, 3, 0) { wg.Add(1) y, m, _ := start.Date() go func(y, m int) { defer wg.Done() for { // 使用協程併發請求,提高速度,出現錯誤時重試 data, err := GetPerpetualCalendar(y, m) if err != nil { fmt.Println("GetPerpetualCalendar", y, m, err) continue // 報錯重試,直到成功 } res := db.Create(&data) if res.Error != nil { fmt.Println("Create", y, m, res.Error) continue // 報錯重試,直到成功 } break } }(y, int(m)) } wg.Wait() return nil }
下面是main.go
,根據傳入的引數,選擇是儲存在mysql
還是sqlite
中。
package main import ( calendar "interesting/perpetual_calendar" ) func main() { dsn := "mysql:user:pass@tcp(127.0.0.1:3306)/janbar?charset=utf8mb4&parseTime=True&loc=Local" //dsn := "sqlite:test.db" err := calendar.SaveCalendar(dsn) if err != nil { panic(err) } }
結果展示
如下圖所示,是存入的資料,已經為陽曆年月日建立唯一聯合索引,陰曆年月日因為存在閏月會重複,因此只建立了聯合索引。
查詢語句可以按照下面的來做。大部分屬性已經按照我的理解寫到註釋裡面了,可以結合百度的萬年曆看看,展示在哪個位置吧。
查詢陽曆 2021-09-16 號的資料,結果有對應的陰曆情況 SELECT *FROM perpetualCalendarAlmanac WHERE year=2021 AND month=9 AND day=16 查詢陰曆 2021-09-03 號的資料,結果有對應陽曆的情況 SELECT *FROM perpetualCalendarAlmanac WHERE lunarYear=2021 AND lunarMonth=9 AND lunarDate=3
總結
作者:janbar 出處:https://www.cnblogs.com/janbar 本文版權歸作者和部落格園所有,歡迎轉載,轉載請標明出處。喜歡我的文章請 [關注我] 吧。 如果您覺得本篇博文對您有所收穫,可點選 [推薦] 並 [收藏] ,或到右側 [打賞] 裡請我喝杯咖啡,非常感謝。這個介面呼叫,和存資料沒啥難度。主要是讓我加深對gorm等庫的使用。
當然最主要是我想實現按照陰曆定時的cron規則,結合cron庫來搞。