• 将百度万年历存入自己的数据库


    Github地址

    前言

    最近有需要研究阴历和阳历互相转换的问题。因此找到两个库carbonsolarlunar
    但是感觉计算出来的总是不太放心,而且也会占用计算资源。我的想法是通过接口获取现成的阴历和阳历数据,存到本地数据库,这样查询的时候一步到位。

    方案

    我通过百度搜索万年历,抓取网页请求得到百度的一个接口正好可以获取万年历的信息,还是挺全面的。
    因此我写代码实现了将百度万年历的数据获取,然后存入数据库的代码。

    下面是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
    

    总结

    这个接口调用,和存数据没啥难度。主要是让我加深对gorm等库的使用。
    当然最主要是我想实现按照阴历定时的cron规则,结合cron库来搞。

    作者:janbar
    本文版权归作者和博客园所有,欢迎转载,转载请标明出处。喜欢我的文章请 [关注我] 吧。
    如果您觉得本篇博文对您有所收获,可点击 [推荐] [收藏] ,或到右侧 [打赏] 里请我喝杯咖啡,非常感谢。
  • 相关阅读:
    逆向
    BUUCTF
    学校健康系统自动打卡
    SQL数据库操作练习(3)
    简单尝试UPX脱壳
    网站WAF-安全狗的绕过(一)
    【题解】担心
    【题解】树上的鼠
    【题解】CF1299B Aerodynamic
    【题解】等你哈苏德
  • 原文地址:https://www.cnblogs.com/janbar/p/15293288.html
Copyright © 2020-2023  润新知