Блог Игоря Шевченко

Cейчас мы будем скрейпить весь интернет питоном 15 апреля 2013

Почти весь технический контент русскоязычного интернета публикуется либо в маленьких блогах, где его почти никто не видит, либо пишется специально для «Хабрахабра», который посещают уже миллионы человек в день. В англоязычном интернете работает «средний» вариант. Существуют социальные новостные сайты вроде реддита с разделом /r/programming или Hacker News, куда одни пользователи отправляют ссылки на интересные материалы, а другие плюсуют, минусуют, обсуждают. Любой может опубликовать что-то у себя в блоге, а потом разместить ссылку на свой пост там, чтобы привлечь читателей и комментаторов.

Но при этом существует проблема с тем, чтобы пользоваться этим. Например, на Hacker News количество публикаций за день достигает почти что тысячи. Поэтому просто подписаться на RSS с новыми постами нельзя: они забьют всю ленту, а найти что-то хорошее среди них будет тяжело. Приходится придумывать сложные ритуалы. К примеру, на реддит я захожу стабильно по воскресеньям, открываю лучшее за неделю и делаю закладки на ссылках, чтобы посмотреть их на следующей неделе.

С тем самым Hacker News оказалось еще сложнее. Хотя контент там часто попадается интересный, непонятно, как за ним следить. Даже ритуала никакого не придумать. Постов много, списков лучшего за какой-то период нет. Только определяемое по неизвестной формуле лучшее на данный момент. Так что единственный вариант — постоянно вспоминать и заходить на сайт. Мне это не очень нравилось. Подумав и вдохновившись многочисленными статьями из серии «сейчас мы будем скрейпить весь интернет питоном», я решил написать штуку, которая поможет мне.

Этой штукой был скрейпер, который каким-то образом должен с HN некоторое количество лучших постов за вчерашний день и создавать RSS-поток, чтобы я без лишних действий мог получать его себе в ленту. При этом хотелось делать всё культурно, не напрягая, в первую очередь, сам сайт количеством запросов или чем-то еще. Уже не помню где, но видел, как Пол Грэм, создатель сайта, негативно отзывался о ботах, которые грузят не только первую страницу списка постов, но и следующие по ссылке «More», хотя в robots.txt написано так не делать. Я так делать не и стал, хотя это и исключало самый первый логичный вариант: заходить в раз день, загружать пару десятков страниц со списком постов за день, извлекать из них лучшие и публиковать. Можно было на странице с новыми постами только собирать список всех постов за день, а перед публикацией заходить непосредственно в эти посты и получать число голосов за них. Но в том же самом robots.txt установлено требование не делать запросы чаще, чем раз в 30 секунд. Так на загрузку всех постов за день может уйти часов восемь, а то и больше. Такой вариант тоже не подходит хотя бы потому, что за это время число голосов на первых загруженных страницах уже успеет измениться, появятся новые посты, в общем, результат получится неконсистентным.

Я сделал так: с некоторой периодичностью бот получает список постов, которые сейчас находятся на главной странице. Это тоже логично: много ссылок за день и не нужно, а топ-10, например, уж точно побывает на главной. Запросы я решил делать раз в полчаса, потому что, опять же, даже если какой-то пост успеет появиться на главной и снова пропасть в течение получаса, то он нас скорее всего интересовать и не будет.

Следующая часть веб-скрейпинга после загрузки — это разбор полученной страницы и выделение нужной информации. В данном случае нужная — это ссылка, заголовок, количество очков и идентификатор каждого поста. Несмотря на то, что сайт в духе старой школы свёрстан таблицами, трудностей с этим не было, если не считать пару внезапно обнаруженных фич. Например, оказалось, что на HN в списке могут быть рекламные ссылки, за которые голосовать нельзя, и очков у них нет. Другая неожиданность — если ссылка ведёт на pdf-файл, то этот файл загружается еще и на сервис scribd, а заголовок содержит уже две ссылки (исходную и на скрибд). Эту вещь я обнаружил далеко не сразу, и она меня заставила какое-то время поразбираться — а почему же ссылки, которые хранятся у меня для некоторых постов, не соответствуют ссылкам, которые в этих постах на самом деле.

