☰
基于Pyecharts的数据可视化开发(二)调用通义千问api分析爬虫数据
2026/10/4 16:48:11 网站建设 项目流程

1. 爬虫数据接大模型分析,卡在返回结构解析这一步

上一篇把“广州市2023年天气情况”爬下来存成 CSV 之后,很多人第一反应是直接把整份文件丢给大模型,让它给结论。我一开始也这么干,结果要么请求体超长被拒,要么返回一大段散文,根本没法往 Pyecharts 里塞。问题不在模型,而在中间少了一层“结构化转换”:大模型输出的是自然语言,Pyecharts 要的是字段和数值,两者之间必须有一个稳定的解析层。

这篇就聚焦这条链路:爬虫 CSV → 通义千问 API 分析 → 返回结构解析 → 字段清洗 → Pyecharts 图表映射。适合已经会 pandas 读表、想用大模型给数据“加一层解读”再可视化的同学。核心检索词就是 Pyecharts 数据可视化、通义千问 API、爬虫数据分析,下面每一步都给可复制的配置和脚本。

先说清楚大模型在这条链路里的定位:它不是替代你算均值、算极值,那些 pandas 一行就搞定;它擅长的是把统计结果翻译成人话,比如“7 月高温天数集中、昼夜温差收窄”这种结论,再让你把结论和图表标题、注释绑定。所以正确姿势是:先用 pandas 做确定性统计,把统计摘要交给模型,模型返回结构化 JSON,你再解析成 Pyecharts 能用的字段。这样既省 token,又避免模型瞎编数字。

我试过直接把 300 多行原始记录拼成 messages 发过去,返回的文本里数字和原始表对不上,因为模型在长上下文里会“概括性失真”。改成只发统计摘要后,结论稳定多了。下面从接入配置开始,一步步把这条链路跑通。

2. 通义千问 API 接入前置:Base URL、Key 与模型 ID 三件套

不管你是用 OpenAI 兼容 SDK 还是直接 requests,调通义千问都要凑齐三件套:Base URL、API Key、Model ID。这三个任何一个写错,报错信息都不一样,后面排障章节会逐个对照。

Base URL 走兼容模式是https://dashscope.aliyuncs.com/compatible-mode/v1,注意结尾是/v1,不要多加/chat/completions,SDK 会自己拼。API Key 在阿里云百炼控制台的 API-KEY 管理里创建,创建后只显示一次,复制下来存好。Model ID 是最容易踩坑的地方:控制台里模型叫 Qwen-Long,但代码里传的 model 字段不一定是qwen-long。我实测下来,分析长文本用qwen-long这个 ID 是能通的,但有些账号或某些时段会提示模型不存在,这时候去模型列表页核对当前可用的 ID,别凭记忆写。

如果你不想在阿里云和本地环境之间来回切,也可以把兼容 OpenAI 协议的网关作为统一入口,比如 TaoToken 的 API 地址https://taotoken.net/api,它同样接受 Base URL + Key + Model ID 这套配置,换 base_url 就能复用同一份脚本。这样做的实际好处是:你本地只维护一套 OpenAI 客户端代码,换模型只改 model 字段,不用重写请求逻辑。

配置建议走环境变量,别硬编码。Windows 用setx DASHSCOPE_API_KEY "sk-xxx",macOS/Linux 写进~/.zshrc或~/.bashrc的export DASHSCOPE_API_KEY="sk-xxx"。代码里用os.getenv("DASHSCOPE_API_KEY")读取。硬编码的风险是分享脚本时 Key 跟着泄露,我见过有人把带 Key 的 notebook 传到公开仓库,几分钟就被刷爆额度。

验证 Key 是否可用,用最小请求打一发,别等整条链路写完才发现鉴权失败:

