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

前言

最近有需要研究阴历和阳历互相转换的问题。因此找到两个库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)
    }
}

结果展示

如下图所示,是存入的数据,已经为阳历年月日创建唯一联合索引,阴历年月日因为存在闰月会重复,因此只创建了联合索引。

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

导出MySQL数据 下载

查询语句可以按照下面的来做。大部分属性已经按照我的理解写到注释里面了,可以结合百度的万年历看看,展示在哪个位置吧。

查询阳历 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库来搞。

Original: https://www.cnblogs.com/janbar/p/15293288.html
Author: janbar
Title: 将百度万年历存入自己的数据库

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516596/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Go语言之函数

    函数就是一块执行特定任务的代码,在输入源的基础上通过一些算法生成预期的输出。 Go 语言中的函数声明语法如下: func 函数名(参数名 类型,参数名 类型)(返回值1类型,返回值…

    Go语言 2023年5月25日
    042
  • go 错误处理设计思考

    前段时间准备对线上一个golang系统服务进行内部开源,对代码里面的错误处理进行了一波优化。 优化的几个原因: 错误处理信息随意,未分类未定义。看到错误日志不能第一时间定位 错误的…

    Go语言 2023年5月25日
    058
  • go 连接MSSQLServer数据库【遇到的坑】

    前言:项目测试需要用到mssqlserver数据库连接,遇到坑,自己爬 直接上代码: go;gutter:true; package main</p> <p&gt…

    Go语言 2023年5月25日
    055
  • Kubernetes容器编排探索与实践v1.22.1-上半部分

    概述 本人博客网站 IT小神; www.itxiaoshen.com Kubernetes官网地址 https://kubernetes.ioKubernetes GitHub源码…

    Go语言 2023年5月25日
    098
  • Goland的那些实用技巧

    1、 自定义结构体tag 2、go mod tidy / download失败 解决办法:设置goproxy 3、取消/打开代码折叠 4、左侧project栏总是展示当前打开文件的…

    Go语言 2023年5月25日
    076
  • 【golang】pprof性能调优工具的具体使用(带案例)

    前言 大晚上的,老是刷到有关pprof的文章,忍不住看了几篇文章…写个学习笔记记录下~ 正文: 1.pprof是什么? pprof是go内置的性能调优工具,可以借助一些…

    Go语言 2023年5月25日
    080
  • 常见的限流算法

    通过限制并发访问数或者限制一个时间窗口内允许处理的请求数量来保护系统,例如,通过限流,你可以过滤掉产生流量峰值的客户和服务。 令牌桶算法 令牌桶算法是常见的一种限流算法。假设有一个…

    Go语言 2023年5月25日
    061
  • Go语言之高级篇beego框架之日志收集系统

    一、日志收集系统架构设计 图1 图2 二、开发环境 1、安装jdk jdk-8u51-windows-x64.exe 安装目录:C:\Program Files\jdk8 2、安装…

    Go语言 2023年5月29日
    055
  • 基于LSM的Key-Value数据库实现WAL篇

    上篇文章简单的实现了基于LSM数据库的初步版本,在该版本中如数据写入到内存表后但还未持久化到SSTable排序字符串表,此时正好程序崩溃,内存表中暂未持久化的数据将会丢失。 引入W…

    Go语言 2023年5月25日
    062
  • golang 实现一个简单的命令行进度条

    由于有时候跑脚本几个小时看不到进度,所以想着写一个简单的命令行的进度条。类似下面这样的 其中的原理主要是\r回车符(将光标移动到行首)。这样的话就可以重新打印一行以覆盖之前的那一行…

    Go语言 2023年5月25日
    055
  • go语言异常处理

    go语言异常处理 go语言引入了一个关于错误错里的标准模式,即error接口,该接口的定义如下: type error interface{ Error() string } 对于…

    Go语言 2023年5月29日
    066
  • Go语言实现线程安全访问队列

    这个例子用Go语言的包”container/list”实现一个线程安全访问的队列。其中不少细节耐人寻味,做出它则是花费了不少精力,找不到样例啊! Go语言的…

    Go语言 2023年5月29日
    051
  • 解决go-micro与其它gRPC框架之间的通信问题

    在之前的文章中分别介绍了使用gRPC官方插件和go-micro插件开发gRPC应用程序的方式,都能正常走通。不过当两者混合使用的时候,互相访问就成了问题。比如使用go-micro插…

    Go语言 2023年5月25日
    081
  • Go语言之高级篇beego框架之controller调用model

    一、controller调用model 开发规范,就该把对数据库的操作写在model文件夹中。 示例: views/main.go routers/router.go models…

    Go语言 2023年5月29日
    066
  • golang实现一个简单的websocket聊天室

    基本原理:1.引入了 golang.org/x/net/websocket 包。2.监听端口。3.客户端连接时,发送结构体: {“type”:”…

    Go语言 2023年5月25日
    079
  • Golang – 关于 proto 文件的一点小思考

    ProtoBuf 是什么? ProtoBuf 是一套接口描述语言(IDL),通俗的讲是一种数据表达方式,也可以称为数据交换格式。 我们常用的数据格式有 JSON 和 XML,为什么…

    Go语言 2023年5月25日
    063
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球