Для парсинга я сначала использовал библиотеку Beautiful Soup. Потом вспомнил, что в обсуждении каждой из того самого множества статей про скрейпинг на питоне обязательно кто-то говорит, что lxml работает быстрее, чем Beautiful Soup (а еще всегда кто-то должен сказать: «А я использую для этого Scrapy»). Переделал на lxml, сравнил скорость: действительно, быстрее. В 90 раз. Поэтому оставил вариант на lxml, хотя временные затраты на парсинг в любом случае меньше, чем на скачивание страницы по сети.

Наконец, получив информацию обо всех постах, нужно выводить десять лучших за день. Здесь проблема была не в том, как выбрать лучшие, — есть же голоса пользователей, ничего придумывать для этого не надо. Вопрос в том, какие считать сегодняшними. Во-первых, дата и время публикации на сайте выводятся в неудобном формате: «5 часов назад», «1 день назад», а в другом виде их никак не получить (обычно делают, чтобы хотя бы при наведении курсора мыши всплывала полная дата). Во-вторых, даже если бы я мог точно сказать про любой пост — этот опубликован сегодня, а этот еще вчера, возникла бы другая проблема. Предположим, что какой-то пост был опубликован за десять минут до окончания дня. К моменту публикации подборки он еще не набирает достаточно голосов, чтобы попасть в нее. За следующий день он выходит на главную страницу, набирает их, даже, может быть, висит на первом месте, но в подборку опять не попадает. Потому что он вчерашний.

Поэтому я решил вообще не принимать во внимание дату публикации, а просто выбирать каждый раз десять новых ссылок с самым большим числом голосов. При таком подходе подборка всё равно будет свежей. А что-то старое (вчерашнее, например) если и будет встречаться, то ближе к концу, да и то вполне заслужено. Кроме того, это решение позволило назначать время публикации подборки совсем произвольно.

За время публикации у меня отвечает cron, который регулярно запускает две самодельные команды для manage.py: одну для загрузки информации с сайта, другую для публикации подборки. Это время я назначил на 23:00 UTC. Это пять утра по челябинскому времени. Пять часов — это отличное время. С одной стороны, ссылки с утра уже успеют появиться в ленте, чтобы их можно было посмотреть по дороге или в университете. С другой, это уже достаточно поздно, чтобы засидевшись вечером не «дождаться» внезапного появления подборки в ленте. Для меня, по крайней мере, это время удобно. А в том, что кто-то еще будет пользоваться этой штукой, я не очень уверен.

http://igorshevchenko.ru/hnews/

3 комментария

Дыхание ветра 22 января 2013

Сделал это я уже давно, но никак не мог выложить, потому что не знал, как объяснить, что это такое. Сейчас попробую еще раз.

Года два назад я копался в контркультурных книжках. Ну, вы знаете, такие с оранжевыми обложками. Там я нашел русский перевод романа Кэндзи Сиратори. Сиратори — нойзовый музыкант, который пишет (по крайней мере, раньше писал) больше сотни альбомов в год. В романе «Кровь электрическая» он использовал тот же подход, что и в музыке. Выглядит текст так:

Кровь электрическая

В предисловии переводчик объясняет, в чем дело:

Текст Сиратори является жестоким миксом четырёх языков: английского, латыни, японского и C++.

Действительно, микс получился довольно жестоким, и в тексте кровь, кишки, хаос и шум вместо нормальной, структурированной программы на правильном С++. Даже на перл не очень похоже.

Дальше переводчик рассказывает о значении элементов С++, использованных Сиратори:

// – конец блока

/ – разделитель уровней (мой дом/моя квартира/ моя комната) + – сложение

++ – постфиксное приращение (брить++себя – бриться)

‹‹ или ‹‹= – сдвиг влево

›› или ››= – сдвиг вправо

! – логическое отрицание

!= – не равно

== – равно

= – присвоение (витал=сыворотка – витал стал сывороткой)

:: – разрешение области действия

Это значит, что даже семантика операторов здесь отличается от настоящей. Например, постфиксное приращение стало уже не унарной, а бинарной операцией (да и постфиксным оно перестало быть, раз записывается между операндов). Что должен делать в программе разделитель уровней я вообще не понял.

Разумеется, несмотря на то, что кто-то решился издать «Кровь электрическую», читать ее, по-моему, невозможно. Поэтому я посмотрел разные места, убедился, что так написаны все 320 страниц, а потом пошел искать в интернете, что об этом думают другие. Кстати да, это было еще до форса частых упоминаний Сиратори на двощах анонимных форумах.

