尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python: xml转json 实战——用 TaoToken 统一 Key 打通解析与校验链路

Python: xml转json 实战——用 TaoToken 统一 Key 打通解析与校验链路 1. 从一份京东手机列表 XML 说起Python xml 转 json 到底难在哪如果你手上有一份爬虫产出的 XML想把它塞进数据管道、丢给前端或者写进 MongoDB第一反应基本都是「转成 JSON 不就行了」。真动手你会发现Python 里 xml 转 json 的坑不在「转」这个动作而在三件事属性往哪放、文本节点和子节点同名怎么合并、以及转完之后结构对不对。我拿一份典型的电商列表 XML 举例结构大概长这样products product id1001 skuP-1001 name手机A/name price currencyCNY2999.00/price tags tag5G/tag tag旗舰/tag /tags /product product id1002 skuP-1002 name手机B/name price currencyCNY1999.00/price tags tag性价比/tag /tags /product /products这份 XML 里有几个典型特征product有属性id和skuprice既有属性currency又有文本2999.00tags下面有多个同名tag。用现成库一把梭常见结果是属性丢了、多个tag只剩最后一个、或者price变成一个奇怪的嵌套对象。所以这篇不讲「装个库调个函数」就完事而是把整条链路走通用xml.etree.ElementTree解析、按字段映射规则转成 dict、json.dumps输出、再用 TaoToken 统一 Key 调接口做一次结构校验。适合正在写数据管道、爬虫后处理、或者做 XML 接口对接的同学。全文代码可直接复制运行Python 3.8 即可。先说清楚一个前提XML 和 JSON 不是一一对应的。XML 有属性、有文本、有混合内容、有命名空间JSON 只有对象、数组、字符串、数字、布尔、null。所谓「转换」本质是你自己定一套映射约定然后按约定把树拍平。约定不统一下游就会天天报字段缺失。我试过直接上xmltodict小文件很爽但遇到属性、重复标签、混合文本就开始加各种参数最后还是得自己写一层。所以下面这套脚本的思路是解析用标准库映射规则用配置输出用json.dumps校验交给接口。这样每一步都可控、可测、可回滚。2. TaoToken 前置准备统一 Key 打通解析与校验链路转换脚本本身不需要联网但「校验」这一步需要。为什么要在转换链路里加校验因为 XML 转 JSON 最容易出的问题不是报错而是静默错误字段名拼错、数组退化成对象、数字变成字符串脚本照样跑完等你写进数据库才发现下游全乱。这时候用一个模型接口对转换结果做一次结构体检比人肉盯 JSON 靠谱得多。TaoToken 在这里的角色是「统一 Key 的模型调用入口」。你不需要为不同模型分别维护 Key一个 Key 就能在解析、校验、甚至后续的字段语义归一化里复用。对数据管道来说少一套凭证管理就少一类事故。前置动作只有三步都很轻第一步拿到 Key。访问 API Keys 管理页创建https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite创建后复制那串sk-开头的 Key先存到环境变量别硬编码进脚本export TAOTOKEN_API_KEYsk-你的key第二步确认 Base URL。所有请求走同一个入口https://taotoken.net/api注意这个地址不带任何查询参数是纯 API 根路径。你在代码里拼/v1/chat/completions这类路径时基于它来拼。第三步选一个用于校验的 Model ID。校验任务对模型要求不高选一个响应快、稳定的即可。Model ID 要写全比如gpt-4o-mini这种完整标识不要只写gpt。三件套凑齐就是Base URL Key Model ID后面所有调用都靠这三个。如果你更习惯在对话界面里先手动验证一下 Key 是否可用可以打开模型对话页试一句https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite能正常返回说明 Key 和网络都没问题再回到脚本里调。这一步看着多余但能帮你把「Key 错」和「代码错」两类问题提前分开排障时省很多时间。对于长期跑数据管道的场景如果你后面还要接 Agent 或者做批量编码任务可以了解下 Coding Plan它把额度管理做得更集中https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite但就本篇的 xml 转 json 校验来说一个普通 Key 足够了。别一上来就上重装备先把链路跑通。3. 可复制配置ElementTree 解析 字段映射 json.dumps 输出这一节是全文核心给你一套能直接跑的脚本。我把它拆成三块解析器、映射配置、输出。先看完整代码再逐段解释。# xml2json_pipeline.py # Python 3.8 import json import os import xml.etree.ElementTree as ET # ---------- 字段映射配置 ---------- # 约定 # attr 表示取 XML 属性 # #text 表示取文本节点 # 其余 key 表示取同名子节点 FIELD_MAP { product: { id: id, sku: sku, name: name, price_value: price.#text, price_currency: price.currency, tags: tags.tag[], # [] 表示强制为数组 } } def _get_by_path(elem, path): 按 a.b.c / a.b.#text / a.b[] 取值 force_list path.endswith([]) if force_list: path path[:-2] parts path.split(.) cur elem for i, part in enumerate(parts): if part.startswith(): return cur.get(part[1:]) if part #text: return (cur.text or ).strip() # 普通子节点 nxt cur.find(part) if nxt is None: return [] if force_list else None cur nxt if force_list: # 收集所有同名兄弟节点 parent elem for part in parts[:-1]: parent parent.find(part) items parent.findall(parts[-1]) return [(it.text or ).strip() for it in items] return (cur.text or ).strip() if cur.text else None def xml_to_dict(xml_path, record_tagproduct): tree ET.parse(xml_path) root tree.getroot() mapping FIELD_MAP[record_tag] records [] for node in root.findall(record_tag): item {} for out_key, path in mapping.items(): item[out_key] _get_by_path(node, path) records.append(item) return {record_tag s: records} def main(): src products.xml dst products.json data xml_to_dict(src) with open(dst, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) print(fwritten {dst}, records{len(data[products])}) if __name__ __main__: main()配套的products.xml就用第 1 节那份。运行python xml2json_pipeline.py输出products.json{ products: [ { id: 1001, sku: P-1001, name: 手机A, price_value: 2999.00, price_currency: CNY, tags: [5G, 旗舰] }, { id: 1002, sku: P-1002, name: 手机B, price_value: 1999.00, price_currency: CNY, tags: [性价比] } ] }几个设计点值得说清楚。第一属性用前缀文本用#text这是为了在配置里一眼区分来源避免price这种既有属性又有文本的节点产生歧义。第二[]后缀强制数组解决「只有一个 tag 时退化成字符串」的经典问题——下游按数组处理就不会崩。第三映射配置和解析逻辑分离换一份 XML 只改FIELD_MAP不动函数。如果你更偏好 TOML 管理映射比如字段多到几十个可以外置成配置文件# field_map.toml [product] id id sku sku name name price_value price.#text price_currency price.currency tags tags.tag[]然后用tomllibPython 3.11读取import tomllib with open(field_map.toml, rb) as f: FIELD_MAP tomllib.load(f)这样非开发同学也能改字段映射不用碰 Python。踩过的坑是TOML 里[]会被当成数组语法所以tags tags.tag[]必须加引号否则解析报错。4. 验证请求用统一 Key 调接口做 JSON 结构校验脚本跑完只是「转出来了」不代表「转对了」。这一步用 TaoToken 的接口对 JSON 做结构校验。思路很简单把转换结果和一份期望的 schema 描述一起发给模型让它判断字段是否齐全、类型是否一致、有没有静默错误。先写校验函数import json import os import urllib.request BASE_URL https://taotoken.net/api API_KEY os.environ[TAOTOKEN_API_KEY] MODEL_ID gpt-4o-mini def validate_json(json_path, expected_desc): with open(json_path, r, encodingutf-8) as f: payload json.load(f) prompt ( 你是数据校验助手。下面是一份 JSON 和它的期望结构描述。\n 请只回答字段是否齐全、类型是否一致、有无明显静默错误。\n 期望结构\n expected_desc \n 实际 JSON\n json.dumps(payload, ensure_asciiFalse) ) body { model: MODEL_ID, messages: [{role: user, content: prompt}], temperature: 0, } req urllib.request.Request( BASE_URL /v1/chat/completions, datajson.dumps(body).encode(utf-8), headers{ Content-Type: application/json, Authorization: Bearer API_KEY, }, methodPOST, ) with urllib.request.urlopen(req, timeout60) as resp: result json.loads(resp.read().decode(utf-8)) return result[choices][0][message][content] if __name__ __main__: desc ( products 为数组每项含 id(str)、sku(str)、name(str)、 price_value(str)、price_currency(str)、tags(str数组)。 ) print(validate_json(products.json, desc))运行python validate_json.py正常返回类似字段齐全类型一致。tags 均为数组price_value 与 price_currency 分离正确未发现静默错误。这里有几个细节。temperature设 0让校验结果稳定可复现。Authorization用Bearer前缀这是标准写法。超时设 60 秒避免网络抖动直接挂掉。返回里取choices[0].message.content这是 OpenAI 兼容格式的固定路径。如果你在对话界面里手动验证可以把同样的 prompt 贴进去效果一致。区别只是脚本能进 CI手动适合一次性排查。校验通过后这份 JSON 就可以放心写进 MongoDB 或者丢给下游了。整个链路是XML 文件 → ElementTree 解析 → 映射配置 → json.dumps → 接口校验 → 入库。每一步都有明确输入输出出问题能快速定位到是哪一段。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth转换和校验跑起来后报错基本集中在几类。我按真实遇到的顺序列一下对照着查。401 Unauthorized。最常见。原因通常是 Key 没读到、Key 写错、或者Authorization头拼错。先确认环境变量echo $TAOTOKEN_API_KEY如果为空说明export没生效或者换了终端。如果 Key 正确检查请求头是不是写成了Authorization: sk-xxx少了Bearer前缀就会 401。还有一种情况是 Key 前后带了空格或换行复制时容易带上用strip()处理一下。local proxy failed / connection refused。这类报错说明请求根本没发出去卡在本地网络层。先确认 Base URL 拼对了是https://taotoken.net/api不要多写或少写路径。然后确认本机没有奇怪的网络配置拦截请求。如果是在容器里跑检查容器网络是否能出网。这类问题跟 Key 无关别去反复重建 Key。reading choices / KeyError: choices。说明请求发出去了但返回体不是预期的对话格式。常见原因是把错误响应当成了成功响应。先打印完整返回print(json.dumps(result, ensure_asciiFalse, indent2))如果里面是{error: {...}}那就是请求参数有问题比如 Model ID 写错、messages格式不对。确认 Model ID 是完整标识messages是数组且每项有role和content。修好参数后choices自然就有了。OAuth / 认证方式不匹配。如果你之前用的是别的认证体系可能残留了 OAuth 相关的配置或环境变量导致请求头被覆盖。检查一下有没有全局的HTTP_PROXY、OPENAI_API_KEY之类的变量干扰。最干净的做法是在脚本里显式指定 Key 和 Base URL不依赖隐式配置。字段静默错误。这类不报错但结果不对。典型表现tags只有一个元素时变成字符串、price_value变成null、属性丢失。排查方法是拿一份「边界样本」测只有一个 tag 的记录、没有属性的记录、空文本的记录。如果tags退化成字符串检查映射里有没有加[]。如果属性丢失检查路径有没有写前缀。编码问题。XML 里有中文时如果读取没指定encodingutf-8可能乱码。ET.parse一般能自动识别声明但json.dump记得加ensure_asciiFalse否则中文会变成\uXXXX虽然不影响解析但可读性差。把这几类对照一遍基本能覆盖 90% 的报错。剩下的多半是 XML 本身结构跟映射配置不匹配打印一下root的标签树就能看出来。6. 把校验接进管道统一 Key 的长期用法链路跑通之后真正省事的是把它变成可重复的动作。我的做法是把校验封装成一个函数在数据管道的每个转换节点后调用一次Key 从环境变量读Base URL 和 Model ID 写成常量。这样无论是本地调试还是 CI 跑批行为一致。如果你后面要接更多模型做字段归一化、语义去重统一 Key 的价值会更明显不用为每个模型单独配凭证换模型只改MODEL_ID一个常量。需要看完整接口说明的话接入文档在这里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite控制台可以看调用量和额度https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite一个实用技巧校验 prompt 里把「期望结构」写成固定模板别每次手写。可以存成schema.txt脚本读取后拼接。这样字段变了只改模板校验逻辑不动。另一个技巧是给校验结果加个简单的关键词判断比如返回里包含「字段齐全」就认为通过否则打日志告警方便接进监控。最后提醒一句模型校验是辅助不是替代。核心字段的类型和必填约束最好还是在代码里用显式断言兜底模型负责发现你没想到的边界情况。两者结合XML 转 JSON 这条链路才算真正稳。
返回列表