简介面向ArcGIS初学者的一份Python脚本入门教程围绕让GIS任务自动化的目标把从零基础到能写出地理处理脚本的路径拆成两段前半部分讲Python的数学运算符、字符串操作、模块导入、def自定义函数、if/while/for流程控制与简单输入输出后半部分转入ArcGIS集成应用涉及Geoprocessor对象的创建、地理处理帮助的获取、工具箱与别名调用、ModelBuilder中嵌入脚本、PythonWin断点调试以及用Describe与Exists读取数据信息、地图代数在脚本中的使用。整套内容以单个docx文档交付压缩包约490KB文件总数1个正文配有目录与分级小标题理论说明与代码片段穿插便于按章节顺序阅读或按需跳查。已有1494人学习对刚接触arcpy的读者来说它可作为边看边练的入门读物帮助建立从语法到地理处理工具的完整认知。1. 一个下午砍掉 200 次手动裁剪ArcGIS 里 Python 脚本能接住哪些活手上压着两百多个分幅数据要按同一条河流的缓冲区逐个裁剪、再统一投影鼠标点一轮就是一个下午还容易点错图层。这份《ArcGIS 中 Python 脚本的使用》笔记就是冲着这类活来的它不教通用编程而是把桌面端那套地理处理工具从对话框搬进脚本文本让重复动作变成一次循环。原稿分两大块前半是 Python 语法最小集——运算符、字符串、模块、def、if/while/for、输入输出后半是接入层——geoprocessor 对象、List 与 Describe、工具箱别名、在 ModelBuilder 里挂脚本、PythonWin 调试、数据管理与游标。刚上手地理处理脚本的人能照着走被分幅数据磨过一遍的老手也能拿它当对照材料核对自己写的那几行是不是踩在同一个坑上。2. geoprocessor 对象与工作空间脚本跑不起来时先查这五行绝大多数“脚本一运行就报错”的场面问题不在算法而在开头几行没把入口对象和工作空间摆正。地理处理工具本质上是桌面端注册好的一批 COM 组件脚本要做的是拿到那个入口告诉它“我在哪个目录下干活”“输出能不能覆盖已有文件”剩下的才是工具调用。2.1 创建 geoprocessor先弄清你的版本该 import 谁9.x 时代的标准入口是arcgisscripting用create()拿到对象括号里的版本号不是装饰它决定了后续能用哪些工具集同一年代还有一条路是走 COM 派发好处是不绑死在随桌面端安装的那个解释器版本上。10.x 之后入口换成arcpy模块本身就是那个对象不用再手动 create。从旧笔记里抄代码最容易翻车的地方就在这——笔记是 9.x 写的你手上是 10.x 环境第一行就挂。# 路线一9.xarcgisscripting 模块create() 里写目标版本 import arcgisscripting gp arcgisscripting.create(9.3) # 版本号决定可用工具集写错会缺工具 gp.workspace rC:\proj\data # 工作空间可以是文件夹、gdb 或 coverage # 路线二10.x 及以后arcpy 直接就是入口 import arcpy arcpy.env.workspace rC:\proj\data.gdb arcpy.env.overwriteOutput True # 允许覆盖同名输出逻辑上只有两件事先拿到入口再声明上下文。参数说明上create()的版本号要和你实际安装的主版本对齐workspace支持三种形态——普通文件夹放 shapefile、地理数据库.gdb、coverage 工作空间选错形态会让 List 系列方法返回空列表而不是报错这是个典型的“静默失败”。走 COM 派发那条路的ProgID 形如某个前缀加GpDispatch.1具体值在系统注册表的HKEY_CLASSES_ROOT下搜GpDispatch就能定位不同主版本的后缀不一样别硬编。那条“不要手动升级随桌面端安装的 Python 版本”的提醒是我见过最省后悔药的一句话。桌面端的 Python 是跟着安装包走的第三方库、COM 注册、路径变量全部按那个版本对齐你换了解释器import arcpy会在一个很奇怪的地方失败报错信息还指向别处。2.2 workspace 与 overwriteoutput两个属性决定脚本能不能重复跑workspace是相对路径的基准。脚本里写streams.shp实际读的是workspace\streams.shp如果 workspace 设成了 gdb而输入是文件夹里的 shp工具会报找不到数据但提示文案往往只写文件名看着像文件丢了其实是上下文不对。overwriteoutput默认是关的。这意味着你第二次跑同一段脚本工具会拒绝写同名输出并抛错。第一遍跑通、第二遍报错很多人第一反应是数据坏了其实是这个开关没打开。属性典型值不设的后果workspace文件夹 / .gdb / coverage相对路径解析失败报“数据不存在”overwriteoutput1 或 True第二次运行报输出已存在scratchWorkspace临时目录中间结果散落在源数据目录outputCoordinateSystem目标坐标系输出跟着输入走批量结果投影不统一scratchWorkspace和outputCoordinateSystem这两个不是必填但批量任务里建议显式给上。前者把中间产物收进一个临时目录跑完能一把清掉后者在输入数据投影混乱时强制所有输出落到同一个坐标系下省掉后面拼图时的对齐麻烦。提示workspace 一旦设在 gdb 里ListFeatureClasses()返回的就是要素类名不带扩展名设在文件夹里则带.shp。后面拼路径时这两种形态不能混着用。2.3 一段能直接抄的脚本骨架把上面几件事按顺序摆好就是下面这个骨架。它做的事很简单设上下文、检查输入、调工具、打印结果。看着平淡但批量脚本 80% 的稳定性来自这十行。import arcpy, os # 1. 上下文工作空间 覆盖开关 临时目录 arcpy.env.workspace rD:\gis\proj\work.gdb arcpy.env.overwriteOutput True arcpy.env.scratchWorkspace rD:\gis\tmp src streams # 要素类名不带扩展名因为在 gdb 里 buf streams_buf200 dist 200 Meters # 带单位比裸数字安全 # 2. 输入检查不存在就早退别让工具去抛难懂的错 if not arcpy.Exists(src): arcpy.AddError(输入不存在: src) raise SystemExit(1) # 3. 调工具显式写全参数不依赖默认值 arcpy.Buffer_analysis(src, buf, dist, FULL, ROUND, ALL) # 4. 输出确认 if arcpy.Exists(buf): arcpy.AddMessage(完成: buf 要素数 str(arcpy.GetCount_management(buf)))逐段说明。第一段三个env属性分别管解析基准、覆盖策略和临时落点scratchWorkspace指向一个专门目录是为了让中间数据不污染成果目录。第二段用Exists做前置判断AddError是脚本工具的标配输出通道挂成工具后这些消息会进地理处理结果窗口比print更规范raise SystemExit(1)让脚本带着非零状态退出模型里后续步骤就不会拿空数据继续跑。第三段把 Buffer 的六个参数写全——输入、输出、距离、末端类型、转角样式、 dissolve 类型虽然FULL/ROUND/ALL恰好是默认值但显式写出来半年后回来看不用再翻帮助。第四段GetCount_management返回的是结果对象得用str()转一下才能拼进字符串9.x 的打印语句在这里也会因为类型不对而报错。3. List 与 Describe让脚本自己认数据、自己判类型手工操作时你打开目录看一眼就知道哪个是线、哪个是面、有没有投影脚本没有眼睛。List*系列负责“找得到”Describe负责“看得懂”Exists负责“别对着空气开火”。这三件套一凑批量脚本才不用把文件名一个个写死。3.1 ListFeatureClasses 的通配符与要素类型参数ListFeatureClasses({wild_card}, {feature_type})两个参数都可选。通配符走的是文件系统那套规则road*匹配所有以 road 开头的要素类型限定取值是Point、Polyline、Polygon、Multipoint、Annotation这些。两个参数组合起来能把“工作目录下所有线要素”一句话捞出来比手动挑选可靠。调用示例返回内容arcpy.ListFeatureClasses()工作空间下所有要素类arcpy.ListFeatureClasses(road*)名字以 road 开头的arcpy.ListFeatureClasses(*, Polygon)只有面要素arcpy.ListFields(landuse, *, All)要素类的全部字段对象arcpy.ListRasters(*, TIF)工作空间下所有 TIFF 栅格有个容易忽略的边界ListFeatureClasses只认工作空间直接下层的要素类不进子目录、不进要素数据集。数据放在要素数据集里的话得先用ListDatasets()拿到数据集名再临时把 workspace 切进去。coverage 工作空间同理遍历 coverage 要用ListDatasets而不是ListFeatureClasses因为 coverage 里的“要素类”在目录层级上是另一套结构。这条在混合格式的项目里最容易踩——脚本在纯 shapefile 目录下跑得好好的换到 gdb 里返回空列表一声不吭。字段列表也有类似的坑。ListFields第三个参数限定字段类型All、Integer、String、Double、Geometry之外还有OID、SmallInteger、Single、Date、Blob这些。字段对象有name、type、length、precision、scale几个属性拼 SQL 表达式时要用name而不是自己敲字段名否则遇到大小写或截断问题会莫名其妙。import arcpy arcpy.env.workspace rD:\gis\proj\landuse.gdb # 拿字段对象而不是字段名避免大小写与截断问题 field_list arcpy.ListFields(landuse, *, All) dsc arcpy.Describe(landuse) print(数据类型: dsc.dataType) for f in field_list: print(f.name | f.type | str(f.length))第二行开始ListFields返回的是字段对象列表Describe返回数据级信息。循环里用f.name和f.type字段长度是整型要转字符串才能拼。输出大概是“数据类型: FeatureClass”加一列字段名和类型用来核对上游数据是不是你以为的那张表。3.2 Describe 返回的对象怎么一层层往下读Describe给的是一个属性包顶层能读到dataType是 FeatureClass、RasterDataset、Shapefile 还是 Folder、catalogPath绝对路径、shapeTypePoint/Polyline/Polygon、spatialReference坐标系对象、extent范围对象、fields字段列表。往下还能钻spatialReference.name拿坐标系名字spatialReference.factoryCode拿 EPSG 编号extent.XMin之类的拿范围四至。dataType是分支判断的主开关。同一个脚本要处理矢量和栅格靠它分流比靠文件扩展名可靠得多——扩展名是可以改的dataType是驱动读出来的。shapeType则用来区分点线面比如裁剪时面裁线、面裁面参数不同就在这一层分开。坐标系判断有个常见误区判断“有没有投影”不能只看spatialReference.name是不是空字符串有些数据 name 有值但factoryCode是 0说明是自定义坐标系投影转换时仍然会出问题。稳妥的做法是同时看type和factoryCode类型为Unknown或者 factoryCode 为 0 的一律走“先定义再投影”的路径。3.3 Exists 放在循环开头批量骨架的写法批量处理的标准骨架是“遍历 → 判断 → 处理 → 记录”。判断放开头能让脚本在数据缺失时立刻报出来而不是跑了两百个文件后在最后崩一次。import arcpy arcpy.env.workspace rD:\gis\proj\work.gdb arcpy.env.overwriteOutput True target_sr arcpy.SpatialReference(4326) # 目标坐标系用 EPSG 编号最省事 fcs arcpy.ListFeatureClasses(*, Polyline) ok, skip 0, 0 for fc in fcs: if not arcpy.Exists(fc): # 理论上不会有防御性写法 skip 1 continue d arcpy.Describe(fc) # 没有坐标系的数据先 DefineProjection 再 Project if d.spatialReference.type Unknown: arcpy.DefineProjection_management(fc, target_sr) out fc _wgs84 arcpy.Project_management(fc, out, target_sr) ok 1 arcpy.AddMessage(fc - out) arcpy.AddMessage(成功 str(ok) 个跳过 str(skip) 个)这段的重点不在 Project 怎么调而在两个分支。第一spatialReference.type Unknown的数据不能直接 Project投影工具需要源坐标系信息没有就报错必须先DefineProjection把坐标系“贴”上去——注意这一步不改变坐标值只写元数据用错了会让数据带着错误的坐标系到处跑。第二计数器分开记成功和跳过跑完一眼能看出是全部处理了还是有一批漏网。SpatialReference(4326)用 EPSG 编号构造比手写 WKT 字符串稳也不会因为空格换行对不上。注意DefineProjection只写元数据、不动坐标Project才真正做坐标变换。两个工具用反了数据会在错误的坐标系下被“变换”一次事后很难查。4. 工具箱别名与参数顺序同名 Clip 撞车时的三种解法地理处理工具在界面上是分组摆的脚本里只有一个平铺的名字空间。裁剪工具在 Coverage 工具集里有一个、在数据管理工具集里也有一个两者参数还不一样。脚本没法像人一样“看着图标点”必须靠别名区分。4.1 别名表脚本里为什么要写 buffer_analysis每个工具箱都注册了一个短别名这个别名会作为后缀拼进脚本里的工具名形如工具名_别名。9.x 时代这是唯一写法10.x 之后又多了模块化写法arcpy.analysis.Buffer和arcpy.Buffer_analysis两种都能用后者兼容性更好从旧项目迁过来的脚本基本不用改。别名工具箱脚本里的写法analysisAnalysisClip_analysis/arcpy.analysis.ClipmanagementData ManagementCopyFeatures_managementconversionConversionRasterToPolygon_conversionarcCoverageClip_arcsaSpatial AnalystCon_sa/Slope_sa3d3D AnalystAddSurfaceInformation_3dcartographyCartographyCreateCartographicPartitions_cartographystatsSpatial StatisticsHotSpotAnalysis_statsgaGeostatistical AnalystKriging_galrLinear ReferencingCreateRoutes_lrgeocodingGeocodingGeocodeAddresses_geocoding这张表的价值在排错时最明显。报错信息里出现“工具不存在”或者“参数数量不对”先回头看一眼工具名后缀是不是配错了工具箱——Clip_analysis和Clip_arc参数结构完全不同一个是“输入要素、裁剪要素、输出”另一个要处理 coverage 的精度和容差。import arcpy arcpy.env.workspace rD:\gis\proj\work.gdb arcpy.env.overwriteOutput True # 先把 coverage 里的线转成 shp/要素类再做缓冲最后裁剪 arcpy.CopyFeatures_management(streams/arc, streams) arcpy.Buffer_analysis(streams, stbuff200, 200 Meters) arcpy.Clip_analysis(geology, stbuff200, geol_in_buf)三行的顺序是有讲究的CopyFeatures先把 coverage 要素导出成普通要素类后面两个工具就都能用 analysis 工具箱的通用版本Buffer生成缓冲区Clip用缓冲区去裁地质图。距离参数写成200 Meters而不是200是因为工作空间的坐标系单位不一定就是米带单位能让工具自己换算这行血泪经验值得记住。4.2 参数顺序不能靠猜从工具帮助反推脚本调用工具时参数按位置传跳不过去。想只给必选参数、其余吃默认值就必须把必选参数全写满。很多人图省事写一半就停结果第 4 个参数实际传的是第 5 个位置的东西工具不报错但结果是错的——这种静默错误最难查。可靠的做法是打开工具的帮助页找那段“语法”块参数从上到下的顺序就是脚本里的位置顺序带花括号的是可选、不带的是必选。Buffer的语法块依次是输入、输出、距离、末端类型、转角样式、dissolve 字段、dissolve 多部件脚本里要传 dissolve 就得把前面六个补齐或者用关键字形式传。参数位置Buffer 参数常见取值说明1输入要素要素类路径必选2输出要素类路径必选含 workspace 时写相对名3距离200 Meters必选建议带单位4末端类型FULL/FLAT线端是圆头还是平头5转角样式ROUND/MITRE拐角处理方式6dissolve 类型NONE/ALL/ 字段列表是否合并重叠部分7dissolve 多部件MULTI_PART/SINGLE_PART合并后是否允许分散部件关键字传参是另一条路写arcpy.Buffer_analysis(in_features..., out_feature_class..., buffer_distance_or_field...)。参数名在帮助页语法块里都有好处是不怕顺序记错坏处是参数名拼错时错误提示不友好老项目里也不太统一。4.3 Toolbox 属性与环境设置少敲字的代价9.x 里有个省事的写法gp.Toolbox Analysis之后调工具就可以不带后缀直接gp.Buffer(...)、gp.Clip(...)。这在连续用同一个工具箱的脚本里能少敲很多字代价是脚本的可移植性变差——别人读的时候看不出这是哪个工具箱的工具混用两个工具箱时还得来回切属性。10.x 的做法是模块化arcpy.analysis.Buffer语义清楚但拼写长了点。两种写法可以在同一个脚本里共存只要别在同一个工具上混着用。我的习惯是核心流程用完整后缀临时验证用短写法验证完再把短写法补齐免得后期维护时对着gp.Buffer猜是哪个 Buffer。环境设置则是另一类省事。arcpy.env.下面有一大批属性可以设默认值比如XYTolerance、MResolution、geographicTransformations、cellSize。这些设了之后所有工具调用都会继承不用一个个传参。风险在于隐式继承——脚本中间改了env.cellSize后面所有栅格工具的像元大小都跟着变查问题时要一路往上翻。批量脚本里我一般把 env 设置集中在开头一小段中间不再动。5. 避坑与排查脚本在 PythonWin 里能跑、挂成工具就翻车的五个原因同一个.py双击运行正常挂成脚本工具就报错或者反过来。这类问题几乎都集中在解释器版本、编码、路径、参数引用、数据锁这五处。下面五条是我自己撞过的按“现象 → 原因 → 解决”记。5.1 现象脚本里带中文注释就报 Non-ASCII character原因很直接Python 2.x 默认按 ASCII 解析源码文件里出现非 ASCII 字节、又没在文件头声明编码解释器在第二行就停下。9.x 时代随附的解释器就是 2.x从网上抄的示例里夹杂中文粘进去就挂。解决是加编码声明或者干脆把注释写成英文。声明必须放在文件第一或第二行且格式固定# -*- coding: utf-8 -*-注意声明要和文件实际保存的编码一致。文件存成 GBK、声明写 utf-8报错会从“非 ASCII”变成“编码解码失败”错误文案更长更绕。统一存 UTF-8 最省事编辑器的保存对话框里能选。5.2 现象路径明明存在工具说找不到数据原因八成是反斜杠被当转义字符吃了。C:\proj\data里的\p、\d单独看没问题但遇到\n换行、\t制表、\b退格就会变成控制字符路径被悄悄改写工具去找一个不存在的目录。解决有三条路字符串前加r变成原始字符串、用双反斜杠转义、或者统一换成正斜杠。我一般用第一种写文件路径时手比脑子快r前缀是肌肉记忆。p1 rC:\proj\data\work.gdb # 原始字符串反斜杠就是反斜杠 p2 C:\\proj\\data\\work.gdb # 转义写法等效 p3 C:/proj/data/work.gdb # 正斜杠桌面端也认字符串切片那个例子也值得记一笔某些 7.5 分幅 DEM 文本格式把高程单位标记写在第 539 个字符位置1表示英尺、2表示米。读第一行按下标取那个字符就能判断要不要给 Z 值乘 0.3048。不做这步判断直接算坡度结果会错得很离谱而且看不出错——数字看着都挺正常。5.3 现象第二次运行报“输出已存在”或者报“文件被另一程序锁定”两个不同的问题常被混为一谈。前者是overwriteOutput没开工具拒绝覆盖后者是输出被桌面端打开着或者上一次运行的进程没退干净文件句柄还占着。解决overwriteOutput在脚本开头统一设 True输出被锁的先关掉桌面端里打开的图层或者把输出写到带时间戳的目录里避开冲突。跑批量任务时我习惯把输出目录名加上日期跑完确认无误再合并。注意还有一种伪锁定——脚本中途异常退出地理处理锁文件残留在 gdb 里。表现是之后所有写操作都失败。这时候用桌面端自带的修复/压缩工具清一遍 gdb比手工删文件安全。5.4 现象从旧笔记抄的 print 语句直接语法错误9.x 的示例大量使用print x这种语句形式10.x 之后print是函数print x会被当成变量名和表达式报SyntaxError。反过来把print(x)抄进老环境里2.x 会把它当成打印一个括号里的元组输出多出一对括号。解决是按环境统一新脚本全部用print()老脚本批量替换。替换时注意print a b这类拼接直接换成print(a b)时括号要包住整个表达式别写成print(a) b。同类问题还有除法。2.x 里整数除整数得整数5/2是 2 不是 2.53.x 里是 2.5。涉及面积、比例的计算两个环境跑出来的数值不一样还不报错。5.5 现象Describe 拿到的对象是 None或者字段名对不上Describe对不存在的路径返回None随后访问.dataType就是AttributeError: NoneType。所以调用前先Exists挡一道是固定的防御写法。字段名对不上则多发生在 shapefile 上字段名超过 10 个字符会被截断中文别名和实际字段名是两回事。ListFields拿到的name是实际存储名界面上看到的可能是aliasName。拼查询表达式时用name别用界面上显示的那串。还有一个边界Describe拿到的extent是数据范围不是显示范围有空几何的数据集extent四个角的值可能是NaN拿去做裁剪范围会直接报错。先用GetCount看看有没有要素再决定要不要用范围。6. 脚本工具的输入输出参数把 .py 变成别人敢用的那个按钮脚本能自己跑只是第一步真正让它有用的是挂成工具别人不用看代码填几个参数就能用。做法是在脚本里用GetParameterAsText(index)按序号取界面上填的值AddParameter在脚本工具属性里定义参数名、数据类型、方向和默认值。序号从 0 开始和属性对话框里的参数列表顺序严格对应改一个序号两边都要动这是挂工具最常出错的地方。import arcpy # 序号 0/1/2 对应脚本工具属性里的参数顺序改顺序必须两边同步 in_fc arcpy.GetParameterAsText(0) # 输入要素类 out_fc arcpy.GetParameterAsText(1) # 输出要素类 dist arcpy.GetParameterAsText(2) # 缓冲距离类型设为 Linear Unit arcpy.env.overwriteOutput True arcpy.Buffer_analysis(in_fc, out_fc, dist, FULL, ROUND, ALL) # 输出参数让下游工具能接住这个结果必须用 SetParameter arcpy.SetParameterAsText(3, out_fc)四个参数里前三个是输入、第四个是输出。输出参数最容易漏——漏了之后工具在模型里跑完下游步骤拿不到输入模型会断在中间界面上看着“成功”实际链路是断的。SetParameterAsText那行必须在工具执行完之后调用放在前面写的是空值。验证方法我一般走三步一是直接在工具对话框里填一组数据跑通二是把它拖进 ModelBuilder看输出能不能连到下一个工具连不上就是输出参数没设三是故意传一个空参数看脚本是不是给出可读的中文提示而不是抛一长串调用栈。第三步最容易被跳过但它是别人用你工具时唯一能看懂的部分。从那以后我每次改脚本工具的参数都强制把“删掉旧工具、重新添加、重跑一遍最小用例”这三步走完再交给别人——跳过这一步省下的五分钟后面往往要用半小时去解释“为什么我这里能跑你那里跑不了”。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?