Оказалось, что думают примерно одно и то же: ужас, кошмар, автора надо отправить на лечение, да и переводчика теперь тоже стоило бы; но были и те, кто смог осилить книгу до конца. Они даже рассказывали свои интерпретации сюжета. Например, менее убедительная версия состоит в том, что текст о киборге, который борется с какой-то корпорацией (там и название ее было, но я не помню).

Более убедительная — что он о машине, которой понадобилось тело. И она каким-то образом попала в морг и стала там пытаться собрать его из отдельных частей. Она приделывает себе новые органы, учится пользоваться ими, исследует работу человеческого тела, изучает физиологические реакции. Заканчивается всё главой «Ликвидация» и словами: «Я вычисляю и взрываюсь::планета эродирует».

В выбранной Сиратори тематике, хотя и вполне подходящей для нойза, я вижу еще одну проблему этого романа. В конце концов, шум может состоять и из звуков, которые большинству приятно слышать. А если судить по ленте новостей на вконтакте (это моё окно в мир!), большинство интересуют явно не киборги и трупы, а парни и девушки, настоящая любовь и настоящая дружба, счастье, свадьбы, люди и мамы.

Поэтому я написал парсер публичных страниц в контакте и собрал корпус из сообщений нескольких популярных пабликов на эти темы. Кстати, совсем недавно попробовал еще выделить основные темы документов этого корпуса с помощью модели латентного распределения Дирихле. Результат получился такой:

LDA

Здесь в каждой строчке перечислен набор слов с коэффициентами, которые характеризуют каждую тему. Не слишком разнообразно, зато жизненно.

Но на тот момент задача была другой. Я достал свой скрипт, который называется Соня Котлованова. Он был написан, чтобы генерировать тексты с помощью цепей Маркова, в частности, вопросы для ЧГК и твиты для твитера. В этот раз я обучил его на извлеченном из пабликов корпусе и на некоторых настоящих программах на C++. Соня Котлованова сгенерировала мне текст под названием «Дыхание ветра».

Отрывок из него:

осадок всё равно твоя жизнь полна любви <начинали эти отношения, возмущенное лицо, расстается без слез>

надеждами и глазами(партнером ради единственного поцелуя, валяться с кем приятно,

    одиночку шоколадку, утробе кричало дитя, скучает по тебе скучали) {

    образ является реальной силой(встречайте счастье и любовь где, освобождает дорогу кому);

    бросились в их животах образуется целое кладбище из упавших на дно(пожелайти удачи, влюбились ещё в таком удовольствии);

    непроизвольно исказилось(

        хитрые и коварные врачи спрашивают<вумя рукм>::ок будет,

        исполняют женских желаний<представляют каким должно быть столько одежды>::сходящих друг от друга и его считают юным агрессором);

    комбинезоне жарко(бутылкою в руках удержать, лебединую верность,

        лез в вашу секту<пятнадцати минут>::поддавайся а предали,

        думают что у девушки мозга<свадьба или кольцо>::производят на окружающих столь ошеломительного впечатления);

    позвонила сказать (вставать не хотела этих концертов != любящий мужчина найдет в каждом часу && смесью из равных частей оливкового масла и рома != спичечных коробках)

    закалённый характер (граффити нарисую(название фильма, распускается во всей этой боли тебя вернут в день свадьбы))

    тогда кричишь;

    нечеловеческие способности(бороды хотабыча(тренировочных базах, радовали нас))

    ++выгодно вдвойне;

    далёком прошлом

    ++разобравшись с тем мужчиной, ++сильному уступят дорогу;

    животными и людьми является то == девичьи привычки;

}

Как видно, получившийся текст, во-первых, написан на настоящем С++, в котором все символы значат именно то, что указано в стандарте языка, и, во-вторых, посвящен той правде жизни, которую любят ванильные девочки. Это уже хорошо.

Но не менее важно, что с точки зрения процесса мы оказались чуть ли не ближе к идеям Сиратори, чем он сам. Ведь программа, которая копается в исходниках Standard Template Library и постах из контакта, по-всякому препарируя их и пытаясь сконструировать что-то, похожее на живой текст о том, что дорого людям, — это почти что описанная им машина, пустившая по органам нескольких тел свою электрическую кровь.

Только плюсы правильные. И про любовь.

Нет комментариев

Прочтение этого текста позволит вам обрести стиральную машину, микроволновую печь и автомобиль 22 сентября 2012

