资讯详情

资讯详情

用Python打造每日星座运势查询工具:从API到打包实战

简介一份基于ASP构建的每日星座运势查询系统源码包面向Web开发初学者或对Access数据库应用感兴趣的站长主要解决星座数据每日自动更新与按星座查询展示的需求。系统通过定时机制将抓取到的运势数据写入Access数据库无需人工干预即可保持信息新鲜度。包内共29个文件以23个gif图表图片、2个ASP页面文件、1个Access数据库文件为主体另含说明文档和下载说明文本整体仅60KB结构精简适合快速阅读。页面文件构成前端查询入口gif图片用于绘制运势图表说明文档提供使用指引。目前已有197人学习下载。对于想了解服务器脚本编程、轻量级数据库设计或定时任务实现的开发者而言此压缩包提供了可直接运行的完整案例稍作修改即可扩展为其他资讯类查询系统也可作为学习动态网站开发的上手练习。1. 每日星座运势查询到底是个什么样的项目如果你打算自己动手写一个「每日星座运势查询」工具先别急着打开代码编辑器——这个项目最花时间的往往不是你熟悉的界面开发而是那个看起来最不起眼的问题数据从哪来。很多人以为星座运势查询就是个简单的展示页面把十二星座的文案往上一摆就完事了但真正做起来才发现运势数据要每天更新、要按时段区分、要按星座切分还要处理数据源不稳定、接口限流、编码错乱这些让人头疼的细节。这个 v1.0 版本要做到的事情很具体每天早上自动拉取最新的十二星座运势数据整理成清爽的格式让用户一次看到今天的整体运势、幸运数字、幸运颜色和注意事项。适合谁来做呢一类是想练手爬虫和 API 调用的开发者另一类是纯粹想给自己做个便民小工具的普通用户。不管哪类人做完这个项目都能带走一套完整的数据获取、解析、缓存和打包发布流程——这些技能换个场景照样用得上。真正动手之后你会发现这个项目是个绝佳的小切口练习它不复杂到劝退但足够让你踩一遍数据类小工具的所有经典坑。2. 数据源选型为什么优先选 API 而不是页面抓取2.1 API 与页面抓取的实战对比做每日星座运势查询第一步不是写代码而是决定数据从哪里拿。这个决定直接决定了你后面要花多少精力去维护。市面上常见的数据来源无非两种开方式接口和页面抓取。我一般会优先考虑 API原因很简单——稳定性。页面抓取听起来自由想抓哪个站点抓哪个但实际上你要面对的是对方前端的每次改版。今天还在的 CSS 选择器明天可能就换了类名今天还能用的 XPath 路径后天页面结构一调整你就抓瞎。更别提对方服务器偶尔抽风、返回个 502你的程序就得跟着罢工。而 API 接口一旦调通只要对方不改协议、不变签名基本就能稳定跑很久。我整理了一张对比表方便你按自己的场景判断维度API 接入页面抓取开发成本低按文档请求即可高需要分析页面结构稳定性高接口协议相对稳定低前端改版即翻车字段完整度取决于接口定义理论上可抓全部展示内容维护成本低偶尔调整参数高需要定期检查页面法律风险按接口授权使用需自行评估选型建议很直接如果只是想快速做出一个能用的工具优先找 API如果某个站点的数据很独特、没有接口可替代再考虑页面抓取但要额外做好容错和监控。2.2 数据字段清单拿数据之前先定义数据结构不管你用 API 还是页面抓取最终拿到的数据都要落到一个统一的结构里。你要先想明白一条今日运势记录应该包含哪些字段我建议至少要有这几项constellation星座名称比如白羊座、金牛座这是最基础的身份标识date运势对应的日期注意要用YYYY-MM-DD这种标准格式方便排序和去重overall整体运势评分用于第一眼的直观判断lucky_color幸运颜色这是用户比较在意的展示项lucky_number幸运数字通常是个整数lucky_direction幸运方位比如东南、西北summary今日运势的具体文案一般是一到两段描述有了统一的字段结构你的展示层、存储层、缓存层才能各自独立。后续哪怕换了数据源也只需要写一个适配器做字段映射主逻辑完全不用动。2.3 请求策略别一股脑全请求先做好限速与超时很多新手拿到接口文档就兴奋地写一个for循环把十二个星座的数据一次性全部拉下来。这个做法在开发环境没问题但上线之后要面对的是对方服务器的限流策略。一般接口都会限制单 IP 的请求频率比如每秒最多 2 次请求。你一口气发 12 个请求大概率会被限流甚至暂时封禁 IP。我一般会这样做在请求循环里加入time.sleep()控制节奏同时给每个请求设置超时时间。超时的作用是把对方挂了和网络慢区分开避免程序卡死在一个请求上。更重要的是要做好异常捕获——某个星座请求失败了不能让整个程序崩溃要跳过或重试其他星座的数据照常返回。3. 把当天的运势拉下来请求封装、解析与入库全流程3.1 项目结构与请求封装选定数据源之后整个项目的结构就清晰了。我一般会分成五个模块请求层负责发 HTTP 请求解析层负责把返回的数据变成结构化对象存储层负责把数据存到本地文件展示层负责格式化输出主流程把它们串起来。先写请求层。这里我封装了一个简单的函数带超时控制、重试机制和 UA 伪装import requests import time def fetch_daily_forecast(constellation, date, retries3): 获取指定星座在指定日期的运势数据 url https://api.example.com/constellation/daily params { constellation: constellation, date: date, key: your_api_key_here } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } for attempt in range(retries): try: resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() return resp.json() except requests.exceptions.Timeout: print(f[{constellation}] 请求超时第 {attempt 1} 次重试) time.sleep(2) except requests.exceptions.RequestException as e: print(f[{constellation}] 请求失败{e}) time.sleep(5) return None这个函数的参数设计有三个关键点retries默认设为 3做有限重试而不是无限重试避免对方已经挂了你还在疯狂请求timeout10是必须的没有超时的请求可能让你的程序挂在某个响应上headers里带上 UA 是基本礼貌很多接口会过滤掉没有 UA 的请求。如果重试三次还拿不到数据就返回None让上层逻辑决定是跳过还是报错。3.2 解析返回数据JSON 优先HTML 兜底接口返回的数据格式无非两种JSON 或者 HTML。JSON 是最理想的解析干净利落。但你也要做好只能拿到 HTML 的心理准备这种情况下我会用正则或者简单解析来做兜底。import json import re def parse_forecast_response(raw_data, source_formatjson): 把接口返回的原始数据解析成统一结构 if source_format json: return parse_from_json(raw_data) elif source_format html: return parse_from_html(raw_data) else: raise ValueError(f不支持的格式: {source_format}) def parse_from_json(raw_data): 从 JSON 中提取关键字段 result {} result[constellation] raw_data.get(name, ) result[overall] raw_data.get(overall_rank, 0) result[lucky_color] raw_data.get(lucky_color, ) result[lucky_number] raw_data.get(lucky_number, 0) result[lucky_direction] raw_data.get(lucky_direction, ) result[summary] raw_data.get(desc, ) return result def parse_from_html(raw_data): 从 HTML 片段中用正则提取关键字段——兜底方案 result {} # 提取星座名称通常出现在 h3 标签附近 name_match re.search(rh3[^]*\s*([\u4e00-\u9fa5]座)\s*/h3, raw_data) if name_match: result[constellation] name_match.group(1) # 提取运势文案通常出现在 class 为 summary 的 div 中 summary_match re.search(rdiv[^]*class[^]*summary[^]*[^]*(.*?)/div, raw_data, re.S) if summary_match: # 去除所有 HTML 标签保留纯文本 text re.sub(r[^], , summary_match.group(1)).strip() result[summary] text return result代码里的两层解析策略是故意的优先尝试标准的 JSON 解析拿不到再退到 HTML 正则提取。很多接口号称返回 JSON但在某些场景下会直接吐出一段 HTML 错误页这时候兜底方案能帮你保住程序的运行。用正则解析 HTML 有两个要注意的坑。第一正则是脆弱的页面一旦改版你的正则大概率要跟着改所以这个方案是保命而不是首选。第二提取出来的文本要记得做去空白和去标签处理否则打印出来的时候会发现文案里全是奇怪的换行和样式残留。3.3 数据入库JSON 文件还是 SQLite拿到了解析好的数据下一步是存储。对于每日星座运势这种量级的数据——十二个星座一天 12 条记录——完全不需要上重型的数据库。我通常的做法是直接落成一个 JSON 文件按日期做区分一天一个文件。import json import os from datetime import datetime def save_forecast_data(data_list, base_dirdata): 保存运势数据到本地 JSON 文件按日期归档 today datetime.now().strftime(%Y-%m-%d) os.makedirs(base_dir, exist_okTrue) file_path os.path.join(base_dir, f{today}.json) # 如果文件已存在先读取旧数据然后按星座名去重合并 if os.path.exists(file_path): with open(file_path, r, encodingutf-8) as f: existing_data json.load(f) else: existing_data [] # 用星座名作为唯一标识新数据覆盖旧数据 data_map {item.get(constellation): item for item in existing_data} for item in data_list: if item and item.get(constellation): data_map[item[constellation]] item with open(file_path, w, encodingutf-8) as f: json.dump(list(data_map.values()), f, ensure_asciiFalse, indent2)这里去重逻辑值得多说一句按星座名做覆盖而不是简单地追加。因为同一天内我们可能因为数据源更新而重新拉取追加会导致重复记录而覆盖可以保证每个星座在同一天只有一份最新数据。ensure_asciiFalse这个参数很关键少了它你的 JSON 文件里中文全变成\u767d之类的转义序列虽然也能读但排查数据的时候会让人崩溃。indent2则是为了让文件可读性好一点调试的时候少费点眼力。3.4 主流程把上面所有环节串起来有了请求、解析、存储三个模块主流程就简单了一个循环把十二个星座跑一遍即可。from datetime import datetime, timedelta CONSTELLATIONS [白羊座, 金牛座, 双子座, 巨蟹座, 狮子座, 处女座, 天秤座, 天蝎座, 射手座, 摩羯座, 水瓶座, 双鱼座] def main(): 每日星座运势查询主流程 today datetime.now().strftime(%Y-%m-%d) success_count 0 all_data [] for idx, constellation in enumerate(CONSTELLATIONS): raw_data fetch_daily_forecast(constellation, today) if raw_data is None: print(f[{constellation}] 获取失败跳过) continue parsed parse_forecast_response(raw_data, source_formatjson) if not parsed: print(f[{constellation}] 解析失败跳过) continue all_data.append(parsed) success_count 1 # 控制请求频率避免触发限流 time.sleep(1) if success_count 0: save_forecast_data(all_data, base_dirdata) print(f成功获取 {success_count}/{len(CONSTELLATIONS)} 个星座的运势数据) else: print(全部失败请检查网络或接口状态)time.sleep(1)放在请求之后作用是控制节奏。十二个星座跑完只需要额外 11 秒但能大幅降低触发限流的概率。这个宁可等一秒不给对方添麻烦的思路是做数据类工具的基本修养。主流程里我用了成功计数这样你能清楚地看到哪些星座的数据拿到了哪些失败了。全部失败的情况下直接不写文件避免生成了空的没有意义的数据文件。4. 从数据到可执行程序数据展示与打包发布4.1 展示层终端比 Web 更符合 v1.0 的定位很多人的第一反应是做一个 Web 页面来展示星座运势。但 v1.0 阶段我更推荐用终端输出。原因很简单终端展示省掉了前后端联调、样式调试、端口配置等一堆事情专注于把数据本身呈现清楚。等你确认数据和逻辑都没问题了再套一层 Web 外壳也不迟。def format_display(data_list): 格式化输出运势数据到终端 if not data_list: print(暂无数据) return for item in data_list: print( * 40) print(f【{item.get(constellation, 未知)}】{item.get(date, )}) print(f整体运势{⭐ * int(item.get(overall, 0))}) print(f幸运颜色{item.get(lucky_color, 未知)}) print(f幸运数字{item.get(lucky_number, 未知)}) print(f幸运方位{item.get(lucky_direction, 未知)}) print(f运势详情{item.get(summary, 暂无描述)}) print( * 40)终端输出用 emoji 和分隔线做视觉区分效果很直观。整体运势用星星数量来表示比单纯打印数字更能让人一眼看懂。这里要注意int(item.get(overall, 0))做了类型转换防止某些数据源返回字符串形式的数字导致报错。数据展示完成后整个 v1.0 核心链路就走通了。接下来要考虑的是别人拿到你的代码怎么跑起来4.2 打包成独立可执行文件一劳永逸的发布方式如果你的工具是给自己用的直接命令行跑 Python 脚本就行。但如果要发给朋友用没人愿意先装 Python、再装依赖、最后还要在命令行里敲一串命令。打包成独立可执行文件是更合适的发布方式。最常见的做法是用 PyInstallerpip install pyinstaller pyinstaller -F --name constellation-tool main.py-F参数表示打包成单文件适合这种小工具用户拿到一个可执行文件就能跑。如果你的项目里有用到配置文件或数据目录记得用--add-data参数把它们一起打进去否则打包后的程序在别人电脑上会找不到这些资源文件。打包完成后在dist/目录下会找到生成的可执行文件。双击运行或者在终端里执行效果跟直接跑 Python 脚本是一样的。但有几个典型问题要注意一是路径问题打包后程序的当前工作目录不一定是可执行文件所在目录代码里如果用了相对路径读写文件要格外小心二是杀毒软件误报PyInstaller 打包的程序经常会被某些安全软件当作未知文件拦截加白名单即可。4.3 加一个简单的命令行参数控制查询日期用户实际使用时很多人不只是想查今天的运势还想查明天的。给主流程加上一个可选参数支持指定日期查询import argparse if __name__ __main__: parser argparse.ArgumentParser(description每日星座运势查询工具) parser.add_argument(--date, typestr, defaultdatetime.now().strftime(%Y-%m-%d), help指定日期格式 YYYY-MM-DD默认今天) args parser.parse_args() main(query_dateargs.date)这个改动很小但实用性提升明显。用户可以提前看看明天甚至后天的运势数据源只要支持按日期查询这个功能就是天然成立的。查询日期这个参数也方便你做数据验证——你可以拿昨天的数据来对比手动填写的数据库记录检查解析逻辑是否正确。5. 避坑记录5 个让星座查询工具突然翻车的真实场景5.1 全盘翻车接口返回内容编码错乱现象程序运行后终端打印出来的星座名全是乱码类似白羊座这种运势文案也完全没法读。明明单独用浏览器访问接口地址是正常的。原因接口返回的编码不是 UTF-8而你的请求代码用resp.json()时默认按 UTF-8 解码。中文站点如果用的是 GBK 或者 GB2312就会全部变成乱码。这种问题最常见于一些老旧的接口服务。解决不要依赖库的自动解码手动检查响应头里的编码声明然后用正确的编码重新解码。代码里可以加一个兜底逻辑if resp.encoding ! utf-8: resp.encoding utf-8但这只对部分情况有效。更稳妥的做法是直接用resp.content拿原始字节然后按resp.encoding或者检测到的编码来解码。我一般会在测试阶段就打印一次resp.headers.get(Content-Type)确认编码后再写解析逻辑避免上线了才发现乱码问题。5.2 星座日期边界数据对不上号的经典原因现象查询结果是正常的但发现某个星座的运势文案和别的平台对不上。比如小A同学查双子座拿到的却是金牛座的运势描述。原因很多接口的时间边界处理是左闭右开的一个请求在 5 月 21 日零点后发出服务端可能已经切换到了双子座的区间但你的本地日期计算还在用前一天的时间戳。更基础的坑是星座日期对照表写错了把双子座的日期区间写成了 5.22-6.21但实际上标准是 5.21-6.21。解决在请求参数里把日期明确传给接口让服务端按你给的日期确定星座区间而不是依赖服务端的当天概念。同时把十二星座的标准日期区间写成一个常量字典对准公开的准确区间。测试的时候刻意选边界日期比如 5 月 21 日、6 月 21 日逐个检查边界星座是哪一边确认之后再固化数据表。5.3 文件路径翻车打包后数据写到奇怪的地方去了现象打包成 exe 后程序本身运行正常但数据文件找不到或者数据写到了系统临时目录里用户第二次运行的时候读不到第一次的数据。原因PyInstaller 打包的程序在运行时当前工作目录不一定是用户双击时所在的那个目录。如果你在代码里直接用open(data.json, w)这种相对路径文件会写到启动时的工作目录这个目录可能是用户的主目录也可能是系统临时目录完全没有确定性。解决把数据目录的路径绑定到可执行文件所在的目录import os import sys def get_app_dir(): if getattr(sys, frozen, False): # 打包后程序所在目录是可执行文件的目录 return os.path.dirname(sys.executable) else: # 开发环境直接用当前目录 return os.path.dirname(os.path.abspath(__file__))然后所有文件读写都用os.path.join(get_app_dir(), data, xxx.json)。这样不管用户把 exe 放到哪里数据都会写到 exe 旁边的data目录里行为可预期排查也方便。5.4 字段缺失翻车接口改了字段名却没被告知现象程序运行第 30 天突然所有星座的运势都显示暂无数据或者某些字段全部为空。原因接口方在没发公告的情况下把字段名改了比如把lucky_color改成了luckyColor或者把desc改成了description。你的代码按旧字段名取值取不到自然就是空值。解决一是做好字段取值的兜底raw_data.get(lucky_color) or raw_data.get(luckyColor)这种二段式取值能兼容一版字段变更二是做数据完整性校验在解析完成后检查关键字段是否都有值如果有缺失就打印警告日志提前发现问题而不是等到用户来投诉。这种日志先行的习惯能帮你减少很多线上排查时间。5.5 频率限制翻车短时间内被接口拉黑现象程序运行到一半突然连续报错邮件提示 403 Forbidden。重试三次之后还是失败整个程序退出。原因请求频率超过了接口的限制。可能是你自己在循环里没加间隔也可能是程序被设定成了每隔几分钟跑一次但没做数据去重每次都在重新请求前一天的数据。解决核心是两点。第一请求循环里加间隔前面已经提到过time.sleep(1)在实际项目中可以按接口文档的限速要求调整。第二本地做数据缓存同一天同一星座的数据如果已经存在且是当天获取的就直接从本地读不再发请求。这样既减轻对方服务器压力也降低自己被拉黑的风险。6. 让工具更耐用的进阶改造缓存、重试与多源降级v1.0 跑通之后你会开始琢磨怎么让这个工具更稳定、更智能我根据自己的使用教训整理了几个值得优先做的进阶改造。第一个改造是加本地缓存和过期策略。现在的流程是每次运行都请求一次接口但用户可能一天内运行好几次每次都拉一遍数据完全没必要。在data/目录下加一个缓存文件记录每个星座每个日期的最后获取时间当天已经成功获取过的数据直接读缓存不重复请求。这个改动能把请求量降低一个数量级而且用户体验完全无感。第二个改造是失败自动重试与数据补拉。现在的重试逻辑只覆盖单次请求如果整个流程跑到一半崩了重新运行会全部重来。更聪明的做法是把请求和存储分开哪个星座失败了就记录失败名单流程结束后单独对失败名单做二次重试。这样不会因为一个星座的失败而浪费整个流程的进度。第三个值得做的改造是多数据源降级。单一数据源总有不可靠的时候维护一个备选接口首选挂了自动切换到备选。实现方式是在请求模块加一层抽象先试首选接口捕获异常后尝试备选两者都失败才真正报错。这样整个工具的抗风险能力会提高很多。第四个方向是内容扩展。星座运势这类数据天然适合做定时推送——每天早上定时拉取当天的运势数据通过通知渠道推送给用户。结合黄历宜忌、塔罗牌占卜等数据源还能做出一个复合型的每日资讯小工具。这个方向的技术栈还是这些不会增加太多复杂度。回到我自己的习惯每次改完这些功能我会把程序跑一遍连续多天的模拟数据来验证稳定性尤其留意跨天时缓存是否失效、接口返回的日期是否切换正确。这种看着琐碎但必要的验证能省掉很多上线后半夜被喊起来的痛苦。做工具的心态就是这样——你多花一小时的调试用户就能少浪费一分钟的等待。希望这篇拆解能帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →