作为一名长期使用Python处理日期时间数据的开发者,我经常需要获取农历、节气等传统历法信息。经过多次实践对比,我发现主要有两种可靠的Python万年历查询方案:通过API接口获取和调用第三方库。下面我将详细介绍这两种方法的实现细节和使用心得。
目前比较稳定的农历查询API是tiax.cn提供的服务,它的响应速度快且数据全面。我们先来看基础调用方法:
python复制import http.client
conn = http.client.HTTPSConnection("api.tiax.cn")
payload = ''
headers = {'User-Agent': 'Mozilla/5.0'}
conn.request("GET", "/almanac/?year=2023&month=11&day=23", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
这个基础调用会返回包含公历、农历、黄历等丰富信息的JSON数据。但在实际项目中,我们通常需要处理更复杂的需求,比如批量查询多日数据并进行格式化处理。
重要提示:在实际生产环境中,建议添加异常处理和重试机制,因为网络请求可能不稳定。我通常会封装一个带重试功能的请求函数。
对于需要处理连续多日数据的场景,我们可以结合datetime和pandas库来实现。下面是一个查询从今天开始连续5天农历信息的完整示例:
python复制import datetime
import http.client
import json
import pandas as pd
import ssl
from retry import retry
ssl._create_default_https_context = ssl._create_unverified_context
@retry(tries=3, delay=1)
def get_lunar_data(date):
conn = http.client.HTTPSConnection("api.tiax.cn")
headers = {'User-Agent': 'Mozilla/5.0'}
url = f"/almanac/?year={date.year}&month={date.month}&day={date.day}"
conn.request("GET", url, headers=headers)
res = conn.getresponse()
return json.loads(res.read().decode("utf-8"))
def get_consecutive_days(days=5):
columns = ["公历日期", "农历日期", "干支", "宜", "忌"]
df = pd.DataFrame(columns=columns)
start_date = datetime.date.today()
for i in range(days):
current_date = start_date + datetime.timedelta(days=i)
try:
data = get_lunar_data(current_date)
df.loc[i] = [
data["公历日期"],
data["农历日期"],
data["干支日期"],
data["宜"],
data["忌"]
]
except Exception as e:
print(f"获取{current_date}数据失败: {str(e)}")
continue
return df
# 使用示例
calendar_df = get_consecutive_days()
print(calendar_df)
这个进阶版本增加了以下实用功能:
cnlunar是一个功能强大的农历计算库,相比API方案,它的优势在于:
安装非常简单:
bash复制pip install cnlunar
基础使用方法:
python复制from datetime import datetime
import cnlunar
lunar = cnlunar.Lunar(datetime(2023, 11, 23), godType='8char')
print(f"农历日期: {lunar.lunarYearCn}年{lunar.lunarMonthCn}{lunar.lunarDayCn}")
print(f"今日宜: {lunar.goodThing}")
print(f"今日忌: {lunar.badThing}")
cnlunar提供了大量传统历法相关的信息,下面是一些特别有用的功能:
八字计算:
python复制bazi = f"{lunar.year8Char} {lunar.month8Char} {lunar.day8Char} {lunar.twohour8Char}"
print(f"八字: {bazi}")
节气信息:
python复制print(f"当前节气: {lunar.todaySolarTerms}")
print(f"下一个节气: {lunar.nextSolarTerm}({lunar.nextSolarTermDate})")
时辰吉凶:
python复制for hour, luck in zip(lunar.twohour8CharList, lunar.get_twohourLuckyList()):
print(f"{hour}时: {'吉' if luck else '凶'}")
经验分享:cnlunar的godType参数支持'8char'和'normal'两种模式,前者提供更详细的八字信息,后者计算速度更快。在不需要八字的情况下建议使用'normal'模式。
| 对比项 | API方案 | cnlunar库方案 |
|---|---|---|
| 网络依赖 | 是 | 否 |
| 响应速度 | 依赖网络(100-500ms) | 本地计算(<10ms) |
| 数据完整性 | 较好 | 优秀 |
| 功能丰富度 | 基础 | 非常丰富 |
| 维护成本 | 依赖第三方API | 自主可控 |
推荐使用API方案的情况:
推荐使用cnlunar库的情况:
在实际使用中,API请求可能会遇到各种问题。以下是几种常见情况及解决方案:
问题1:SSL证书验证失败
python复制# 解决方案1(不推荐长期使用):
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 解决方案2(推荐):
conn = http.client.HTTPSConnection("api.tiax.cn", context=ssl.create_default_context())
问题2:请求频率限制
时区问题:
cnlunar默认使用本地时区,如果需要处理其他时区的日期,需要先转换:
python复制from datetime import datetime
import pytz
tz = pytz.timezone('Asia/Shanghai')
dt = datetime(2023, 11, 23, tzinfo=pytz.utc).astimezone(tz)
lunar = cnlunar.Lunar(dt)
性能优化:
当需要批量处理大量日期时,可以复用Lunar实例:
python复制lunar = cnlunar.Lunar(datetime.now())
for date in date_list:
lunar.update(date)
# 处理逻辑...
我们可以将上述功能封装成Web服务:
python复制from flask import Flask, jsonify, request
import cnlunar
from datetime import datetime
app = Flask(__name__)
@app.route('/lunar', methods=['GET'])
def get_lunar_info():
date_str = request.args.get('date')
try:
date = datetime.strptime(date_str, '%Y-%m-%d')
lunar = cnlunar.Lunar(date)
return jsonify({
'lunar_date': f"{lunar.lunarYearCn}年{lunar.lunarMonthCn}{lunar.lunarDayCn}",
'solar_terms': lunar.todaySolarTerms,
'appropriate': lunar.goodThing,
'inappropriate': lunar.badThing
})
except Exception as e:
return jsonify({'error': str(e)}), 400
if __name__ == '__main__':
app.run()
将农历节日导出到日历应用:
python复制from ics import Calendar, Event
import cnlunar
from datetime import datetime, timedelta
def generate_lunar_events(year, filename):
cal = Calendar()
start = datetime(year, 1, 1)
end = datetime(year, 12, 31)
current = start
while current <= end:
lunar = cnlunar.Lunar(current)
if lunar.get_otherLunarHolidays():
event = Event()
event.name = lunar.get_otherLunarHolidays()[0]
event.begin = current
event.description = f"农历{lunar.lunarMonthCn}{lunar.lunarDayCn}"
cal.events.add(event)
current += timedelta(days=1)
with open(filename, 'w') as f:
f.write(str(cal))
generate_lunar_events(2023, 'lunar_holidays.ics')
在实际项目中,我发现农历数据的应用场景非常广泛,从简单的日期显示到复杂的传统节日提醒系统,合理选择技术方案可以事半功倍。对于需要长期维护的项目,我强烈建议使用cnlunar这样的本地库方案,它不仅功能强大,而且完全避免了网络依赖带来的各种不确定性。