调用Teleport API遇正则类型错误,如何正确提取首个geonameid
报错根因说明
你触发TypeError: expected string or bytes-like object的核心原因非常直接:
re.search()方法要求第二个入参必须是字符串或字节类对象,但你的代码里调用这个方法时,传入的api变量是json.loads()解析完成的Python嵌套字典对象,类型完全不匹配- 哪怕注释掉后续的强制类型转换行也没用,因为报错发生在
re.search()调用的瞬间,和后面的转换逻辑没有任何关联
原有实现的逻辑偏差
你想从API返回结果里提取geonameid的思路存在不必要的冗余:
- 既然已经把接口返回的JSON文本解析成了结构化字典,完全不需要用正则去逆向匹配文本内容,直接按字典的固定层级取值可靠性更高
- 针对多结果返回的城市重名场景,Teleport搜索接口的返回结果是按匹配度从高到低排序的,直接取列表首个元素的逻辑本身符合业务需求,不需要额外调整匹配优先级
正确实现步骤
按照接口返回的固定嵌套结构,按层级取值即可,不需要引入正则做匹配:
- 解析接口返回的字典后,先定位到
_embedded键,再取对应的city:search-results列表(所有匹配城市的集合) - 取列表索引为0的元素,就是匹配度最高的首个城市结果
- 顺着该结果的层级依次取值:结果对象内的
_links→city:item→href字段,拿到格式为https://api.teleport.org/api/cities/geonameid:5809844/的详情链接 - 对链接字符串做简单分割,提取
geonameid:后的数字部分,去掉末尾的斜杠,就能得到最终的城市ID
修正后的完整代码
import urllib.request import json area = input("What's the city you'd like to see? ") area = area.lower().replace(' ', '-') if area == 'new-york-city': area = 'new-york' with urllib.request.urlopen(f'https://api.teleport.org/api/cities/?search={area}') as url: api_resp = json.loads(url.read().decode()) search_results = api_resp['_embedded']['city:search-results'] # 增加空结果兜底判断 if not search_results: print("未匹配到对应城市,请调整输入关键词重试") else: # 取首个匹配城市 first_city = search_results[0] city_detail_url = first_city['_links']['city:item']['href'] # 拆分提取geonameid geonameid = city_detail_url.split('geonameid:')[1].rstrip('/') print(f"首个匹配城市的geonameid为:{geonameid}")
补充说明
- 这种结构化取值的方式比正则匹配更稳定:后续如果API调整返回的文本格式(比如修改URL的拼接规则、在JSON文本里加入额外的注释类内容),正则匹配很容易失效,而按字典键名取值的逻辑只要和API文档约定的结构一致,就不会出问题
- 如果后续需要支持用户从多个重名结果里手动选择城市,只需要把
search_results列表遍历打印出来让用户选索引值即可,不需要额外改动取值逻辑
内容的提问来源于stack exchange,提问作者ESarousi
相关产品推荐
相关产品推荐

