如何用Pywikibot批量匹配Geonames ID与Wikidata ID?
批量通过Geonames ID查询对应Wikidata ID(Pywikibot实现)
问题背景
已有大量Geonames ID列表,需要批量查询对应的Wikidata ID,当前仅能实现单个ID的SPARQL查询,希望将查询中的固定ID替换为变量,遍历列表完成批量查询。
单个查询的SPARQL语句:
SELECT DISTINCT ?item ?itemLabel WHERE { SERVICE wikibase:label { bd:serviceParam wikibase:language "de". } { SELECT DISTINCT ?item WHERE { ?item p:P1566 ?statement0. ?statement0 (ps:P1566) "2867714". } } }
单个查询的Pywikibot脚本可正常返回结果,但需扩展为批量处理。
解决方案
方法一:逐ID替换查询模板(适合小批量)
通过定义SPARQL模板,将固定ID替换为占位符,遍历ID列表时动态生成查询语句:
import pywikibot from pywikibot import pagegenerators as pg # 定义SPARQL模板,用占位符{geonames_id}代替固定ID SPARQL_TEMPLATE = """ SELECT DISTINCT ?item ?itemLabel WHERE { SERVICE wikibase:label { bd:serviceParam wikibase:language "de". } { SELECT DISTINCT ?item WHERE { ?item p:P1566 ?statement0. ?statement0 (ps:P1566) "{geonames_id}". } } } """ # 你的Geonames ID列表,可从文件读取(示例:慕尼黑、柏林、维也纳的ID) geonames_ids = ["2867714", "2950159", "3067696"] wikidata_site = pywikibot.Site("wikidata", "wikidata") # 遍历每个ID执行查询 for gid in geonames_ids: query = SPARQL_TEMPLATE.format(geonames_id=gid) generator = pg.WikidataSPARQLPageGenerator(query, site=wikidata_site) for item in generator: print(f"Geonames ID {gid} → Wikidata ID: {item}")
方法二:SPARQL VALUES批量查询(适合大批量,效率更高)
利用SPARQL的VALUES子句一次性传入所有ID,减少请求次数,提升效率:
import pywikibot from pywikibot import pagegenerators as pg # 批量查询的SPARQL模板 BATCH_SPARQL_TEMPLATE = """ SELECT DISTINCT ?geonamesId ?item ?itemLabel WHERE { SERVICE wikibase:label { bd:serviceParam wikibase:language "de". } VALUES ?geonamesId { {geonames_id_list} } ?item p:P1566 ?statement0. ?statement0 ps:P1566 ?geonamesId. } """ # Geonames ID列表 geonames_ids = ["2867714", "2950159", "3067696"] # 将ID转换为SPARQL VALUES要求的格式:"id1" "id2" "id3" formatted_ids = ' '.join([f'"{gid}"' for gid in geonames_ids]) query = BATCH_SPARQL_TEMPLATE.format(geonames_id_list=formatted_ids) wikidata_site = pywikibot.Site("wikidata", "wikidata") generator = pg.WikidataSPARQLPageGenerator(query, site=wikidata_site) # 解析查询结果 for result in generator: # 提取绑定的变量值 bindings = result.get('binding', []) gid = next(b['value'] for b in bindings if b['name'] == 'geonamesId') wikidata_id = f"wikidata:{next(b['value'].split('/')[-1] for b in bindings if b['name'] == 'item')}" print(f"Geonames ID {gid} → Wikidata ID: {wikidata_id}")
注意事项
- 若ID数量极大(上千级),建议分批次处理,避免SPARQL语句过长被服务器拒绝
- 从文件读取ID可使用以下代码:
with open('geonames_ids.txt', 'r', encoding='utf-8') as f: geonames_ids = [line.strip() for line in f if line.strip()]
内容的提问来源于stack exchange,提问作者OnceUponATime
相关产品推荐
相关产品推荐