import os from openai import OpenAI client = OpenAI( api_key=os.getenv("DASHSCOPE_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) resp = client.chat.completions.create( model="qwen-long", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "只回复两个字:可用"}, ], ) print(resp.choices[0].message.content)

输出“可用”就说明三件套没问题。如果报from openai import OpenAI导入失败,是 openai 库版本太旧,pip install --upgrade openai升级即可。如果报 model 不存在,去模型列表核对 ID。这一步过了,再往下做数据转换。

3. 可复制配置:从 CSV 统计摘要到结构化 JSON 请求

这一步是整个链路的核心。目标是把爬虫 CSV 变成“模型能读懂、返回能解析”的请求。分三段:读表统计、拼请求体、约束返回格式。

先读表并生成统计摘要。不要发原始行,发聚合结果:

import pandas as pd import json df = pd.read_csv("guangzhou_weather_2023.csv") df["date"] = pd.to_datetime(df["date"]) summary = { "row_count": int(len(df)), "date_range": [str(df["date"].min().date()), str(df["date"].max().date())], "temp_high_avg": round(float(df["temp_high"].mean()), 2), "temp_low_avg": round(float(df["temp_low"].mean()), 2), "temp_high_max": float(df["temp_high"].max()), "rain_days": int((df["rainfall"] > 0).sum()), "rain_total": round(float(df["rainfall"].sum()), 2), "monthly_rain": df.groupby(df["date"].dt.month)["rainfall"].sum().round(2).to_dict(), } print(json.dumps(summary, ensure_ascii=False, indent=2))

monthly_rain的 key 是月份数字,JSON 序列化后会变成字符串,解析时注意转换。这个摘要大概几百 token,比原始表小两个数量级。

接着拼请求体,关键是 system prompt 里强制模型只返回 JSON,并给出字段 schema:

system_prompt = """你是数据分析助手。根据用户提供的天气统计摘要,输出严格 JSON,不要任何解释文字。 JSON 结构: { "overview": "一句话总体结论", "highlights": ["要点1", "要点2", "要点3"], "monthly_rain_rank": [{"month": 1, "rain": 0.0}], "advice": "一句出行建议" } monthly_rain_rank 按 rain 从高到低排序。""" user_prompt = "统计摘要如下:\n" + json.dumps(summary, ensure_ascii=False) resp = client.chat.completions.create( model="qwen-long", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], response_format={"type": "json_object"}, ) raw = resp.choices[0].message.content print(raw)

response_format={"type": "json_object"}是兼容模式下的 JSON 模式,能大幅降低返回带 markdown 代码块的概率。但注意:即使开了这个,模型偶尔还是会在 JSON 前后加反引号,所以解析前要清洗。

如果你用 TaoToken 作为统一入口,把base_url换成https://taotoken.net/api,其余请求结构不变,model 字段按你选的模型填。这样本地脚本不用为每个供应商写一套。

清洗和解析返回:

import re def parse_model_json(text: str) -> dict: text = text.strip() text = re.sub(r"^```(?:json)?", "", text).strip() text = re.sub(r"```$", "", text).strip() return json.loads(text) data = parse_model_json(raw) print(data["overview"]) print(data["monthly_rain_rank"])

到这里,模型返回的已经是带monthly_rain_rank列表的结构化数据,可以直接喂给 Pyecharts。这一步的坑在于:模型返回的 month 可能是字符串 "1",rain 可能是字符串 "123.4",渲染前统一转 int/float,否则 Pyecharts 会把它们当类目而不是数值。

4. 端到端验证:把分析结果渲染成 Pyecharts 图表

现在把解析后的数据映射到图表。用两个图:月度降雨柱状图 + 高温低温折线图,标题和注释用模型返回的 overview 和 highlights。

from pyecharts import options as opts from pyecharts.charts import Bar, Line, Grid rank = data["monthly_rain_rank"] months = [str(int(item["month"])) + "月" for item in rank] rains = [float(item["rain"]) for item in rank] bar = ( Bar() .add_xaxis(months) .add_yaxis("月降雨量(mm)", rains, color="#5470c6") .set_global_opts( title_opts=opts.TitleOpts( title="广州2023年月度降雨量", subtitle=data["overview"], ), xaxis_opts=opts.AxisOpts(name="月份"), yaxis_opts=opts.AxisOpts(name="降雨量(mm)"), ) ) monthly_temp = df.groupby(df["date"].dt.month).agg( high=("temp_high", "mean"), low=("temp_low", "mean") ).round(1) line = ( Line() .add_xaxis([str(m) + "月" for m in monthly_temp.index]) .add_yaxis("月均高温", monthly_temp["high"].tolist(), is_smooth=True) .add_yaxis("月均低温", monthly_temp["low"].tolist(), is_smooth=True) .set_global_opts( title_opts=opts.TitleOpts(title="广州2023年月均气温"), yaxis_opts=opts.AxisOpts(name="温度(℃)"), ) ) grid = ( Grid() .add(bar, grid_opts=opts.GridOpts(pos_bottom="60%")) .add(line, grid_opts=opts.GridOpts(pos_top="60%")) ) grid.render("guangzhou_weather_report.html") print("渲染完成:guangzhou_weather_report.html")

打开生成的 HTML,你应该看到上半部分是月度降雨柱状图,副标题是模型给的一句话结论;下半部分是双折线气温图。验证成功的标志有三个:柱状图的月份顺序和monthly_rain_rank一致(从高到低),折线图 12 个月都有点,副标题文字和模型返回的 overview 完全一致。

如果副标题是空的,说明data["overview"]没取到,回去检查 JSON 解析。如果柱状图 x 轴出现重复月份,说明模型返回的 rank 里有重复项,去重后再渲染。这一步跑通,整条链路就闭环了:爬虫 CSV → 统计摘要 → 通义千问 API → JSON 解析 → Pyecharts 渲染。

5. 本篇常见报错排查:401、token 超限与 choices 解析失败

排障按报错原文对照,别凭感觉改。

401 Unauthorized / invalid_api_key:Key 没读到或写错。先确认os.getenv("DASHSCOPE_API_KEY")返回的不是 None,再确认 Key 没有多余空格。如果你用的是 TaoToken 的 API 地址,Key 也要换成对应平台的 Key,别混用。

400 invalid_parameter_error: Single round file-content exceeds token limit:这是把原始行全塞进 messages 导致的。解决方式是只发统计摘要,或者改用文件上传拿 fileid 再引用。我实测把 300 行原始记录直接拼 messages 必报这个,换成摘要后一次通过。

local proxy failed / connection error:本地网络到 API 端点的连接问题。先确认 base_url 拼写,兼容模式结尾是/v1,不要写成/v1/chat/completions。如果公司网络有出口限制,换网络环境重试,别在代码里加代理配置。

reading 'choices' / KeyError: 'choices':返回体里没有 choices,通常是请求失败但没抛异常。打印resp原始内容看 error 字段。常见原因是 model ID 写错,比如把qwen-long写成qwen_long,或者账号没开通对应模型。

json.decoder.JSONDecodeError:模型返回带了 markdown 反引号或前后有解释文字。用第 3 节的parse_model_json清洗,或者把 system prompt 里的“只返回 JSON”再强调一遍。如果还不行,加一次重试,把上一次的返回作为上下文让模型“只输出 JSON”。

OAuth / auth.json 相关报错:如果你在用 Claude Code 或 Codex 这类工具接模型,认证走的是 OAuth 或 auth.json,和本文的 API Key 模式不同。这类工具要配全三件套:Base URL、Key、Model ID,缺一个都会在启动时报认证失败。CC Switch、Cline MCP 同理,配置项里 Base URL 填兼容模式地址,Key 填 API Key,Model ID 填控制台核对的 ID。

排障顺序建议:先跑第 2 节的最小验证请求,确认三件套没问题;再跑第 3 节的统计摘要请求,确认返回是 JSON;最后跑第 4 节渲染,确认字段类型对。哪一步断,就查哪一步的报错。

6. 继续往下走:把这条链路固化成可复用脚本

链路跑通后,建议把它拆成三个函数:build_summary(csv_path)、ask_model(summary)、render_charts(data, df)。这样换一份爬虫数据,只改 CSV 路径就能复用。模型返回的highlights可以进一步做成图表注释,比如在柱状图最高月加 markdown 标注,让结论直接落在图上。

如果你要长期跑这类“爬虫 + 模型分析 + 可视化”的任务,可以考虑用 Coding Plan 把脚本工程化,把 API 调用、重试、JSON 校验封装成模块,避免每次手写。需要看模型返回效果、快速验证 prompt 的,可以直接在模型对话里试,改完 prompt 再落回脚本。API Key 管理和接入文档在控制台和文档页,配置项对照着填,别凭记忆。

最后留一个实用技巧:模型返回的 JSON 一定要做 schema 校验,哪怕只是检查overview、highlights、monthly_rain_rank三个 key 是否存在。我踩过的坑是模型某次返回了monthly_rain而不是monthly_rain_rank,渲染时直接 KeyError。加一行assert "monthly_rain_rank" in data就能提前拦住,比在 Pyecharts 里报错好定位得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询