Викиданные — получение меток для большого количества идентификаторов

#python #wikidata #ndjson

#python #викиданные #ndjson

Вопрос:

У меня есть список из примерно 300 000 идентификаторов викиданных (например, Q1347065, Q731635 и т. Д.) В файле ndjson в виде

 {"Q1347065": ""}
{"Q731635": ""}
{"Q191789": ""} ... etc
 

Я хотел бы получить метку каждого идентификатора и сформировать словарь ключевых значений, таких как

{"Q1347065":"epiglottitis", "Q731635":"Mount Vernon", ...} и т.д.

То, что я использовал до того, как список идентификаторов стал таким большим, была библиотека python викиданных (https://pypi.org/project/Wikidata /)

 from wikidata.client import Client
import ndjson

client = Client()
with open("claims.ndjson") as f, open('claims_to_strings.json', 'w') as out:
    claims = ndjson.load(f)

    l = {} 
    for d in claims: 
        l.update(d)

    for key in l:
        v = client.get(key)
        l[key] = str(v.label)

    json.dumps(l, out)
 

Но это слишком медленно (около 15 часов для 1000 идентификаторов). Есть ли другой способ добиться этого быстрее, чем то, что я делал?

Ответ №1:

Прежде чем ответить: я не знаю, что вы имеете в json.dumps(r, out) виду; Я предполагаю, что вы хотите json.dump(l, out) вместо этого.

Мой ответ заключается в использовании следующего запроса SPARQL к службе запросов викиданных:

 SELECT ?item ?itemLabel WHERE {
  VALUES ?item { wd:Q1347065 wd:Q731635 wd:Q105492052 }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
 

для одновременного запроса нескольких меток.

Это значительно ускоряет время выполнения, потому что узким местом является количество подключений, и с помощью этого метода сопоставление id -> label полностью выполняется на стороне сервера.

 import json
import ndjson
import re
import requests

def wikidata_query(query):
    url = 'https://query.wikidata.org/sparql'
    try:
        r = requests.get(url, params = {'format': 'json', 'query': query})
        return r.json()['results']['bindings']
    except json.JSONDecodeError as e:
        raise Exception('Invalid query')

with open("claims.ndjson") as f, open('claims_to_strings.json', 'w') as out:
    claims = ndjson.load(f)

    l = {} 
    for d in claims: 
        l.update(d)
    
    item_ids = l.keys()
    sparql_values = list(map(lambda id: "wd:"   id, item_ids))
    item2label = wikidata_query('''
        SELECT ?item ?itemLabel WHERE {
        VALUES ?item { %s }
        SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
    }''' % " ".join(sparql_values))

    for result in item2label :
        item = re.sub(r".*[#/\]", "", result['item']['value'])
        label = result['itemLabel']['value']
        l[item] = label
    
    json.dump(l, out)
 

Я думаю, вы не можете выполнить один запрос для всех 300 000 элементов, но вы можете легко найти максимальное поддерживаемое количество принятых идентификаторов и разделить свой исходный список идентификаторов в соответствии с этим числом.

Комментарии:

1. Да, я имел в виду l вместо r, спасибо, что поняли это, и спасибо за предложение! Я попробую это сейчас!

2. @Paschalis Хорошо, но проверьте также разницу между dumps dump методами и!