
1. 不动产数据整理里SearchCursor 读取地籍号到底难在哪做不动产数据整理的人大概率都遇到过这种场面同一个项目甲方给的资料里既有.gdb又有.mdb宗地图层名字一个叫「宗地500」另一个叫「ZD」地籍号字段一会儿是DJH一会儿是BDCDYH还有的干脆是中文别名「地籍号」。你想用arcpy.da.SearchCursor批量把地籍号捞出来做核对结果脚本在 gdb 上跑得好好的换到 mdb 就报字段不存在或者读出来一堆None再或者中文变成乱码。这不是你脚本写得差而是 gdb 和 mdb 本身就是两套不同的存储机制。gdb 是 Esri 的文件地理数据库字段名区分大小写、支持长字段名、默认 UTF-8 编码mdb 是微软 Access 数据库字段名有 64 字符限制、对大小写不敏感、中文依赖系统区域设置Personal Geodatabase 还只支持 32 位 Python。你用同一段SearchCursor代码去套两种数据源字段名对不上、编码对不上、空值处理对不上三个坑必然踩中至少一个。这篇就围绕「用 SearchCursor 读取不动产 gdb/mdb 地籍号」这个具体动作展开。我会先讲清楚两种数据源的字段差异从哪来再给出一份可复制的字段映射配置然后是一段能同时跑 gdb 和 mdb 的批量提取脚本接着是逐条校验的动作最后把常见报错一个个拆开。中间会顺带说明怎么用 TaoToken 的统一 Key 和 API 通道来管理你在做数据整理时调用的模型凭据——毕竟现在很多整理流程会接一个模型做字段语义匹配或异常描述凭据散落各处很容易乱。适合谁看手里有不动产调查数据库、需要批量提取或核对地籍号的 GIS 从业者正在把 ArcPy 脚本从单机 gdb 迁移到多源 mdb 的开发者以及想给数据整理流程加一层模型校验、但不想把 Key 硬编码进脚本的人。核心检索词就是 SearchCursor、arcpy、gdb、mdb、地籍号下面每个环节都会落到可跟做的操作上。先说结论gdb 和 mdb 的差异不是靠一段「万能代码」抹平的而是靠「先探测字段、再映射、后校验」三步走。你只要把字段探测和映射这层做扎实后面的 SearchCursor 读取就是水到渠成的事。2. TaoToken 前置统一 Key 与 API 通道管住整理流程里的调用凭据在正式写脚本之前先把凭据这层理清楚。不动产数据整理本身是本地 ArcPy 的活但实际项目里往往还会接一些模型能力比如用地籍号做规则校验时让模型判断某个号段是否符合行政区划编码规则或者把异常记录丢给模型生成一段人话描述再或者用 coding 类模型帮你补全 ArcPy 脚本里的字段映射逻辑。这些调用如果每个脚本里都硬编码一个 Key时间一长你自己都记不清哪个 Key 对应哪个项目。TaoToken 在这里的角色是「统一入口」你通过一个 Key、一个 API 通道就能访问多种模型不用为每个模型单独申请和轮换凭据。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 这个地址不加 UTM 参数直接用于代码里的 base_url。具体到操作层面你需要做三件事第一拿到 Key。登录后在控制台创建 API Key路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Key 的管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。创建后立刻复制保存页面刷新后就不再完整显示。第二确认你要用的模型 ID。如果你只是做字段语义匹配或短文本校验用对话类模型就够如果你要让它帮你写或改 ArcPy 脚本走 coding 能力更合适Coding Plan 的入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。想先在网页里试一下模型对话效果可以用 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。第三把 Key 放进环境变量而不是写进脚本。这是我在多个项目里踩过坑之后固定下来的习惯脚本里只读os.environKey 本身放在系统环境变量或.env文件里.env加进.gitignore。这样你的 ArcPy 脚本可以随便发给同事不会因为带了一个 Key 而泄露。如果你用的是 Claude Code 这类命令行工具做脚本辅助它的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面会说明 Base URL 和 Key 怎么填。记住一个原则Base URL 用https://taotoken.net/apiKey 用你刚创建的那串Model ID 用控制台里列出的具体名称这三件套缺一不可后面在配置片段里我会再写一遍。注意TaoToken 是模型调用的统一通道不替代 ArcGIS 或 ArcPy 本身。你的 gdb/mdb 读取仍然由本地 ArcPy 完成TaoToken 只负责你在整理流程中额外发起的模型请求。把凭据这层管住之后我们就可以专心对付 SearchCursor 和字段映射了。3. 可复制配置字段映射 JSON 与 SearchCursor 批量提取脚本这一节是全文的核心操作区。我会先给一份字段映射配置再给一段能同时处理 gdb 和 mdb 的脚本最后给一个模型调用的 settings 片段方便你把校验环节接上。3.1 字段映射配置field_mapping.json不动产项目里地籍号的字段名五花八门与其在代码里写一堆 if-else不如抽成一份 JSON 配置。下面这份是我按常见命名整理的你可以直接复制后按自己项目改{ gdb: { 宗地: { djh: [DJH, ZDDJH, BDCDYH, 地籍号], xzqdm: [XZQDM, 行政区代码], zl: [ZL, 坐落] }, 房屋: { djh: [DJH, FWDJH, 房屋地籍号], fwbh: [FWBH, 房屋编号] } }, mdb: { 宗地500: { djh: [DJH, ZDDJH, 地籍号], xzqdm: [XZQDM], zl: [ZL] }, 房屋: { djh: [DJH, FWDJH], fwbh: [FWBH] } } }这份配置的关键在于每个逻辑字段比如djh对应一个候选字段名列表脚本按顺序去探测命中第一个就用。gdb 和 mdb 分开写是因为 mdb 的字段名往往更短、更可能是中文别名而 gdb 里长字段名更常见。3.2 字段探测函数读取之前必须先知道目标图层里到底有哪些字段否则 SearchCursor 直接报字段不存在。用arcpy.ListFields探测# -*- coding: utf-8 -*- import arcpy import os import json def list_field_names(feature_class): 返回图层所有字段名大写集合用于映射匹配 return {f.name.upper(): f.name for f in arcpy.ListFields(feature_class)} def resolve_field(feature_class, candidates): 按候选列表顺序返回第一个真实存在的字段名 real list_field_names(feature_class) for c in candidates: if c.upper() in real: return real[c.upper()] return Nonelist_field_names返回的是「大写名 - 真实名」的字典这样无论 gdb 里字段是DJH还是djh都能匹配上。resolve_field按你 JSON 里的顺序找找到就返回真实字段名找不到返回None后面脚本会跳过并记录。3.3 批量提取脚本兼容 gdb 与 mdb下面这段是主脚本输入可以是 gdb 路径也可以是 mdb 路径脚本会自动判断数据源类型并加载对应映射# -*- coding: utf-8 -*- import arcpy import os import json import csv def load_mapping(json_path): with open(json_path, r, encodingutf-8) as f: return json.load(f) def detect_source_type(workspace): 根据扩展名判断数据源类型 ext os.path.splitext(workspace)[1].lower() if ext .gdb: return gdb if ext .mdb: return mdb raise ValueError(不支持的数据源: workspace) def extract_djh(workspace, layer_name, mapping, out_csv): src_type detect_source_type(workspace) arcpy.env.workspace workspace layer_map mapping.get(src_type, {}).get(layer_name) if not layer_map: print([跳过] 映射中未配置: {} / {}.format(src_type, layer_name)) return djh_field resolve_field(layer_name, layer_map[djh]) if not djh_field: print([跳过] 未找到地籍号字段: {}.format(layer_name)) return xzqdm_field resolve_field(layer_name, layer_map.get(xzqdm, [])) zl_field resolve_field(layer_name, layer_map.get(zl, [])) fields [djh_field] if xzqdm_field: fields.append(xzqdm_field) if zl_field: fields.append(zl_field) rows_out [] with arcpy.da.SearchCursor(layer_name, fields) as cur: for row in cur: djh row[0] djh if djh is None else str(djh).strip() xzqdm zl idx 1 if xzqdm_field: xzqdm if row[idx] is None else str(row[idx]).strip() idx 1 if zl_field: zl if row[idx] is None else str(row[idx]).strip() rows_out.append([djh, xzqdm, zl]) with open(out_csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([地籍号, 行政区代码, 坐落]) writer.writerows(rows_out) print([完成] {} 提取 {} 条 - {}.format(layer_name, len(rows_out), out_csv)) if __name__ __main__: mapping load_mapping(field_mapping.json) workspace rE:\项目\连云区农村地籍.mdb extract_djh(workspace, 宗地500, mapping, djh_out.csv)几个细节值得说encodingutf-8-sig是为了让 Excel 打开 CSV 不乱码str(djh).strip()处理了数字型地籍号和前后空格None统一转成空字符串避免后面校验时None参与字符串运算报错。3.4 模型校验的 settings 片段如果你要在提取后接一个模型做地籍号规则校验把凭据配置抽成一份 settings路径放在项目根目录的.taotoken/settings.json{ base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model_id: 你的模型ID, timeout: 30 }脚本里这样读import os, json with open(.taotoken/settings.json, r, encodingutf-8) as f: cfg json.load(f) api_key os.environ.get(cfg[api_key_env]) base_url cfg[base_url] model_id cfg[model_id]Base URL、Key、Model ID 三件套齐了后面无论你是用 requests 直接发请求还是用某个 SDK都从这份配置里取不散落在各处。4. 验证请求跑通提取并确认成功结果配置和脚本都齐了接下来要验证它真的能跑通。验证分两层先验证 SearchCursor 提取本身再验证模型调用通道。4.1 验证 SearchCursor 提取先在一个已知的小图层上跑比如 mdb 里的「宗地500」。运行脚本后控制台应该输出类似[完成] 宗地500 提取 1287 条 - djh_out.csv打开djh_out.csv你应该看到表头「地籍号,行政区代码,坐落」下面每行一条记录。重点检查三件事第一地籍号列有没有空值。如果整列都是空说明字段映射没命中回到resolve_field打印一下实际字段名。第二中文有没有乱码。如果乱码检查 mdb 的编码环境或者确认你用的是 32 位 PythonPersonal Geodatabase 需要 32 位。第三条数对不对。可以拿 ArcGIS 属性表里的记录数对一下差太多说明 SearchCursor 的 where 条件或图层名有问题。再换 gdb 跑一遍同样的脚本只改workspace和layer_name。如果 gdb 和 mdb 都能输出结构一致的 CSV说明字段映射这层是通的。4.2 验证模型调用通道模型调用用一个最小请求验证。下面这段用标准库urllib发一个请求不依赖第三方包方便你在受限环境里测# -*- coding: utf-8 -*- import os, json, urllib.request with open(.taotoken/settings.json, r, encodingutf-8) as f: cfg json.load(f) api_key os.environ.get(cfg[api_key_env]) url cfg[base_url].rstrip(/) /v1/chat/completions payload { model: cfg[model_id], messages: [ {role: user, content: 判断地籍号 320703001001GB00001 是否符合行政区划前缀规则只回答是或否} ] } req urllib.request.Request( url, datajson.dumps(payload).encode(utf-8), headers{ Content-Type: application/json, Authorization: Bearer api_key }, methodPOST ) with urllib.request.urlopen(req, timeoutcfg[timeout]) as resp: result json.loads(resp.read().decode(utf-8)) print(result[choices][0][message][content])成功的话你会看到模型返回「是」或「否」。这一步验证的是Base URL 通、Key 有效、Model ID 正确。如果这一步过了你就可以把校验逻辑批量接在提取脚本后面。4.3 把两层验证串起来实际项目里我会先跑提取把 CSV 生成出来再写一个独立的校验脚本读 CSV、逐条调模型。这样提取和校验解耦提取失败不影响校验脚本的调试校验脚本改规则也不用重跑 ArcPy。校验脚本读 CSV 时同样用utf-8-sig保证中文列名正确。验证通过的标准很简单gdb 和 mdb 各跑一次CSV 条数、字段、编码都对模型请求返回预期内容。这两条都满足你的整理流程就立住了。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节把实际会撞到的报错一个个拆开。每个报错我都给出触发场景、原因和修法。5.1 401 Unauthorized触发场景模型请求返回 401。原因通常是 Key 没读到、Key 失效、或者 Authorization 头格式不对。先确认os.environ.get(cfg[api_key_env])不是None如果是None说明环境变量没设或名字写错。再确认请求头是Bearer加空格加 Key少空格也会 401。最后去控制台确认 Key 还在有效期内路径是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。5.2 local proxy failed触发场景请求发不出去报连接本地代理失败。这通常是你本机设置了系统代理但代理服务没开或端口变了。检查环境变量HTTP_PROXY、HTTPS_PROXY如果不需要代理就清掉如果公司网络要求走代理确认代理地址和端口正确。注意这里说的是你本机网络配置不是任何绕过网络管理的手段按你所在环境的合规要求配置即可。5.3 reading choices 相关报错触发场景解析响应时报KeyError: choices或读取choices失败。原因一般是响应体不是预期的 JSON 结构可能是请求被拒后返回了错误对象。先把原始响应打印出来看with urllib.request.urlopen(req, timeoutcfg[timeout]) as resp: raw resp.read().decode(utf-8) print(raw) result json.loads(raw)如果 raw 里是错误信息按错误信息定位如果是正常结构但没有choices检查 Model ID 是否写错。Model ID 写错时有的服务会返回错误对象而不是标准补全结构。5.4 OAuth 相关报错触发场景你用的某个客户端工具要求 OAuth 登录报 token 获取失败。这类工具通常支持 API Key 模式优先用 Key 模式接入Base URL 填https://taotoken.net/apiKey 填你的 KeyModel ID 填控制台里的名称。如果工具强制 OAuth看它的文档是否支持自定义 endpoint接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。5.5 SearchCursor 自身的报错除了模型侧ArcPy 侧也有几个高频错。RuntimeError: Cannot find field说明字段名没命中用arcpy.ListFields打印真实字段名。RuntimeError: Cannot open说明 workspace 路径错或 mdb 需要 32 位 Python。中文乱码则检查脚本文件头的# -*- coding: utf-8 -*-和 CSV 的utf-8-sig。空值导致的TypeError用None判断兜住。5.6 三件套对照表如果你用的是 Claude Code、Cline MCP 或 Codex 这类工具配置里必须写全三件套缺一个就连不上配置项值说明Base URLhttps://taotoken.net/api不加 UTM直接用于代码API Key控制台创建的那串放环境变量不硬编码Model ID控制台列出的名称大小写敏感照抄Codex 的auth.json里如果填 Key同样遵循这三件套Cline MCP 的配置里 Base URL 和 Key 分开填Model ID 填在模型字段。任何一处写错表现都是 401 或连接失败。6. 把提取、校验、凭据管理串成一条流水线到这里你已经有了字段映射配置、兼容 gdb/mdb 的提取脚本、模型校验的 settings 片段以及一份排错清单。把它们串起来就是一条可复用的不动产数据整理流水线先探测字段、按映射提取地籍号到 CSV再读 CSV 逐条做规则校验校验时通过 TaoToken 的统一 Key 和 API 通道调用模型凭据从环境变量和 settings 文件里取。我自己的习惯是给每个项目建一个目录里面放field_mapping.json、.taotoken/settings.json、extract_djh.py、validate_djh.py再加一个run_all.bat把两步串起来。这样换项目时只改映射和 workspace 路径脚本本身不动。地籍号这种字段不同区县的命名差异很大映射配置就是你的适配层值得花时间维护好。最后留一个实用技巧提取出来的 CSV 先别急着删它是你和甲方核对时的原始凭证。校验脚本输出的异常清单单独存一份标注每条异常的原因空值、格式不符、行政区代码对不上这样返工的时候有据可查。凭据方面定期去控制台轮换 Key旧 Key 停用避免一个 Key 用到底。