如何用geopy库获取哥伦比亚城市列表?解决KeyError: address报错
问题分析与解决
原代码的错误点
- 无效地址导致
location为None:你用的"Calle 1, Ciudad, Colombia"是虚构地址,geolocator.geocode()会返回None,直接访问location.raw会触发错误,后续的location.raw['address']自然会引发KeyError。 - 错误解析
locality字段:即使地理编码成功,location.raw['address']['locality']是单个字符串(比如某个城市名),不是列表,遍历它会逐个字符拆分,完全不符合需求。 - Nominatim单条查询无法直接获取全国城市列表:Nominatim是地理编码工具,不是行政区划数据集,直接查国家无法返回所有城市。
正确实现方案
要获取哥伦比亚城市列表并入库,我们可以通过Nominatim的搜索接口批量获取城市数据,再处理后存入数据库,以下是完整步骤:
1. 基础配置与错误处理
首先确保请求合法(Nominatim要求设置合理的user_agent,且避免高频请求),并处理geocode返回None的情况:
import time import sqlite3 from geopy.geocoders import Nominatim from geopy.exc import GeocoderTimedOut, GeocoderServiceError # 初始化地理编码器,替换成你的应用标识 geolocator = Nominatim(user_agent="colombia-city-scraper") # 数据库连接(SQLite示例,可替换为MySQL/PostgreSQL) conn = sqlite3.connect('colombia_cities.db') cursor = conn.cursor() # 创建城市表 cursor.execute(''' CREATE TABLE IF NOT EXISTS cities ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, latitude REAL, longitude REAL, country TEXT DEFAULT 'Colombia' ) ''') conn.commit()
2. 批量获取哥伦比亚城市数据
通过Nominatim的搜索接口,指定查询关键词和国家过滤,分页获取结果(注意Nominatim有请求频率限制,需加延时):
def get_colombia_cities(page=1, limit=50): cities = [] try: # 搜索哥伦比亚的城市,结构化参数过滤 results = geolocator.geocode( query={'country': 'Colombia', 'featuretype': 'city'}, exactly_one=False, limit=limit, page=page ) if not results: return cities for result in results: # 提取城市名、经纬度 city_name = result.raw.get('display_name', '').split(',')[0].strip() lat = result.latitude lon = result.longitude cities.append((city_name, lat, lon)) return cities except (GeocoderTimedOut, GeocoderServiceError): # 超时或服务错误时重试一次 time.sleep(2) return get_colombia_cities(page, limit) # 循环获取多页结果(根据需求调整页数) page_num = 1 while True: print(f"获取第 {page_num} 页数据...") city_data = get_colombia_cities(page=page_num) if not city_data: break # 批量插入数据库 cursor.executemany(''' INSERT OR IGNORE INTO cities (name, latitude, longitude) VALUES (?, ?, ?) ''', city_data) conn.commit() page_num += 1 # 遵守Nominatim的请求限制,每次请求后延时 time.sleep(1)
3. 验证与关闭资源
# 查询数据库中的城市数量 cursor.execute('SELECT COUNT(*) FROM cities') print(f"共存入 {cursor.fetchone()[0]} 个哥伦比亚城市") # 关闭连接 conn.close()
注意事项
- Nominatim使用限制:Nominatim禁止大规模爬虫,若需要完整的哥伦比亚城市列表,建议使用专门的行政区划数据集(比如GeoNames),再用geopy补充经纬度信息。
- 数据去重:使用
INSERT OR IGNORE避免重复入库,确保城市名唯一性。 - 请求频率:必须添加延时(至少1秒),否则会被Nominatim封禁IP。
内容的提问来源于stack exchange,提问作者MR Santy
相关产品推荐
相关产品推荐