Эдгар По написал рассказ о дядьке, который случайно нашел зашифрованную записку, применил к ней в то время считавшиеся передовыми криптографические методы и в результате получил инструкцию по обретению пиратского клада. Сперва он произвел сложные логические ходы: определил, что послание написано на английском языке, и предположил, что каждая буква латинского алфавита везде заменена каким-то другим символом. Дальше чисто технические. Известно, что в любом языке одни буквы встречаются реже, а другие чаще. Исследовав достаточный массив текстов, можно определить, что это за буквы. Логично, что и в зашифрованном тексте символ, встречающийся наибольшее число раз, кодирует самую популярную букву, и так далее. Можно так расшифровать хотя бы часть текста, а дальше уже подбирать по смыслу.

Но это происходило в XIX веке, а сегодня криптографической деятельностью можно заниматься только специальным федеральным службам, поэтому простым любителям частотного анализа приходится искать другие области его применения. Например, капитал-шоу «Поле Чудес».

Типичная игра там проходит так: игроки видят, из скольких букв состоит слово; пытаются назвать буквы, которые вероятнее всего будут в этом слове (обычно гласные, они же во всех словах есть); затем, когда гласных набирается, на их взгляд, достаточно, пытаются угадать согласные, которые были бы уместны среди того, что уже получено; и наконец, когда открыто достаточно букв, чтобы исключить большинство вариантов, какой-то счастливчик называет слово. Таким образом, у каждого игрока в голове есть какая-то частотная модель русского языка, которой они пользуются, чтобы максимизировать вероятность угадывания. Причем эта модель должна отличаться от той, которой пользовался герой Эдгара По (и, как мне почему-то казалось, герой Артура Конана Дойля в «Пляшущих человечках», но там оказалась своя мистическая технология):

Во-первых, слова в игре выбираются случайно, в отличие от нашей речи, где одни есть чуть ли не в каждом предложении, а про другие никто никогда не слышал. Поэтому модель должна создаваться не по какому-то корпусу, а по словарю, учитывая каждое слово по одному разу.

Во-вторых, самые распространенные слова — в основном, предлоги, союзы и всё такое, — как и глаголы с именами прилагательными, вообще считать не надо, потому что в русских играх со словами (в отличие от английских, например) не принято их загадывать. Только нарицательные существительные.

В-третьих, не имеет значения, сколько раз встречается буква в слове. Игрок не целится в какую-то конкретную позицию, а говорит: «я думаю, что это слово, в котором присутствует буква Ю». И вероятность угадывания зависит уже от количества слов, в которых эта буква присутствует, а не от количества употреблений этой буквы.

Представьте, что вы оказались на телеигре, и попробуйте и у себя в голове построить такую модель. Какие буквы, на ваш взгляд, будут более популярными, а какие менее?

Я извлек из словаря Открытого корпуса русского языка все нарицательные существительные, которых вышло примерно 66 тысяч, обработал их и получил такой результат (буквы по убыванию частоты):

А О Е И Н Р Т К С Л В П Ь М Д У З Г Б Я Ц Ч Ф Ж Ш Х Ы Щ Й Э Ю Ъ

Немного отличается от обычной частотности (из Википедии на основе Национального корпуса русского языка):

О Е А И Н Т С Р В Л К М Д П У Я Ы Ь Г З Б Ч Й Х Ж Ш Ю Ц Щ Э Ф Ъ

Хотя и не очень.

В-четвертых, игрокам изначально известна длина слова. И это можно использовать, потому что в коротких словах чаще встречаются одни буквы, а в длинных другие.

Вот, например, по четырехбуквенным словам:

А Р О Е Т К Л С И У Н П Д М Б Г В З Ь Я Ш Ф Х Ч Ю Ж Й Э Ы Ц Щ Ъ

И по восьмибуквенным:

А О И Е Н Р К Т Л С П В М Д У Ь З Б Г Ч Я Ц Ш Ф Х Ж Й Ы Щ Э Ю Ъ

И в-пятых, в процессе игры ее участники получают новую информацию — о буквах, которых нет в слове, и о местонахождении тех, которые есть.

Учитывая эти пять пунктов, можно предложить такую стратегию: игрок, перед тем как назвать букву, выбирает из словаря все слова нужной длины, в которых не встречаются ошибочно названные буквы, а отгаданные стоят на соответствующих местах. Затем он подсчитывает для каждой буквы количество слов, в которых она встречается, и называет ту, для которой это число максимально. Мне кажется, любой участник «Поля чудес» способен проделать это, пока крутится барабан.

