Долго выбирал между «Алгоритмами», «Читальным залом» и «Я пиарюсь», в итоге остановился на Data Mining.
Эта история началась в конце октября, когда я очередной раз пытался выбрать, что бы мне почитать. Лично я с собой в отпуск/в дорогу беру что-нибудь из фантастики (как, думаю, и большинство присутствующих), причем категорически не люблю всякий модный новодел.
И вот, терзаясь муками выбора, я забил в поиск «IMDB for books» и… не нашел ничего пристойного. Весь интернет забит рекомендательными сервисами для книг, и все они выдают полную чушь. Вот, например, топ имхонета для раздела «Самая лучшая фантастика и фэнтези»:
Эээ… Это совсем не то, что я ожидал увидеть на первых местах в рейтинге фантастики. «Мы пойдём другим путём», — подумал я. Отказавшись от идеи найти нормальный читательский рейтинг, я просто пошёл в Вики, нашёл список лауреатов премий Хьюго и Небьюла и выбрал пару-тройку книг — как, собственно, я всегда раньше и делал.
«А не замутить ли мне свой рейтинг книг, взяв за основу престижные премии?» — внезапно подумал я. И замутил. Знакомьтесь: top-books.info
Итак, мне потребовалось сделать следующее:
1. Найти и распарсить логи номинантов и победителей премий;
2. Сформировать из них списки книг и авторов;
3. Присвоить каждой книге рейтинг;
4. Найти и приклеить к каждой книге картинку и описание;
5. Найти и приклеить к каждому автору краткую биографическую справку;
6. Сделать по всему этому поиск;
7. Прикрутить голосовалку.
А теперь подробнее…
Я решил ограничиться тремя премиями: Hugo, Nebula и Locus. Все остальные либо узкоспециализированные, либо даются недавно.
Списки победителей и номинантов на Hugo и Nebula я взял из Вики:
en.wikipedia.org/wiki/Hugo_Award_for_Best_Novel
en.wikipedia.org/wiki/Nebula_Award_for_Best_Novel
С Locus оказалос�� сложнее. Списки номинантов пришлось собирать по годам:
www.locusmag.com/SFAwards/Db/Locus.html
К тому же в этих списках огромное количество номинантов, штук по 20, большая часть из которых мне абсолютно ничего не говорила. Так что я ограничился первой пятеркой номинантов из категории «Best Novel» (выдавались в 1971-1981 гг) и категорий «SF Novel», «Fantasy Novel» (с 1982 по 2011).
Разбирал я всё это дело скриптами, написанными на лучшем в мире языке — JavaSсript-е :). Hugo и Nebula разобрались легко (в Википедии всё-таки единого стиля оформления придерживаются), с Locus пришлось немного помучиться. Вот так примерно выглядел разбор логов Locus-а:
В итоге я получил примерно вот такой список авторов:
И вот такой список книг:
У номинантов на Locus ещё есть поле place — занятое место. Hugo и Nebula ранжирования для номинантов не дают.
Я перепробовал несколько вариантов, и в итоге остановился на вот такой формуле:
rating = 6 + 3 * (sum(s[i])) / possibleAwards + yearTotal / 100
Здесь possibleAwards — число наград, которые могла теоретически получить книга (= число премий, выдававшихся в год публикации книги), yearTotal — общее количество номинантов премий в год публикации книги, s[i] — набранный книгой балл по каждой премии.
s[i] считалось так: 1, если книга выигрывала премию; 1/число номинантов, если книга была номинирована на Hugo или Nebula, но не получала премию; (число номинантов — занятое место + 1)/число номинантов для претендентов на Locus.
Итого, каждая книга получала 6 баллов просто так, по факту попадания в шорт-лист какой-нибудь премии; от 0 до 3 баллов в зависимости от полученных премий (итого от 6 до 9); плюс небольшую поправку в виде общего числа номинантов в тот год / 100, для того, чтобы (а) немного пессимизировать книги, получавшие премии в самом начале, когда списков номинантов ещё не было; (б) из того соображения, что, если в какой-то год было много номинантов, то год в целом был удачнее предыдущих.
Например, возьмём «Проклятье Шалиона»:
Книга набирает 0.16(6) балла за номинацию на Хьюго (1 из 6 номинантов) + 0.6 балла за Локус (3-е место из 5) + 0.11 за общее число претендентов (6 + 5) на премии, в которых книга участвовала. Итого: 6.9.
В итоге, топ-10 приобрел следующий вид:
Из десятки лично я, правда, читал только «Паладин душ», «Дюну» и «Сами боги», но их нахождение в топ-10 представлялось мне вполне адекватным.
С рейтингом авторов пришлось помучиться. Мне хотелось, чтобы автор с большим количеством хороших книг был в топе выше автора с одной, но очень хорошей. Я перебрал много формул, и остановился на такой:
rating = (sum + 3)/(n + 1)
Здесь sum — сумма рейтингов книг автора, n — количество книг. Легко заметить, что эта формула фактически эквивалентна тому, что каждому автору засчитывается фиктивная книга с рейтингом 3, что и позволяет пессимизировать авторов с малым количеством книг. Топ-10 в итоге получился таким:
1 Heinlein, Robert A.
2 Le Guin, Ursula K.
3 Asimov, Isaac
4 Card, Orson Scott
5 Bujold, Lois McMaster
6 Willis, Connie
7 Brin, David
8 Haldeman, Joe
9 Clarke, Arthur C.
10 Pohl, Frederik
Вот этот топ меня полностью удовлетворил :)
Информацию о книгах я набрал из Amazon Product Advertising API — в рамках партнерской программы Амазон разрешает использовать информацию о продаваемых изданиях. Меня интересовали картинки и описания. В целом, схема работы была такая:
1. Выбираем книгу
2. Делаем запрос по заголовку книги с фильтром по одному из авторов
3. Ищем в ответе item-ы с тем же заголовком и автором
4. Записываем уникальный идентификатор (ASIN) и reviews.
5. Если чего-то не нашли, пробуем искать по другому заголовку (если у книги их несколько) либо в другом индексе.
Я искал сначала в индексе Kindle Store (я за прогресс и всё такое :)), а потом по бумажным книгам. В итоге, из 580 книг 378 удалось найти в Kindle Store.
Ищет Amazon PAAPI довольно адекватно, хотя на первые места могут проскакивать какие-то левые ответы. Единственное, что API полностью игнорирует диакритические знаки и не находит таких авторов, как Miéville и такие заголовки, как Tales of Nevèrÿon — их, в итоге, пришлось искать руками.
Авторов пришлось выцеплять из Википедии посредством Wikimedia API. Велосипед, честно говоря, тот ещё. В итоге, 90% запросов по авторам нормально отработало просто по имени и фамилии, но те 10%, у которых распространенные имена, пришлось потом перезабивать руками. Если же к поисковому запросу помимо имени дописать что-нибудь типа «author» или «fantasy writer» — то начнут нормально работать 10% неуникальных имен, но остальные 90% сломаются напрочь.
В итоге, для каждого автора я вытащил из википедии преамбулу к статье. Дорогие редакторы википедии, вам очень не помешает guideline для преамбул. Многие статьи сурово отдают капитанством (David Brin, например), в других в преамбуле целое сочинение написано (Isaac Asimov).
Ну, тут особо выбора не было — Google Custom Search Engine. Пришлось немного пошаманить CSS-ом, чтобы расположить его там, где я хотел, но вроде работает.
Кстати, у Google CSE обратная к Амазону проблема — по Mieville отказывается искать, нужно забивать Miéville.
Держать у себя авторизацию и комменты сильно не хотелось, так что решил воспользоваться фейсбучегом.
Господа разработчики 2gis API и Leaflet API! Простите меня! Ваши API — сказка по сравнению с FB. Такого плохо организованного и отвратительно документированного API я ещё не встречал. Почти неделя мучений потребовалось мне, чтобы прикрутить эту байду.
Господа разработчики Фейсбука! Наведите порядок в документации! Невозможно работать совершенно.
В изначальных планах стояло так же создание и русскоязычной версии, но, как оказалось, никакого русскоязычного контента вытянуть я не могу. У Озона своего API нет, русская Вики и половины авторов не знает. Так что в этом месте полный фейл.
Да ничего, особо. Господа любители фантастики — наслаждайтесь. Рейтинг, по моим ощущениям, более чем адекватен. (В порядке эксперимента я прочитал №1 в списке — "Американские боги" Нила Геймана. Очень крутая книжка, доложу я вам.) Если какие-то оценки кажутся вам неправильными — welcome, голосуйте. Только учтите, что у начальных оценок выставлен вес в 1000 голосов, так что перебить их, кхм, непросто. Лично я первым делом выставил десятки сильно недооценным, на мой взгляд, "Проклятью Шалиона", "Ночи в тоскливом октябре" и "Другому ветру".
Сразу предупреждаю, что у литературных премий в чести серьёзное чтиво, поэтому развлекательная фантастика представлена в рейтинге очень слабо. То же, к сожалению, относится и к пионерам фантастического жанра — в рейтинге широко представлена литература, начиная с 60-х годов, более ранняя — с перебоями. (Кстати, волевым волюнтаристским решением я добавил в рейтинг «Властелина колец» с рейтингом 9.0 и «Хоббита» с рейтингом 8.0, а то Толкиен с единственным «Сильмариллионом» выглядел странновато.)
Новых поступлений в рейтинг (включая отечественную фантастику) нет и не будет, пока не найдётся способ более-менее надёжно дать им начальный рейтинг. Если это кому-то интересно (и мне будет не лень) могу дополнительно вкрутить и рейтинг классических романов по тому же принципу.
В общем, enjoy!
UPD Хабрэффект, хабрэффект… 3% CPU, 8% Memory.
Эта история началась в конце октября, когда я очередной раз пытался выбрать, что бы мне почитать. Лично я с собой в отпуск/в дорогу беру что-нибудь из фантастики (как, думаю, и большинство присутствующих), причем категорически не люблю всякий модный новодел.
И вот, терзаясь муками выбора, я забил в поиск «IMDB for books» и… не нашел ничего пристойного. Весь интернет забит рекомендательными сервисами для книг, и все они выдают полную чушь. Вот, например, топ имхонета для раздела «Самая лучшая фантастика и фэнтези»:
1. Мастер и Маргарита. Михаил Булгаков, 1940 год
2. ��веты для Элджернона (рассказ). Дэниел Киз, 1959 год
3. Цветы для Элджернона. Дэниел Киз, 1966 год
4. Битва Королей. Джордж Мартин, 1998 год
5. Рыцарь Ордена: Клинки у трона. Сергей Садов, 2000 год
6. Голубятня в Орехове. Владислав Крапивин, 1983 год
Эээ… Это совсем не то, что я ожидал увидеть на первых местах в рейтинге фантастики. «Мы пойдём другим путём», — подумал я. Отказавшись от идеи найти нормальный читательский рейтинг, я просто пошёл в Вики, нашёл список лауреатов премий Хьюго и Небьюла и выбрал пару-тройку книг — как, собственно, я всегда раньше и делал.
«А не замутить ли мне свой рейтинг книг, взяв за основу престижные премии?» — внезапно подумал я. И замутил. Знакомьтесь: top-books.info
Итак, мне потребовалось сделать следующее:
1. Найти и распарсить логи номинантов и победителей премий;
2. Сформировать из них списки книг и авторов;
3. Присвоить каждой книге рейтинг;
4. Найти и приклеить к каждой книге картинку и описание;
5. Найти и приклеить к каждому автору краткую биографическую справку;
6. Сделать по всему этому поиск;
7. Прикрутить голосовалку.
А теперь подробнее…
Логи премий
Я решил ограничиться тремя премиями: Hugo, Nebula и Locus. Все остальные либо узкоспециализированные, либо даются недавно.
Списки победителей и номинантов на Hugo и Nebula я взял из Вики:
en.wikipedia.org/wiki/Hugo_Award_for_Best_Novel
en.wikipedia.org/wiki/Nebula_Award_for_Best_Novel
С Locus оказалос�� сложнее. Списки номинантов пришлось собирать по годам:
www.locusmag.com/SFAwards/Db/Locus.html
К тому же в этих списках огромное количество номинантов, штук по 20, большая часть из которых мне абсолютно ничего не говорила. Так что я ограничился первой пятеркой номинантов из категории «Best Novel» (выдавались в 1971-1981 гг) и категорий «SF Novel», «Fantasy Novel» (с 1982 по 2011).
Книги и авторы
Разбирал я всё это дело скриптами, написанными на лучшем в мире языке — JavaSсript-е :). Hugo и Nebula разобрались легко (в Википедии всё-таки единого стиля оформления придерживаются), с Locus пришлось немного помучиться. Вот так примерно выглядел разбор логов Locus-а:
parseBook = function (s) {
var alternates = /\([^\)]+title ([^\)]+)\)/.exec(s);
if (alternates) {
var alternateTitle = trim(alternates[1]).replace(/^"/, '').replace(/"$/, '');
s = s.replace(alternates[0], '');
}
s = s.replace(/ \(.+\)$/, '');
var parts = s.split(', '),
delimeter = parts.length - 1;
if (delimeter > 1 && parts[delimeter].indexOf('Jr') == 0) {
delimeter--;
}
var title = trim(parts.slice(0, delimeter).join(', ')).replace(/^"/, '').replace(/"$/, ''),
author = trim(parts.slice(delimeter).join(', '));
if (author.indexOf(' & ') != -1) {
author = author.split(' & ');
}
return {
title: alternateTitle ? [title, alternateTitle] : title,
author: author
}
}
В итоге я получил примерно вот такой список авторов:
"a-e-van-vogt": {
"fullName": "Vogt, A. E. van",
"alias": "a-e-van-vogt",
"firstName": "A.",
"middleName": "E.",
"lastName": "Vogt",
"preposition": "van"
},
"kurt-vonnegut": {
"fullName": "Vonnegut, Kurt",
"alias": "kurt-vonnegut",
"firstName": "Kurt",
"middleName": "",
"lastName": "Vonnegut",
"preposition": ""
},
И вот такой список книг:
"the-boy-who-bought-old-earth": {
"see": "the-planet-buyer"
},
"dune": {
"alias": "dune",
"title": "Dune",
"awards": {
"1965": [
{
"award": "nebula",
"won": true
}
],
"1966": [
{
"award": "hugo",
"won": true
}
]
},
"authorAlias": "frank-herbert"
},
"and-call-me-conrad": {
"alias": "and-call-me-conrad",
"title": [
"...And Call Me Conrad",
"This Immortal"
],
"awards": {
"1966": [
{
"award": "hugo",
"won": true
}
]
},
"authorAlias": "roger-zelazny"
},
"this-immortal": {
"see": "and-call-me-conrad"
},
У номинантов на Locus ещё есть поле place — занятое место. Hugo и Nebula ранжирования для номинантов не дают.
Рейтинги
Я перепробовал несколько вариантов, и в итоге остановился на вот такой формуле:
rating = 6 + 3 * (sum(s[i])) / possibleAwards + yearTotal / 100
Здесь possibleAwards — число наград, которые могла теоретически получить книга (= число премий, выдававшихся в год публикации книги), yearTotal — общее количество номинантов премий в год публикации книги, s[i] — набранный книгой балл по каждой премии.
s[i] считалось так: 1, если книга выигрывала премию; 1/число номинантов, если книга была номинирована на Hugo или Nebula, но не получала премию; (число номинантов — занятое место + 1)/число номинантов для претендентов на Locus.
Итого, каждая книга получала 6 баллов просто так, по факту попадания в шорт-лист какой-нибудь премии; от 0 до 3 баллов в зависимости от полученных премий (итого от 6 до 9); плюс небольшую поправку в виде общего числа номинантов в тот год / 100, для того, чтобы (а) немного пессимизировать книги, получавшие премии в самом начале, когда списков номинантов ещё не было; (б) из того соображения, что, если в какой-то год было много номинантов, то год в целом был удачнее предыдущих.
Например, возьмём «Проклятье Шалиона»:
"the-curse-of-chalion": {
"alias": "the-curse-of-chalion",
"title": "The Curse of Chalion",
"awards": {
"2002": [
{
"award": "hugo",
"won": false
},
{
"award": "locus",
"won": false,
"category": "fantasy novel",
"place": 3
}
]
},
"authorAlias": "lois-mcmaster-bujold"
}
Книга набирает 0.16(6) балла за номинацию на Хьюго (1 из 6 номинантов) + 0.6 балла за Локус (3-е место из 5) + 0.11 за общее число претендентов (6 + 5) на премии, в которых книга участвовала. Итого: 6.9.
В итоге, топ-10 приобрел следующий вид:
9.2 American Gods / Gaiman, Neil
9.2 Paladin of Souls / Bujold, Lois McMaster
9.1 The Forever War / Haldeman, Joe
9.1 The Gods Themselves / Asimov, Isaac
9.1 Dune / Herbert, Frank
9.1 Ringworld / Niven, Larry
9.1 Startide Rising / Brin, David
9.1 Speaker for the Dead / Card, Orson Scott
9.1 Doomsday Book / Willis, Connie
9.1 The Yiddish Policemen's Union / Chabon, Michael
Из десятки лично я, правда, читал только «Паладин душ», «Дюну» и «Сами боги», но их нахождение в топ-10 представлялось мне вполне адекватным.
Рейтинг авторов
С рейтингом авторов пришлось помучиться. Мне хотелось, чтобы автор с большим количеством хороших книг был в топе выше автора с одной, но очень хорошей. Я перебрал много формул, и остановился на такой:
rating = (sum + 3)/(n + 1)
Здесь sum — сумма рейтингов книг автора, n — количество книг. Легко заметить, что эта формула фактически эквивалентна тому, что каждому автору засчитывается фиктивная книга с рейтингом 3, что и позволяет пессимизировать авторов с малым количеством книг. Топ-10 в итоге получился таким:
1 Heinlein, Robert A.
2 Le Guin, Ursula K.
3 Asimov, Isaac
4 Card, Orson Scott
5 Bujold, Lois McMaster
6 Willis, Connie
7 Brin, David
8 Haldeman, Joe
9 Clarke, Arthur C.
10 Pohl, Frederik
Вот этот топ меня полностью удовлетворил :)
Майним данные о книгах
Информацию о книгах я набрал из Amazon Product Advertising API — в рамках партнерской программы Амазон разрешает использовать информацию о продаваемых изданиях. Меня интересовали картинки и описания. В целом, схема работы была такая:
1. Выбираем книгу
2. Делаем запрос по заголовку книги с фильтром по одному из авторов
3. Ищем в ответе item-ы с тем же заголовком и автором
4. Записываем уникальный идентификатор (ASIN) и reviews.
5. Если чего-то не нашли, пробуем искать по другому заголовку (если у книги их несколько) либо в другом индексе.
Я искал сначала в индексе Kindle Store (я за прогресс и всё такое :)), а потом по бумажным книгам. В итоге, из 580 книг 378 удалось найти в Kindle Store.
Ищет Amazon PAAPI довольно адекватно, хотя на первые места могут проскакивать какие-то левые ответы. Единственное, что API полностью игнорирует диакритические знаки и не находит таких авторов, как Miéville и такие заголовки, как Tales of Nevèrÿon — их, в итоге, пришлось искать руками.
Майним данные об авторах
Авторов пришлось выцеплять из Википедии посредством Wikimedia API. Велосипед, честно говоря, тот ещё. В итоге, 90% запросов по авторам нормально отработало просто по имени и фамилии, но те 10%, у которых распространенные имена, пришлось потом перезабивать руками. Если же к поисковому запросу помимо имени дописать что-нибудь типа «author» или «fantasy writer» — то начнут нормально работать 10% неуникальных имен, но остальные 90% сломаются напрочь.
В итоге, для каждого автора я вытащил из википедии преамбулу к статье. Дорогие редакторы википедии, вам очень не помешает guideline для преамбул. Многие статьи сурово отдают капитанством (David Brin, например), в других в преамбуле целое сочинение написано (Isaac Asimov).
Поиск
Ну, тут особо выбора не было — Google Custom Search Engine. Пришлось немного пошаманить CSS-ом, чтобы расположить его там, где я хотел, но вроде работает.
Кстати, у Google CSE обратная к Амазону проблема — по Mieville отказывается искать, нужно забивать Miéville.
Голосовалка
Держать у себя авторизацию и комменты сильно не хотелось, так что решил воспользоваться фейсбучегом.
Господа разработчики 2gis API и Leaflet API! Простите меня! Ваши API — сказка по сравнению с FB. Такого плохо организованного и отвратительно документированного API я ещё не встречал. Почти неделя мучений потребовалось мне, чтобы прикрутить эту байду.
Господа разработчики Фейсбука! Наведите порядок в документации! Невозможно работать совершенно.
Русская версия
В изначальных планах стояло так же создание и русскоязычной версии, но, как оказалось, никакого русскоязычного контента вытянуть я не могу. У Озона своего API нет, русская Вики и половины авторов не знает. Так что в этом месте полный фейл.
И что дальше?
Да ничего, особо. Господа любители фантастики — наслаждайтесь. Рейтинг, по моим ощущениям, более чем адекватен. (В порядке эксперимента я прочитал №1 в списке — "Американские боги" Нила Геймана. Очень крутая книжка, доложу я вам.) Если какие-то оценки кажутся вам неправильными — welcome, голосуйте. Только учтите, что у начальных оценок выставлен вес в 1000 голосов, так что перебить их, кхм, непросто. Лично я первым делом выставил десятки сильно недооценным, на мой взгляд, "Проклятью Шалиона", "Ночи в тоскливом октябре" и "Другому ветру".
Сразу предупреждаю, что у литературных премий в чести серьёзное чтиво, поэтому развлекательная фантастика представлена в рейтинге очень слабо. То же, к сожалению, относится и к пионерам фантастического жанра — в рейтинге широко представлена литература, начиная с 60-х годов, более ранняя — с перебоями. (Кстати, волевым волюнтаристским решением я добавил в рейтинг «Властелина колец» с рейтингом 9.0 и «Хоббита» с рейтингом 8.0, а то Толкиен с единственным «Сильмариллионом» выглядел странновато.)
Новых поступлений в рейтинг (включая отечественную фантастику) нет и не будет, пока не найдётся способ более-менее надёжно дать им начальный рейтинг. Если это кому-то интересно (и мне будет не лень) могу дополнительно вкрутить и рейтинг классических романов по тому же принципу.
В общем, enjoy!
UPD Хабрэффект, хабрэффект… 3% CPU, 8% Memory.
