☰
IATA航空公司代码PDF解析与数据库落地:从匹配到版本更新
2026/10/2 7:40:46 网站建设 项目流程

简介:这份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 文本抽成可入库的 CSV

PDF 解析没有万能库,常见组合是 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, start=1): # 按表格线抽,若抽不到再退回 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", index=False, encoding="utf-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", dtype=str).fillna("") df["errors"] = df.apply(validate_row, axis=1) bad = df[df["errors"].map(len) > 0] bad.to_csv("airline_codes_bad.csv", index=False, encoding="utf-8-sig") print(f"问题行 {len(bad)} 条,已输出待修")

逻辑说明:校验函数返回错误列表而不是布尔值,是为了让修复人员一次看到所有问题。国家列表实际应从标准文件加载,示例里只列了几个。dtype=str 防止 pandas 把数字码前导零吃掉,这是血泪经验,一旦变成 int,'001' 就找不回来了。问题行单独输出,不要混在正常数据里。

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 读取指定 dtype=str,数据库列用 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。这样做的代价是前期慢,但后面接口调用方很少再报“代码转错”。代码表这种东西,平时没人夸,一出错就是全链路对账,稳比快重要。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询