Недостатком этого алгоритма является то, что он детерминированный, то есть каждый раз можно предсказать, каким будет следующий ход. В теории игр существует понятие равновесия Нэша — состояния, когда всем участникам игры известны стратегии остальных, но ни один из них не может увеличить своего выигрыша, изменив свою стратегию, если у остальных участников она останется прежней. Если представить, что в «Поле чудес» есть два игрока, один из которых — Якубович, загадывающий слово, а другой — вся тройка, пытающаяся назвать как можно меньше букв, которых в слове нет, причем они побеждают, если отгадают слово, допустив менее какого-то фиксированного числа ошибок; так вот, если представить, то равновесия при использовании этой стратегии нет. Ведущий может подобрать слово, которое не получается отгадать, и каждый раз загадывать его. Немного (но далеко не полностью) приблизить равновесие Нэша можно, если делать выбор более случайно. Например, в какой-то момент посчитали, что буква А встречается в 5 словах, буква О — в 7, а буква Р — в 3. Тогда запускается генератор случайных чисел, с помощью которого с вероятностью 5/15 выбирается А, 7/15 — О и 3/15 — Р. В среднем это должно дать то же самое.

Чтобы выяснить, какие слова при такой игре окажутся самыми сложными, я промоделировал этот алгоритм на сингл-плеерном аналоге «Поля чудес» — «Виселице» (о которой, на самом деле, всё это время речь и шла). Поскольку никаких вычислительных ресурсов, кроме ноутбука, который можно оставить на ночь, у меня нет, то после нескольких часов с профилировщиком питона (благодаря которым я даже смог улучшить время работы в несколько раз!), было решено проводить исследования в три этапа:

  1. Перебрать все 66 тысяч слов с помощью первого детерминированного варианта, чтобы отбросить наиболее легкие. Примерно в трети случаев слова были названы полностью без единой ошибки. В итоге, я оставил те десять тысяч, в которых отгадыватель предположил более трех букв неправильно.
  2. Затем, прогнать каждое слово из оставшихся по 20 раз уже на случайном алгоритме. Тут появилась небольшая проблема с тем, что требовалось определиться, сколько ошибок можно сделать до того, как игрока повесят. Просто отгадывать до конца, а потом посчитать среднее, нельзя: поражение с пороговым числом ошибок и поражение с числом, превышающим его, по сути одинаковы. Оказалось, что единого мнения о количестве деталей на рисунке с висельником, нет. Пришлось прикинуть: столб, перекладина, петля, голова, туловище, правая рука, левая рука, правая нога, левая нога, посиневший язык. Итого десять.
  3. Около четырехсот слов, которые не были отгаданы более, чем в половине случаев, я прогнал уже немного более масштабно: по 100 раз каждое. Топ по числу поражений выглядит так:

ЦАЦКА ЖИЖА ЦЕЖ ЧОХ

Такие результаты объяснимы (кроме слова цой, которое внезапно оказалось нарицательным, но составители словаря наверняка знали, что делали). Самыми сложными оказались не длинные непонятные слова — в таких встречается много разных букв, поэтому вероятность ошибки мала, — а короткие с редкими и иногда с повторяющимися буквами, чтобы попасть в правильную было как можно труднее. В случае с цацкой не помогает даже легко отгадываемая буква А: в словаре было двести пятибуквенных слов с А на второй и последней позициях.

А перед тем, как начнется рекламная пауза, я хочу передать привет Саше, Саше, Антону, Сереже, Насте, Саше, Оле, Толику, Юле, Анюте, Богдану и всем остальным, кто может меня читать.

1 комментарий

Расчлененка и сноб 29 февраля 2012

Решил попробовать читать Воннегута в оригинале.

Мысль возникла где-то на середине «Завтрака для чемпионов», который я читал пару месяцев назад. Переводили «Завтрак» в Советском Союзе, ну и, разумеется, цензура сократила роман на несколько абзацев. Пропущенные куски восстановил только создатель электронной версии, оставив их на английском. Я их чудесно понял, и подумал, что надо было мне без перевода читать, раз я могу. Было слегка скучно, а так хоть какая-то польза. Это во-первых. Во-вторых, в «Бойню» и «Колыбель», которые я читал раньше, никто таких примечаний не добавлял, хотя непонятно, почему бы советские цензоры не могли вырезать что-нибудь и там. Утерянное надо компенсировать, а чтобы вы поняли, как высоко его стоит оценивать, приведу одну вырезанную часть:

