Получаем данные из Википедии через SPARQL

Feb 20, 2017 23:19 · 543 words · 3 minute read sparql tutorial

В своей игре GeoPuzzle мне понадобились некоторые данные о странах: флаги, столицы, население… Откуда бы их взять? Наверно, с Википедии :) Задача, вроде, достаточно распространённая, так что спросим Google как это делается, авось и библиотека найдётся. Сразу бросается в глаза то, что настоятельно не рекомендуют парсить инфобоксы. Это верно - я сходу нашёл 3 разных их шаблона на 10 страницах. В одном из ответов на StackOverflow настоятельно рекомендовали использовать SPARQL. Так я узнал о том, что есть некий язык запросов для Википедии (на самом деле это реализовано в качестве плагина для MediaWiki). “Отлично, немного кода и задача будет решена”, - каким же наивным я был!

Знакомимся со SPARQL

SELECT DISTINCT ?country ?capital ?row
WHERE
{
?country wdt:P31 wd:Q3624078 .
FILTER NOT EXISTS {?country wdt:P31 wd:Q3024240}
OPTIONAL { ?country wdt:P36/rdfs:label ?capital } .

BIND(lang(?capital) as ?row)
filter (?row = 'ru' || ?row = 'en')
}
ORDER BY ?capital

вот примерно так выглядит запрос на получение всех современных государств и их столиц. Проверить можно здесь. Да-да, теперь вы понимаете почему это заняло у меня не одну бессонную ночь и километры сожженных нервов. Спасибо за ещё один стандарт, как говорится… Ладно, на самом деле здесь показаны почти все языковые конструкции, которые мне понадобились.

Понимаем SPARQL

Итак, с SELECT, DISTINCT, WHERE и ORDER BY вроде бы всё понятно. Замечу только что поля в SELECT перечисляются без запятой. Далее - самое важное - ?country wdt:P31 wd:Q3624078 это на самом деле запись Субъект Предикат Объект, то есть выбирается сопоставление “нечто ?country, у которого свойство wdt:P31 равно объекту wd:Q3624078”. Q3624078 и P31 явно какие-то уникальные идентификаторы, а что ж такое wdt и wd? Это префиксы - namespace для идентификаторов. Ещё немного примеров. Ладно, если wdt это описатель какого-то свойства, то как понять что за свойство такое P31? Список, конечно, можно найти здесь, но мне было проще смотреть прям в объекте. Таким образом, после выполнения строчки ?country wdt:P31 wd:Q3624078 в ?country будет список суверенных государств (свойство wdt:P31 “экземпляр” == значению wd:Q3624078 “суверенное государство”). Самое время пойти сделать чай, покурить или вытащить пельмени.

Переходим ко второй строчке в WHERE. Тут всё проще - исключаем те страны, которые уже не существуют (значение wd:Q3024240). Далее опционально задаём столицу. Почему опционально? Нууу, её может не быть :) Шутка - в данном случа нам повезло, и она есть у каждой записи, но вот допустим площадь указана не у каждой страны. Так что если записать без OPTIONS

?country wdt:P31 wd:Q3624078 .
?country wdt:P36/rdfs:label ?capital .

то получается, что мы хотим найти все существующие страны, у которых есть свойство wdt:P36, которое нужно поместить в переменную ?capital. То есть страны без столиц будут игнорироваться, потому что невозможно заполнить ?capital. Да, кстати, запись через слеш wdt:P36/rdfs:label означает вложенное свойство. Нас же интересует строка, а не код объекта столицы. Ещё забыл сказать, что комментирование строки - #.

Иногда бывает нужно вывести значение какого-нибудь выражения. В нашем примере это функция lang(?capital), которая возвращает код языка, на котором написано название столицы. С помощью оператора BIND можно записать вычисления в какую-нибудь переменную, а потом использовать это в фильтрах и выводе.

Любим SPARQL

Вот и пригодилось мне понимание Пролога, не зря нам его в универе преподавали. Надеюсь, что принёс хоть немного понимания. Всё не так уж и сложно как на первый взгляд. Зато теперь вы сможете получать информацию прямо из первых рук цивилизованным способом. :)