
简介这份PDF文档系统整理了国际航空运输协会IATA为全球航空公司指定的两字母代码并附有对应的所属国家或地区信息面向航空从业者、票务代理、航班数据分析人员及航空爱好者可用于航班信息识别、票务系统开发、航线申请资料查阅等场景。资源包内仅含1个PDF文件约690KB按代码首字符从A到Z及数字0-9顺序排列共26页涵盖琥珀航空、四川航空、宿雾太平洋航空、优比速快递等众多承运人及旅游分销系统代码并说明了两字母代码在预订、时刻表、票务、征税、航空提单及无线电通讯中的用途以及唯一英文号、数字加英文号、有控制性重复码三种分发形式。目前已有413人学习浏览适合需要快速查询航空公司代码、核对承运人所属地区或补充航空业务基础知识的读者参考使用。1. 从一份 IATA 航空公司代码 PDF 说起为什么你的匹配逻辑总在“对不上号”手里拿到一份 IATA 航空公司代码 PDF第一反应往往是“不就是两列数据航司名对二字码、三字码”。真把它塞进系统里跑翻车点立刻冒出来同一家航司在不同版本里名字写法不一致二字码和三字码的对应关系存在历史变更代码被回收后分配给了另一家PDF 复制出来的文本还夹着换行和全角空格。这些不是数据脏而是 IATA 代码体系本身带时间维度。这份 PDF 的价值不在于“查名字”而在于它是航线、运价、票务、行李规则、航班计划等系统的公共主键来源。谁把主键做稳谁后面所有关联查询都省心。这篇面向需要把 IATA 航空公司代码落进数据库、接口或对账流程的工程师从 PDF 解析一路讲到校验、更新和排错新手能照着跑熟手能看到边界。2. IATA 航空公司代码的结构二字码、三字码和数字码到底怎么分工2.1 三种代码的语义与使用场景IATA 航空公司代码并不是单一字段常见有三类。二字码IATA airline designator是两位字母或字母数字组合用于票务、行李牌、航班号前缀比如航班号 CA1234 里的 CA。三字码ICAO airline designator是三位字母用于空中交通管制、飞行计划、电报比如 CCA。数字码是三位数字用于部分结算和运价系统。三者不是一一映射的稳定关系一家航司可能同时持有多个二字码也可能在合并后保留旧码一段时间。PDF 里通常按航司名排列列出二字码、三字码、国家、备注。做系统时不要把二字码当唯一主键正确做法是建立“航司实体 代码分配时间段”的模型代码只是实体的属性。2.2 从 PDF 到结构化数据的字段设计拿到 PDF 后先别急着写解析脚本先定目标表结构。常见做法是拆成两张表一张航司实体表存 IATA 内部编号或自建 ID、航司全名、国家、状态一张代码分配表存实体 ID、代码类型、代码值、生效日期、失效日期、来源版本。这样处理历史变更时不用改主表。字段类型上二字码用 char(2)三字码用 char(3)数字码用 char(3) 而不是 int因为前导零有意义。国家字段用 ISO 3166-1 alpha-2不要直接存 PDF 里的国家名避免“United States”和“United States of America”两种写法。下面是一个最小建表语句MySQL 和 PostgreSQL 都能跑注意注释里的取舍。-- 航司实体表一个航司一行不随代码变更而新增 CREATE TABLE airline_entity ( entity_id BIGINT PRIMARY KEY, -- 自建主键不用代码当主键 airline_name VARCHAR(128) NOT NULL, -- 规范化后的航司名 country_code CHAR(2), -- ISO 3166-1 alpha-2 status VARCHAR(16) DEFAULT active, -- active/merged/defunct created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 代码分配表同一航司可有多个代码、多段时间 CREATE TABLE airline_code ( id BIGINT PRIMARY KEY, entity_id BIGINT NOT NULL, code_type VARCHAR(8) NOT NULL, -- iata2 / icao3 / numeric3 code_value VARCHAR(4) NOT NULL, -- 统一按字符串存保留前导零 valid_from DATE, valid_to DATE, source_version VARCHAR(32), -- 记录来自哪版 PDF FOREIGN KEY (entity_id) REFERENCES airline_entity(entity_id) ); CREATE INDEX idx_code_lookup ON airline_code (code_type, code_value, valid_from, valid_to);建表逻辑说明entity_id 用自建序列而不是二字码是为了在代码回收或合并时只改分配表。code_value 用 VARCHAR 而不是 CHAR是因为不同数据库对 CHAR 尾部空格处理不一致容易在等值查询时出现“看起来一样却查不到”的玄学问题。valid_from 和 valid_to 允许为空表示该代码在已知范围内长期有效。source_version 字段很关键后面做版本对比和回滚时靠它定位。索引把 code_type、code_value 和有效期一起放进去是因为最常见的查询是“给定代码和日期找航司”。2.3 用 Python 把 PDF 文本抽成可入库的 CSVPDF 解析没有万能库常见组合是 pdfplumber 抽文本、camelot 或 tabula 抽表格。IATA 代码 PDF 多为规整表格但复制出来常带换行。下面脚本用 pdfplumber 按页抽表再做清洗输出 CSV。运行前装好 pdfplumber 和 pandas。import re import pdfplumber import pandas as pd def clean_cell(v): if v is None: return # 去掉换行、全角空格、多余空白 v str(v).replace(\n, ).replace(\u3000, ) v re.sub(r\s, , v).strip() return v rows [] with pdfplumber.open(IATA航空公司代码.pdf) as pdf: for page_no, page in enumerate(pdf.pages, start1): # 按表格线抽若抽不到再退回 extract_text tables page.extract_tables() for table in tables: for row in table: cells [clean_cell(c) for c in row] if len(cells) 3: continue # 跳过表头二字码通常为 2 位三字码 3 位 if not re.fullmatch(r[A-Z0-9]{2}, cells[0] or ): continue rows.append({ iata2: cells[0], icao3: cells[1] if len(cells) 1 else , airline_name: cells[2] if len(cells) 2 else , country: cells[3] if len(cells) 3 else , page: page_no }) df pd.DataFrame(rows).drop_duplicates(subset[iata2, icao3, airline_name]) df.to_csv(airline_codes_raw.csv, indexFalse, encodingutf-8-sig) print(f抽取 {len(df)} 行来源页数 {page_no})逻辑说明clean_cell 处理 PDF 里最常见的三类噪声——换行、全角空格、连续空白。extract_tables 依赖 PDF 有表格线若你的 PDF 是纯文本排版改用 extract_text 后按固定列宽切分。跳过表头用正则判断首列是否为两位字母数字比按行号跳过稳因为不同版本表头行数会变。去重按三列组合避免同一航司因跨页重复出现。参数上encoding 用 utf-8-sig 是为了 Excel 直接打开不乱码如果后续要入库建议再输出一份不带 BOM 的 utf-8。这一步跑完先人工抽查 20 行重点看三字码列有没有把国家名误抽进来这是最常见的列错位。3. 把代码表接进业务系统匹配、校验和版本更新怎么做3.1 代码匹配的三种策略与选择依据代码进库后业务侧查询分三种。第一种精确匹配给定二字码和日期返回唯一航司用于票务和航班号解析。第二种模糊匹配用户输入航司名片段返回候选代码列表用于后台搜索。第三种反向匹配给定三字码找二字码用于电报和飞行计划转换。精确匹配必须带日期条件否则代码回收会导致返回多行。模糊匹配不要用 LIKE %关键词% 直接扫全表数据量虽小但并发高时仍会拖慢常见做法是建一张搜索词表把航司名拆成词元并建索引。反向匹配要注意三字码并非全局唯一少数情况下存在历史共用返回结果要带有效期供调用方判断。下面是一个带日期条件的精确查询示例注意 valid_to 为 NULL 表示仍有效。-- 给定二字码和业务日期查当前有效航司 SELECT e.airline_name, e.country_code, c.code_value, c.valid_from, c.valid_to FROM airline_code c JOIN airline_entity e ON e.entity_id c.entity_id WHERE c.code_type iata2 AND c.code_value CA AND (c.valid_from IS NULL OR c.valid_from 2024-06-01) AND (c.valid_to IS NULL OR c.valid_to 2024-06-01);参数说明code_value 传参前统一转大写并去空格避免用户输入 ca 查不到。日期条件两个方向都要判空因为历史数据可能只填了一端。如果返回多行说明该代码在该日期存在重叠分配需要人工核对来源版本不要用 LIMIT 1 掩盖问题。3.2 用校验规则挡住脏数据入库PDF 抽取难免出错入库前加一层校验能省掉后面大量排查。常见规则二字码必须匹配 ^[A-Z0-9]{2}$三字码必须匹配 ^[A-Z]{3}$数字码必须匹配 ^[0-9]{3}$同一 entity_id 下同类型代码在同一时间段不允许重复国家代码必须在 ISO 3166-1 列表内。下面用 Python 做入库前校验输出问题行而不是直接抛异常方便批量修。import re import pandas as pd ISO_COUNTRIES {CN, US, JP, DE, GB, FR, SG, AE} # 实际应加载完整列表 def validate_row(r): errors [] if not re.fullmatch(r[A-Z0-9]{2}, str(r.get(iata2, ))): errors.append(iata2 格式错误) if r.get(icao3) and not re.fullmatch(r[A-Z]{3}, str(r[icao3])): errors.append(icao3 格式错误) if r.get(country) and r[country] not in ISO_COUNTRIES: errors.append(国家代码不在 ISO 列表) return errors df pd.read_csv(airline_codes_raw.csv, dtypestr).fillna() df[errors] df.apply(validate_row, axis1) bad df[df[errors].map(len) 0] bad.to_csv(airline_codes_bad.csv, indexFalse, encodingutf-8-sig) print(f问题行 {len(bad)} 条已输出待修)逻辑说明校验函数返回错误列表而不是布尔值是为了让修复人员一次看到所有问题。国家列表实际应从标准文件加载示例里只列了几个。dtypestr 防止 pandas 把数字码前导零吃掉这是血泪经验一旦变成 int001 就找不回来了。问题行单独输出不要混在正常数据里。3.3 版本更新与差异对比IATA 代码 PDF 会更新新版本可能新增航司、修改名称、调整代码有效期。直接覆盖旧表会丢失历史正确做法是每次导入带 source_version导入后跑差异对比。常见对比维度新增代码、消失代码、名称变更、三字码变更。下面用 SQL 做一次简单差异查询假设新旧两版都已入库用版本号区分。-- 找出新版本有、旧版本没有的二字码 SELECT n.code_value, e.airline_name FROM airline_code n JOIN airline_entity e ON e.entity_id n.entity_id WHERE n.source_version 2024-06 AND n.code_type iata2 AND NOT EXISTS ( SELECT 1 FROM airline_code o WHERE o.source_version 2024-01 AND o.code_type iata2 AND o.code_value n.code_value );参数说明source_version 命名建议用年月便于排序。差异结果不要自动合并先人工确认尤其是“消失代码”可能是 PDF 排版问题导致漏抽不一定是真删除。确认后再更新 valid_to而不是物理删除。4. 避坑与排查IATA 代码落地时最容易翻车的五件事4.1 现象查询返回多行原因代码回收未设有效期解决补 valid_to 并加日期条件代码回收是 IATA 体系里的常态一个二字码停用几年后可能分配给新航司。如果表里只存代码和航司名没有有效期精确查询就会返回多行。排查时先看该代码在表里是否有多个 entity_id再看各自 valid_from 和 valid_to 是否重叠。解决分两步历史数据补有效期来源版本里能查到停用日期的就填查不到的至少把旧记录 valid_to 设为新记录 valid_from 前一天查询侧强制带业务日期条件。4.2 现象PDF 抽出的三字码列混入国家名原因表格列错位解决按列宽或表头定位不同版本 PDF 的列顺序可能变化有的把国家放在三字码前有的把备注插在中间。用固定索引取列必然翻车。排查时打印前 10 行原始 cells看列数和内容特征。解决优先用表头文字定位列索引找不到表头再按列宽切分抽取后加规则校验三字码必须是三位纯字母不符合的整行标记待修。4.3 现象数字码前导零丢失原因入库时被当整数解决全链路用字符串数字码如 001、012 在 CSV 打开、pandas 读取、数据库写入任一环节被转成整数前导零就没了。排查时对比原始 PDF 和库里的值长度变短即是。解决CSV 读取指定 dtypestr数据库列用 VARCHAR接口传输用字符串展示时再按需补零。这个坑一旦发生只能重新导入没有后悔药。4.4 现象航司名匹配不上原因全角半角、大小写、括号差异解决入库前规范化用户输入“中国国际航空”和 PDF 里的“Air China”对不上或者“ANA”和“All Nippon Airways”被当成两家。排查时把两边名字做字符级对比看是否有全角空格、英文括号和中文括号混用。解决入库前统一转半角、去多余空白、英文转大写或统一小写同时建别名表把常见简称、旧名、中文名映射到 entity_id不要试图用模糊匹配解决所有问题。4.5 现象版本更新后旧代码查不到原因物理删除或覆盖解决只追加不删除有人图省事新版本导入前先 TRUNCATE 旧表结果历史订单里的代码查不到航司。排查时看该代码在表里是否存在若整批消失就是被覆盖。解决导入只追加用 source_version 区分旧记录通过 valid_to 标记失效不物理删除。查询侧默认查当前有效需要历史时显式带日期。5. 进阶技巧用一张对照表把 IATA 代码和 ICAO 代码的转换做稳二字码和三字码的转换是高频需求但两者并非严格一一对应。进阶做法是维护一张转换对照表字段包括 iata2、icao3、entity_id、valid_from、valid_to、confidence。confidence 表示该对应关系的可信度来自官方 PDF 的记 high来自第三方补全的记 medium来自推测的记 low。查询时优先返回 high没有 high 再降级。下面是一个转换查询示例带可信度排序。-- 二字码转三字码优先高可信度 SELECT icao3, confidence FROM code_mapping WHERE iata2 CA AND (valid_from IS NULL OR valid_from CURRENT_DATE) AND (valid_to IS NULL OR valid_to CURRENT_DATE) ORDER BY CASE confidence WHEN high THEN 1 WHEN medium THEN 2 ELSE 3 END LIMIT 1;参数说明CURRENT_DATE 按数据库调整MySQL 用 CURDATE()。LIMIT 1 只在有可信度排序时使用否则会掩盖多义性。维护这张表时每次 PDF 更新后跑一次差异新增映射先记 medium人工确认后升 high。我自己的习惯是每月固定一天做版本对比把差异输出成 CSV 存档连续三个月无变化的映射才升 high。这样做的代价是前期慢但后面接口调用方很少再报“代码转错”。代码表这种东西平时没人夸一出错就是全链路对账稳比快重要。希望帮到你。本文还有配套的精品资源点击获取