Dwayne Hoover, incidentally, had an unusually large penis, and didn’t even know it. The few women he had had anything to do with weren’t sufficiently experienced to know whether he was average or not. The world average was five and seven-eighths inches long, and one and one-half inches in diameter when engorged with blood. Dwayne’s was seven inches long and two and one-eighth inches in diameter when engorged with blood.

Dwayne’s son Bunny had a penis that was exactly average.

Kilgore Trout had a penis seven inches long, but only one and one-quarter inches in diameter.

...

Harry LeSabre, Dwayne’s sales manager, had a penis five inches long and two and one-eighth inches in diameter.

Cyprian Ukwende, the black physician from Nigeria, had a penis six and seven-eighths inches long and one and three-quarters inches in diameter.

Don Breedlove, the gas-conversion unit installer who raped Patty Keene, had a penis five and seven-eighths inches long and one and seven-eighths inches in diameter.

И так далее. Очень важный момент.

И, наконец, как же не проверить тезис о том, что романы Курта проигрывают в оригинале, который высказал кто-то у Довлатова. Сергей Довлатов выглядел так:

Довлатов

О Довлатове существует легенда, упоминая которую, иногда ссылаются на Андрея Арьева. Говорят, что он писал так, чтобы в каждом предложении все слова начинались с разных букв. Это формальное правило он использовал, чтобы замедлить появление текста. Необходимость следить за его соблюдением заставляла Довлатова останавливаться на каждой фразе, совершенствовать ее, подбирать лучшие слова. Иногда ради этого приходилось жертвовать правдой. Здесь можно вспомнить отредактированную цитату из Пушкина в «Заповеднике»:

— Исполнилось пророчество; «Не зарастет священная тропа!..»

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

Но если читать внимательно, то можно заметить, что предложения со словами на одну букву порой встречаются. Читая других авторов, я никогда ни о чем подобном не думал, поэтому трудно сказать, но кажется, что у других чаще. Гарантировать это не стал бы. Чтобы понять однозначно, я применил немного современных технологий.

Инновационные средства исследования включают в себя два модуля на пайтоне. Первый состоит из класса, который получает куски текста, обрабатывает их, а затем сообщает, сколько в этих кусках было предложений и сколько из них содержат слова на одну букву. Второй открывает по очереди файлы с текстами и отдает их на анализ классу из первого. Он получился не то чтобы красивым, потому что электронные книги имеют странные форматы, ну и с кодировками понадобилась кое-какая возня. Посмотреть код можно на гитхабе.

Сперва я посчитал статистику по тридцати случайным произведениям.

Статистика по 30 случайным произведениям

Здесь обращать внимание стоит в первую очередь на проценты у Довлатова. Остальные — просто для точечного сравнения с конкретными произведениями, и показывать что-то не могут, потому что их всего тридцать, а текстов на русском языке немного больше. Например, в архиве с рутрекера их шестьдесят две тысячи. Но в анализе участвовали не все. Небольшую сомнительную часть я удалил сразу. Некоторые файлы не смогли обработаться, потому что в них был какой-то непостижимый иврит. Наконец, уже получив результат, я исключил тексты, содержащие менее двадцати предложений. В том числе и пелевинскую «Водонапорную башню», в одном предложении которой двести девяносто семь слов на букву с. В итоге, осталось шестьдесят две тысячи шесть текстов. Обработка заняла три с половиной часа. Ее итоги таковы:

Статистика по всем произведениям

Здесь видно, что в девяноста восьми процентах текстов два предложения из пяти содержат слова на одну букву. Среднее значение — шестьдесят семь с половиной процентов таких предложений. Более конкретные результаты приводить не буду, потому что книги из этого сборника могут содержать влияющие ошибки (уверен, что если и есть что-то неправильное, то только там; для своего кода я написал прекрасные юнит-тесты). Но количество должно сделать средний результат достоверным. Но в любом случае, даже с расхождениями на пару процентов, у Довлатова предложений со словами на одну букву настолько меньше этого среднего, что это вряд ли могло получиться случайно. Confirmed.

Confirmed

«Сирен Титана» я бросил после двенадцати страниц. Невыносимо стало перечитывать одно и то же, и понимать, что еще долго ничего не произойдет. А может, Гор Видал и прав, действительно проигрывают.

Plausible.

Нет комментариев
Позже →