├── .gitignore ├── 1 ДЗ - скачать заголовки.md ├── 1 Семинар - urllib.ipynb ├── 12 ДЗ.md ├── 12 Просто множества.ipynb ├── 2 Семинар - краулеры.ipynb ├── 3 Семинар - Mystem.md ├── 4 Семинар - про проект!.ipynb ├── 5 Семинар - json.ipynb ├── 6 Семинар - Запросы и формы.ipynb ├── 7 Семинар - flask intro.ipynb ├── 8 Семинар - Снова flask.ipynb ├── 9-10 Семинар - Анкета.md ├── FAQ.md ├── Heroku.md ├── Matplotlib+VK.ipynb ├── Matplotlib.ipynb ├── README.md ├── TelegramBot1.ipynb ├── TelegramBot2.ipynb ├── TelegramBot3.ipynb ├── Tweepy.ipynb ├── VK + matplotlib.ipynb ├── VK API Часть 1.ipynb ├── bot_example ├── bot.py ├── conf.py ├── results.csv └── reviews.csv ├── data ├── Genproc.csv ├── lang_codes.csv ├── skolkovo_ru.csv ├── text.json └── text.xml ├── flask_example ├── my_app.py └── templates │ ├── answer.html │ ├── books.html │ ├── index.html │ ├── question.html │ └── thanks.html ├── img ├── one_does_not_mystem.jpg ├── tedbot1.png └── tedbot2.png ├── matplotlib+vk+homework.md ├── matplotlib+vk.md ├── pymorphy2, pymystem3.ipynb ├── skolkovo_retweets.py ├── telegram_bot.md ├── test └── corpus.txt ├── text.txt ├── word2vec.ipynb ├── Графы, networkx.ipynb ├── Занятия в 1-м семестре.md ├── Интерактивные графики и карты в вебе.md ├── Командная строка UNIX, логин на сервере.md └── Лекция по дистрибутивной семантике.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .idea/ 3 | .ipynb_checkpoints/ -------------------------------------------------------------------------------- /1 ДЗ - скачать заголовки.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание 1 2 | 3 | В течение нескольких следующих занятий мы будем делать небольшой проект - региональный газетный корпус. Все вместе мы выкачаем тексты статей с сайтов небольших местных газет, обработаем их и соберем в настоящий корпус. 4 | 5 | Задание к следующему семинару такое:
6 | 7 | 1. Выбрать себе одну газету из [этого списка](https://docs.google.com/spreadsheets/d/1VHGhQN1ohaEMFaxMn4nPz7COdHuMtflagoD3kA2TuxM/edit?usp=sharing). Впишите себя в таблицу напротив выбранной газеты. При выборе нужно иметь в виду две вещи:
8 | 10 | 11 | 2. С помощью `urllib.request` нужно скачать главную страницу вашей газеты, извлечь оттуда все заголовки статей и напечатать заголовки в отдельный текстовый файл. 12 | 13 | Это домашнее задание не на оценку, а для тренировки. Домашнее задание на оценку будет большое -- это будет ваш проект корпуса региональной газеты, его вы сделаете после того, как мы пройдем все нужные темы. 14 | 15 | ------ 16 | 17 | UPDATE: 18 | А еще напомните, пожалуйста, нам адреса ваших репозиториев для домашних заданий: вставьте ссылку в эту [форму](https://goo.gl/forms/qnPUwdlohUpInDbo1). 19 | 20 | Если у вас нет репозитория на GitHub для домашних заданий, создайте его. 21 | -------------------------------------------------------------------------------- /1 Семинар - urllib.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Семинар 1. Выкачиваем Интернет.\n", 8 | "\n", 9 | "## Введение\n", 10 | "Современный Интернет предоставляет лингвистам большое количество языковых данных: электронные газеты и журналы, блоги, форумы, социальные сети и т.д. Например, можно найти в сети много-много текстов и собрать корпус, или найти все газетные статьи и блог-посты про какую-нибудь корпорацию и проанализировать тональность сообщений. Сейчас мы научимся заниматься выкачиванием страниц из интернета с помощью Python.\n", 11 | "\n", 12 | "Для скачивания HTML-страниц в питоне есть специальный модуль **urllib.request**. \n", 13 | "\n", 14 | "## Минимальный пример\n", 15 | "Допустим, мы хотим скачать главную страницу Хабрахабра. \n", 16 | "На самом деле, когда мы хотим открыть какую-то страницу в интернете, наш браузер отправляет на сервер запрос (\"Привет, сервер! я хочу код страницы по вот такому адресу!\"), а сервер затем отправляет ответ (\"Привет! Вот код страницы: ...\").\n", 17 | "Чтобы получить страницу через питон, нужно сформировать запрос на сервер так же, как это делает браузер:" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 12, 23 | "metadata": { 24 | "collapsed": true 25 | }, 26 | "outputs": [], 27 | "source": [ 28 | "import urllib.request # импортируем модуль \n", 29 | "req = urllib.request.Request('https://habrahabr.ru/')\n", 30 | "with urllib.request.urlopen(req) as response:\n", 31 | " html = response.read().decode('utf-8')" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "В переменной **req** у нас как раз находится запрос.\n", 39 | "Функция **urlopen** получает ответ сервера и скачивает страницу по ссылке https://habrahabr.ru/ в переменную **response**. **response** ведет себя как файл: например мы можем прочитать его содержимое с помощью **.read()** в другую переменную. \n", 40 | "Вот так просто мы сохранили код страницы в переменной **html**. Убедимся, что в там лежит html-код:" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 13, 46 | "metadata": { 47 | "collapsed": false 48 | }, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "\n", 55 | "\n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " Интересные публикации / Хабрахабр\n", 60 | "\n", 61 | "\n", 62 | "\n" 63 | ] 64 | } 65 | ], 66 | "source": [ 67 | "print(html[:210])" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "Иногда сайт блокирует запросы, если их посылает не настоящий браузер с пользователем, а какой-то бот (например, так делает Гугл или Википедия). Иногда сайты присылают разные версии страниц, разным браузерам. \n", 75 | "По этим причинам полезно бывает писать скрипт, который умеет притворяться то одним, то другим браузером.\n", 76 | "Когда мы пытаемся получить страницу с помощью **urllib**, наш код по умолчанию честно сообщает серверу, что он является программой на питоне. Он говорит что-то вроде \"Привет, я Python-urllib/3.5\". \n", 77 | "Но можно, например, представиться Мозиллой:" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": { 84 | "collapsed": true 85 | }, 86 | "outputs": [], 87 | "source": [ 88 | "url = 'https://habrahabr.ru/' # адрес страницы, которую мы хотим скачать\n", 89 | "user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)' # хотим притворяться браузером\n", 90 | "\n", 91 | "req = urllib.request.Request('https://habrahabr.ru/', headers={'User-Agent':user_agent}) \n", 92 | "# добавили в запрос информацию о том, что мы браузер Мозилла\n", 93 | "\n", 94 | "with urllib.request.urlopen(req) as response:\n", 95 | " html = response.read().decode('utf-8')" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "## Напоминание: как найти на странице что-нибудь\n", 103 | "Теперь предположим, что мы хотим выкачивать заголовки статей с главной страницы Хабрахабра. Код страницы у нас уже есть, но как из него что-то вытащить. Для начала нужно посмотреть в [исходник](view-source:https://habrahabr.ru/) и заметить, что заголовки хранятся в тэге **h2** с классом **post__title**. Заголовок выглядит примерно так:\n", 104 | "\n", 105 | "

\n", 106 | " Администрирование →\n", 107 | " Мониторинг сетевого стэка linux\n", 108 | "

\n", 109 | " \n", 110 | "А код у него такой:" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": { 117 | "collapsed": true 118 | }, 119 | "outputs": [], 120 | "source": [ 121 | "

\n", 122 | " Администрирование →\n", 123 | " Мониторинг сетевого стэка linux\n", 124 | "

" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "Чтобы вытащить все такие заголовки, воспользуемся регулярным выражением." 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 19, 137 | "metadata": { 138 | "collapsed": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "import re\n", 143 | "regPostTitle = re.compile('

.*?

', flags=re.U | re.DOTALL)\n", 144 | "titles = regPostTitle.findall(html)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "Посмотрим, сколько там заголовков. И взглянем, например, на первые три." 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 20, 157 | "metadata": { 158 | "collapsed": false 159 | }, 160 | "outputs": [ 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "10\n" 166 | ] 167 | } 168 | ], 169 | "source": [ 170 | "print(len(titles))" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 21, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "['

\\n Разработка →\\n Выявление проблем дорожной сети с помощью Яндекс.Пробок. Лекция в Яндексе\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n

', '

\\n Разработка →\\n Plug-and-Get-Security I, мониторинг настроек TLS в роще доменов\\n \\n\\n\\n\\nиз песочницы\\n\\n\\n\\n\\n

', '

\\n Администрирование →\\n Мониторинг сетевого стэка linux\\n \\n\\n\\n\\n\\n\\n\\n\\n\\n

']\n" 185 | ] 186 | } 187 | ], 188 | "source": [ 189 | "print(titles[:3])" 190 | ] 191 | }, 192 | { 193 | "cell_type": "markdown", 194 | "metadata": {}, 195 | "source": [ 196 | "Теперь давайте очистим заголовки от лишних переносов строк, лишних тэгов и распечатаем их подряд." 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 28, 202 | "metadata": { 203 | "collapsed": false 204 | }, 205 | "outputs": [ 206 | { 207 | "name": "stdout", 208 | "output_type": "stream", 209 | "text": [ 210 | "Разработка →Выявление проблем дорожной сети с помощью Яндекс.Пробок. Лекция в Яндексе\n", 211 | "Разработка →Plug-and-Get-Security I, мониторинг настроек TLS в роще доменовиз песочницы\n", 212 | "Администрирование →Мониторинг сетевого стэка linux\n", 213 | "Управление →Квалификация коллег-программистов: ожидание и реальностьиз песочницы\n", 214 | "Разработка →Немного о ARM Security Extensions (aka ARM TrustZone)\n", 215 | "Разработка →Offline-first приложение с Hoodie & React. Часть вторая: авторизацияtutorial\n", 216 | "Администрирование →Кабель-менеджмент и PUE: как они связаны?\n", 217 | "Разработка →Функциональная безопасность, Часть 2 из 2. МЭК 61508: кем быть, Шерлоком Холмсом или Дата Туташхиа?\n", 218 | "Управление →Дорогие стартапы, хватит задавать математические задачки, чтобы понять умею-ли я программироватьперевод\n", 219 | "Управление →Мнения экспертов об ушедшем в историю «правиле 20%» Google\n" 220 | ] 221 | } 222 | ], 223 | "source": [ 224 | "new_titles = []\n", 225 | "regTag = re.compile('<.*?>', flags=re.U | re.DOTALL)\n", 226 | "regSpace = re.compile('\\s{2,}', flags=re.U | re.DOTALL)\n", 227 | "for t in titles:\n", 228 | " clean_t = regSpace.sub(\"\", t)\n", 229 | " clean_t = regTag.sub(\"\", clean_t)\n", 230 | " new_titles.append(clean_t)\n", 231 | "for t in new_titles:\n", 232 | " print(t)" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "Ну и осталось убрать некрасивые кусочки html, а именно заменить специальные html-последовательности nbsp и rarr на стрелочку, например." 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 29, 245 | "metadata": { 246 | "collapsed": false 247 | }, 248 | "outputs": [ 249 | { 250 | "name": "stdout", 251 | "output_type": "stream", 252 | "text": [ 253 | "Разработка -> Выявление проблем дорожной сети с помощью Яндекс.Пробок. Лекция в Яндексе\n", 254 | "Разработка -> Plug-and-Get-Security I, мониторинг настроек TLS в роще доменовиз песочницы\n", 255 | "Администрирование -> Мониторинг сетевого стэка linux\n", 256 | "Управление -> Квалификация коллег-программистов: ожидание и реальностьиз песочницы\n", 257 | "Разработка -> Немного о ARM Security Extensions (aka ARM TrustZone)\n", 258 | "Разработка -> Offline-first приложение с Hoodie & React. Часть вторая: авторизацияtutorial\n", 259 | "Администрирование -> Кабель-менеджмент и PUE: как они связаны?\n", 260 | "Разработка -> Функциональная безопасность, Часть 2 из 2. МЭК 61508: кем быть, Шерлоком Холмсом или Дата Туташхиа?\n", 261 | "Управление -> Дорогие стартапы, хватит задавать математические задачки, чтобы понять умею-ли я программироватьперевод\n", 262 | "Управление -> Мнения экспертов об ушедшем в историю «правиле 20%» Google\n" 263 | ] 264 | } 265 | ], 266 | "source": [ 267 | "for t in new_titles:\n", 268 | " print(t.replace(\" →\", \" -> \"))" 269 | ] 270 | }, 271 | { 272 | "cell_type": "markdown", 273 | "metadata": {}, 274 | "source": [ 275 | "### Ура! Мы умеем скачивать страницу и вытаскивать из нее какую-то интересную информацию.\n", 276 | "\n", 277 | "## Задания\n", 278 | "\n", 279 | "1. Скачать главную страницу Яндекс.Погоды и
\n", 280 | " \n", 281 | "     а) распечатать сегодняшнюю температуру и облачность
\n", 282 | " \n", 283 | "     б) распечатать время восхода и заката
\n", 284 | " \n", 285 | "     в) погоду на завтра
\n", 286 | " \n", 287 | "2. Скачать главную страницу waitbutwhy.com. Распечатать заголовки популярных постов (которые в колонке справа с надписью Popular Posts) и колличество комментариев у каждого из них." 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## Некоторые объяснения про регулярные выражения\n", 295 | "\n", 296 | "* Что такое `re.compile`?

\n", 297 | "Грубо говоря, `compile()` позволяет запомнить регулярное выражение и использовать его несколько раз. Суть в том, что перед тем как прогнать регулярку через строку, питон должен ее \"скомпилировать\" - превратить **строку** с регулярным выражением в специальный **объект**.
\n", 298 | "Строчка `re.search(..., ...)` сначала компилирует регулярное выражение, а потом выполняет поиск. Если нужно поискать что-то один раз, то такая строчка очень удобна. А если нужно поискать что-то много раз, то получится что одно и то же выражение мы компилируем много раз. А хочется один раз скомпилировать и потом много раз пользоваться. Поэтому пишут так:" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "metadata": { 305 | "collapsed": true 306 | }, 307 | "outputs": [], 308 | "source": [ 309 | "text = 'тут текст, внутри которого мы что-то ищем'\n", 310 | "regName = re.compile('тут регулярное выражение') # скомпилировали\n", 311 | "toSearch = regName.search(text) # теперь можно искать в тексте\n", 312 | "toFindAll = regName.findall(text) # можно использовать скомпилированное выражение много раз\n", 313 | "toSub = regName.sub('на.что.заменить', text) # и так тоже можно использовать" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "* Что делает `regName.sub(..., ...)`?

\n", 321 | "Выражение `regName.sub('на_что_заменить', text)` значит: возьми скомпилированное выражение из переменной `regName`, и замени все, что соответствует этому выражению в переменной `text`, на строку `'на_что_заменить'`. Если первый аргумент в этом случае - пустая строка, то все найденные регуляркой куски заменятся на пустую строку, короче говоря, удалятся.

\n", 322 | "\n", 323 | "* Что такое `re.DOTALL`?

\n", 324 | "Обычно точка в регулярном выражении означает любой символ КРОМЕ символа новой строки. Чтобы изменить такое поведение, в компиляцию регулярки можно добавить параметры-флаги вот так: `flags = re.DOTALL`, и тогда точка будет ловить вообще любой символ, включая новую строку. Эти флаги слегка меняют поведение функции, вот и все.

\n", 325 | "\n", 326 | "* Что такое `re.U`?

\n", 327 | "U я написала зря, потому что я обычно пишу на втором питоне: во втором питоне по умолчанию выражения типа `\\w`, `\\W`, `\\s` и подобные работают только на строках ASCII, и чтобы они работали на юникодных строках нужно написать re.U. В третьем питоне все строки и так юникодные, поэтому необходимости в таком флаге больше нет. Но если вдруг вам придется использовать регулярки из второго питона, запомните про такую штуку.\n" 328 | ] 329 | } 330 | ], 331 | "metadata": { 332 | "celltoolbar": "Raw Cell Format", 333 | "kernelspec": { 334 | "display_name": "Python 3", 335 | "language": "python", 336 | "name": "python3" 337 | }, 338 | "language_info": { 339 | "codemirror_mode": { 340 | "name": "ipython", 341 | "version": 3 342 | }, 343 | "file_extension": ".py", 344 | "mimetype": "text/x-python", 345 | "name": "python", 346 | "nbconvert_exporter": "python", 347 | "pygments_lexer": "ipython3", 348 | "version": "3.4.3" 349 | } 350 | }, 351 | "nbformat": 4, 352 | "nbformat_minor": 0 353 | } 354 | -------------------------------------------------------------------------------- /12 ДЗ.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание по множествам 2 | ## весит 1/3 оценки от обычного большого проекта 3 | 4 | Вам нужно зайти в [Яндекс-новости](https://news.yandex.ru/) (на самом деле -- в любой новостной агрегатор, который умеет схлопывать новостные заметки с разных сайтов в один сюжет). 5 | Там есть "новостные сюжеты", то есть кластеры, в которые собираются отдельные новостные заметки с разных сайтов. Обычно в практическом смысле это значит, что несколько (или даже много, если событие важное) новостных сайтов берут информацию из одного источника, немного переписывают и дополняют исходный текст и порождают в конечном счёте очень похожие друг на друга тексты новостей. 6 | [Вот пример такого сюжета](https://news.yandex.ru/yandsearch?lr=213&cl4url=www.m24.ru%2Farticles%2F123422&lang=ru&rubric=science&from=rubric). Здесь есть какое-то основное событие и ссылки на отдельные новости на разных сайтах, которые про него рассказывают. 7 | 8 | Там вам нужно будет выбрать себе сюжет по вкусу и написать программу, которая: 9 | 10 | 1. скачивала несколько (4-5) страниц таких новостей из одного сюжета. 11 | 12 | Если Яндекс вас блокирует, то вместо того, чтобы скачивать страницы Яндекс.новостей, можно зайти в сюжет и найти там ссылки на новости руками. Эти ссылки можно, например, записать в массив или в файл. А вот уже скачивать страницы с новостями по этим ссылкам (не с яндекса! а с новостного сайта) нужно уже с помощью urlllib.request. 13 | 14 | 2. Доставала из них собственно текст новости. 15 | 3. Преобразовывала эти тексты в множества слов. 16 | 4. Находила пересечения этих множеств, то есть какие словоформы для всех заметок в сюжете являются общими? 17 | 5. Находила симметрическую разность этих множеств, то есть какие словоформы являются уникальными для новостных заметок в пределах одного сюжета. 18 | 19 | Результаты должны быть выведены в файлы, по одному на строчку в алфавитном порядке. 20 | 21 | За это можно получить 8 баллов. 22 | 23 | Отдельное задание на 10 баллов: 24 | Из симметрической разности множеств нужно оставить только те словоформы, которые имеют частотность больше 1. 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /12 Просто множества.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Операции с множествами\n", 8 | "\n", 9 | "Эпиграф:\n", 10 | "\n", 11 | "*Соня меж тем закрыла глаза и задремала. Но тут Болванщик ее ущипнул, она взвизгнула и проснулась. - начинается на М, - продолжала она. - Они рисовали мышеловки, месяц, математику, множество... Ты когда-нибудь видела, как рисуют множество?*\n", 12 | "\n", 13 | "*- Множество чего? - спросила Алиса.*\n", 14 | "\n", 15 | "*- Ничего, - отвечала Соня. - Просто множество!*" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "Множества - особенная структура данных, чем-то похожая и на массив, и на словарь, но от них отличающаяся.\n", 23 | "\n", 24 | "Как и массивы, множества содержат элементы. Но в отличие от массивов, где элементы упорядочены, в множестве они идут в \"произвольном\" порядке (для компьютера он, конечно, не произвольный, а обусловленный рядом технических причин, для человека не очевидных).\n", 25 | "\n", 26 | "На словари множества похожи тем, что, как и ключи в словарях, элементы множеств должны быть уникальны, и не следуют в фиксированном порядке. " 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 9, 32 | "metadata": { 33 | "collapsed": false 34 | }, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "{1, 2, 3}\n" 41 | ] 42 | } 43 | ], 44 | "source": [ 45 | "# вот так можно превратить массив в множство:\n", 46 | "\n", 47 | "a = [1, 2, 3, 1, 2, 3]\n", 48 | "s = set(a)\n", 49 | "print (s)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Зачем нужны множества\n", 57 | "\n", 58 | "Во-первых, поиск в множестве происходит быстрее, чем в массиве. Если вам нужно собрать какие-то данные, а потом проверять, есть ли в этих данных что-то, то целесообразно использовать именно множества" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 10, 64 | "metadata": { 65 | "collapsed": false 66 | }, 67 | "outputs": [ 68 | { 69 | "name": "stdout", 70 | "output_type": "stream", 71 | "text": [ 72 | "True\n", 73 | "True\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "# вариант не очень:\n", 79 | "a = [1, 2, 3, 1, 2, 3]\n", 80 | "if 1 in a:\n", 81 | " print('True')\n", 82 | " \n", 83 | "# как надо:\n", 84 | "a = [1, 2, 3, 1, 2, 3]\n", 85 | "s = set(a)\n", 86 | "if 1 in a:\n", 87 | " print('True')\n", 88 | " \n", 89 | "# конечно, на таком маленьком наборе разница незаметна, но когда объём данных вырастет, \n", 90 | "# программы, в которых используются множства, начнут работать ощутимо быстрее." 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "Во-вторых, преобразование в множества помогают быстро превратить набор данных в набор уникальных элементов, как это было в примере выше: в массиве повторялись числа 1, 2 и 3. В множестве каждое из чисел осталось в единственном экземпляре." 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "## Собственно операции\n", 105 | "\n", 106 | "Множества поддерживают разные специальные операции, которых нельзя так же просто осуществить с другими структурами данных:" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 13, 112 | "metadata": { 113 | "collapsed": false 114 | }, 115 | "outputs": [ 116 | { 117 | "name": "stdout", 118 | "output_type": "stream", 119 | "text": [ 120 | "{1, 2, 3, 4, 5, 6, 7, 8, 9}\n", 121 | "{4, 5, 6}\n", 122 | "{1, 2, 3}\n", 123 | "{1, 2, 3, 7, 8, 9}\n" 124 | ] 125 | } 126 | ], 127 | "source": [ 128 | "a = set([1, 2, 3, 4, 5, 6])\n", 129 | "b = set([4, 5, 6, 7, 8, 9])\n", 130 | "\n", 131 | "# объединение\n", 132 | "c = a | b\n", 133 | "print(c)\n", 134 | "\n", 135 | "# пересечение\n", 136 | "c = a & b\n", 137 | "print(c)\n", 138 | "\n", 139 | "# разность\n", 140 | "c = a - b\n", 141 | "print(c)\n", 142 | "\n", 143 | "# симметрическая разность, то есть элементы, входящие в a или в b, но не в оба множества одновременно\n", 144 | "c = a ^ b\n", 145 | "print(c)" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Задание для тренировки можно посмотреть [на странице ДЗ](https://github.com/elmiram/2016learnpython/blob/master/12%20%D0%94%D0%97.md)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": { 159 | "collapsed": true 160 | }, 161 | "outputs": [], 162 | "source": [] 163 | } 164 | ], 165 | "metadata": { 166 | "kernelspec": { 167 | "display_name": "Python 3", 168 | "language": "python", 169 | "name": "python3" 170 | }, 171 | "language_info": { 172 | "codemirror_mode": { 173 | "name": "ipython", 174 | "version": 3 175 | }, 176 | "file_extension": ".py", 177 | "mimetype": "text/x-python", 178 | "name": "python", 179 | "nbconvert_exporter": "python", 180 | "pygments_lexer": "ipython3", 181 | "version": "3.5.2+" 182 | } 183 | }, 184 | "nbformat": 4, 185 | "nbformat_minor": 1 186 | } 187 | -------------------------------------------------------------------------------- /2 Семинар - краулеры.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Семинар 2. Выкачиваем Интернет (продолжение).\n", 8 | "\n", 9 | "Эпиграф: *У меня есть дома интернет. Можно я его скачаю на флешку и с собой принесу?*", 10 | "\n", 11 | "## Введение\n", 12 | "\n", 13 | "На прошлом семинаре мы научились скачивать из Интернета HTML-код страницы с заданным адресом. Это здорово, но на практике обычно приходится выкачивать содержимое не одной страницы, а целого сайта или даже многих сайтов --- тысячи или миллионы страниц. Понятно, что перечислить все интересующие нас адреса вручную, чтобы выкачать их по отдельности, в таком случае не получится. На этом семинаре мы выясним, как можно выкачивать страницы оптом, а также научимся получше их чистить от ненужных вещей.\n", 14 | "\n", 15 | "Основной проблемой, которую нужно решить при выкачивании большого количества страниц, --- как узнать адреса всех этих страниц. Мы рассмотрим два подхода.\n", 16 | "\n", 17 | "*Первый подход* обычно применяется, когда нужно загрузить все страницы какого-нибудь крупного ресурса --- например, газеты или форума. Адреса страниц на таких сайтах нередко устроены довольно просто: начинаются они все одинаково, а заканчиваются разными числами. Если внимательно посмотреть на адреса нескольких произвольных страниц, можно довольно быстро выяснить, так ли это и каков допустимый диапазон номеров страниц. В этом случае закачка всех страниц будет представлять собой простой цикл, в котором будут перебираться все номера страниц из этого диапазона.\n", 18 | "\n", 19 | "*Второй подход* обычно применяется в **краулерах** --- программах, которые обходят какой-то фрагмент интернета, собирая информацию с разных сайтов. Краулерами, например, пользуются поисковые системы, чтобы индексировать содержимое сайтов. Краулер начинает работу с одной или нескольких страниц, адреса которых задаются вручную, а затем переходит по всем ссылкам из этих страниц. Каждый раз, когда краулер загружает очередную страницу, он находит на ней не только нужную ему информацию, но и все ссылки, которые добавляются в очередь. Важно при этом помнить, где краулер уже побывал, чтобы не переходить по нескольку раз на одни и те же страницы. В настоящих краулерах применяют и другие ухищрения, например, чтобы выяснить, по каким ссылкам лучше переходить сначала, но мы этого касаться не будем.\n", 20 | "\n", 21 | "\n", 22 | "## Напоминание\n", 23 | "\n", 24 | "Ссылки в HTML задаются тэгом `a`, а сам адрес находится в атрибуте `href`. Ссылка обязательно должна начинаться с протокола (`http://`); если это не так, это означает, что ссылка указывает на другую страницу на том же сайте. При поиске ссылок в HTML нужно помнить, что между тэгом (`a`) и атрибутом (`href`) могут находиться другие атрибуты и любое количество пробелов.\n", 25 | "\n", 26 | "## Пример\n", 27 | "\n", 28 | "Чтобы было не так скучно, в этот раз мы будем тренироваться не на русском сайте, а на албанском. Допустим, мы хотим скачать для корпуса все посты с http://www.forumishqiptar.com --- главного албанского форума. С ходу трудно разобраться, что там к чему, но если присмотреться, станет понятно, что перед нами список тем. Нажав на какую-нибудь тему, мы попадём на страницу со списком тредов, а нажав на тред, обнаружим страницу с постами. Это, видимо, и есть та страница нижнего уровня, которые мы хотим скачивать.\n", 29 | "\n", 30 | "Посмотрим на адреса нескольких таких страниц. Выглядят они примерно так:\n", 31 | "\n", 32 | "http://www.forumishqiptar.com/threads/79403-%C3%87far%C3%AB-%C3%ABsht%C3%AB-dashuria\n", 33 | "http://www.forumishqiptar.com/threads/41551-P%C3%ABrkufizimi-i-dashuris%C3%AB%21%21\n", 34 | "http://www.forumishqiptar.com/threads/160424-Mos-luaj-me-dashurin\n", 35 | "\n", 36 | "Видно, что у них общее начало и у каждой страницы есть свой номер. Номера легко можно было бы перебрать, но кроме номера, в адресе стоит ещё и заголовок, который нам заранее неизвестен. Одним из решений было бы загрузить сначала страницы с темами и найти в них полные ссылки на треды. Однако мы применим хитрость, которая срабатывает в 90% случаев вроде этого. Попробуем взять адрес какой-нибудь страницы, вручную убрать в нём всё после числа и ввести это в адресную строку браузера. Вуаля! Всё работает и без заголовка (точнее, происходит автоматическое перенаправление). Это значит, что мы можем пользоваться перебором номеров. Для этого нам достаточно узнать диапазон --- номера самой первой и самой последней (по времени) страницы. Это мы оставляем в качестве упражнения читателю. В результате должно получиться что-то такое:\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": { 43 | "collapsed": false 44 | }, 45 | "outputs": [ 46 | { 47 | "name": "stdout", 48 | "output_type": "stream", 49 | "text": [ 50 | "Error at http://www.forumishqiptar.com/threads/160403\n", 51 | "Error at http://www.forumishqiptar.com/threads/160407\n", 52 | "Error at http://www.forumishqiptar.com/threads/160408\n", 53 | "Error at http://www.forumishqiptar.com/threads/160411\n", 54 | "Error at http://www.forumishqiptar.com/threads/160412\n", 55 | "Error at http://www.forumishqiptar.com/threads/160413\n", 56 | "Error at http://www.forumishqiptar.com/threads/160415\n", 57 | "Error at http://www.forumishqiptar.com/threads/160418\n" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "import urllib.request\n", 63 | "\n", 64 | "def download_page(pageUrl):\n", 65 | " try:\n", 66 | " page = urllib.request.urlopen(pageUrl)\n", 67 | " text = page.read().decode('ISO-8859-1')\n", 68 | " except:\n", 69 | " print('Error at', pageUrl)\n", 70 | " return\n", 71 | " # do something with the downloaded text\n", 72 | "\n", 73 | "commonUrl = 'http://www.forumishqiptar.com/threads/'\n", 74 | "for i in range(160400, 160425):\n", 75 | " pageUrl = commonUrl + str(i)\n", 76 | " download_page(pageUrl)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "В функции, отвечающей за загрузку, мы поместили собственно загрузку HTML в блок `try-except`. Это было сделано потому, что зачастую не всем номерам из допустимого диапазона соответствуют реальные страницы. Если страницы с таким адресом не существует, функция `urlopen` вызовет ошибку, которая благодаря `try-except` не вызовет падения всей программы.\n", 84 | "\n", 85 | "## Важный комментарий\n", 86 | "\n", 87 | "Когда Ваша программа выкачивает много страниц сразу, она создаёт нагрузку на сервер выкачиваемого сайта --- и эта нагрузка намного больше, чем нагрузка от обычного посещения сайта людьми. Если Вы выкачиваете содержимое крупного сайта с одного компьютера, то ничего страшного в этом, скорее всего, нет. Но если это не какой-то крупный ресурс, который владеет мощными серверами, и тем более если страницы с него скачивают несколько человек одновременно, это может создать реальные проблемы владельцам выкачиваемого ресурса. Поэтому, во-первых, нужно всегда выяснять, не доступно ли всё содержимое нужного Вам ресурса по отдельной ссылке (например, так обстоит дело с Википедией), а во-вторых, ставить между обращениями к серверу искуственный временной интервал хотя бы в пару секунд:\n" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 3, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [], 97 | "source": [ 98 | "import time\n", 99 | "\n", 100 | "time.sleep(2)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "## Как почистить текст\n", 108 | "\n", 109 | "В html странице, конечно, всегда много тэгов, скриптов и комментариев. А нам обычно бывает нужен только текст.\n", 110 | "Чтобы вытащить из html только текст, можно воспользоваться регулярными выражениями: " 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "metadata": { 117 | "collapsed": true 118 | }, 119 | "outputs": [], 120 | "source": [ 121 | "html_content = '....' # тут какой-то html\n", 122 | "\n", 123 | "regTag = re.compile('<.*?>', flags=re.U | re.DOTALL) # это рег. выражение находит все тэги\n", 124 | "regScript = re.compile('', flags=re.U | re.DOTALL) # все скрипты\n", 125 | "regComment = re.compile('', flags=re.U | re.DOTALL) # все комментарии\n", 126 | "\n", 127 | "# а дальше заменяем ненужные куски на пустую строку\n", 128 | "clean_t = regScript.sub(\"\", t)\n", 129 | "clean_t = regComment.sub(\"\", clean_t)\n", 130 | "clean_t = regTag.sub(\"\", clean_t)" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "Нужно не забывать, что для отображения на html-странице символов, которых нет на клавиатуре, применяются специальные последовательности символов, начинающиеся с амперсанда (&) и заканчивающиеся точкой с запятой (;). Чтобы получить текст не с такими последовательностями, а с нормальными символами, используется специальная функция в питоне `unescape`:" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 4, 143 | "metadata": { 144 | "collapsed": false 145 | }, 146 | "outputs": [ 147 | { 148 | "name": "stdout", 149 | "output_type": "stream", 150 | "text": [ 151 | "Петя & Вася\n" 152 | ] 153 | } 154 | ], 155 | "source": [ 156 | "# если у вас Python3.4+\n", 157 | "import html\n", 158 | "test_string = 'Петя & Вася'\n", 159 | "print( html.unescape(test_string) )" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 10, 165 | "metadata": { 166 | "collapsed": false 167 | }, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "Специальные символы: \" « » ♠ ♥ ♣ ♦ и так далее\n" 174 | ] 175 | } 176 | ], 177 | "source": [ 178 | "print( html.unescape('Специальные символы: " « » ♠ ♥ ♣ ♦ и так далее') )" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": null, 184 | "metadata": { 185 | "collapsed": true 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "# если у вас Python3.3 или более ранняя версия \n", 190 | "import html.parser \n", 191 | "html.parser.HTMLParser().unescape('Петя & Вася')" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "## Задание\n", 199 | "\n", 200 | "Вообще-то в длинных постах бывает по многу страниц, например, как тут:\n", 201 | "http://www.forumishqiptar.com/threads/79403\n", 202 | "Нужно написать код, который умеет скачивать все страницы треда, а не только первую." 203 | ] 204 | } 205 | ], 206 | "metadata": { 207 | "celltoolbar": "Raw Cell Format", 208 | "kernelspec": { 209 | "display_name": "Python 3", 210 | "language": "python", 211 | "name": "python3" 212 | }, 213 | "language_info": { 214 | "codemirror_mode": { 215 | "name": "ipython", 216 | "version": 3 217 | }, 218 | "file_extension": ".py", 219 | "mimetype": "text/x-python", 220 | "name": "python", 221 | "nbconvert_exporter": "python", 222 | "pygments_lexer": "ipython3", 223 | "version": "3.4.3" 224 | } 225 | }, 226 | "nbformat": 4, 227 | "nbformat_minor": 0 228 | } 229 | -------------------------------------------------------------------------------- /3 Семинар - Mystem.md: -------------------------------------------------------------------------------- 1 | # Семинар 3: Mystem 2 | 3 | ![Mystem](https://github.com/elmiram/2016learnpython/blob/master/img/one_does_not_mystem.jpg) 4 | 5 | ## Запуск программ из командной строки 6 | 7 | Кроме программ, которые имеют GUI (графический, оконный интерфейс), часто приходится использовать такие программы, которые управляются из командной строки. Ещё она называется *терминалом* или *консолью*. 8 | 9 | Для того, чтобы запустить командную строку, в Windows нужно найти среди других программу **cmd**. 10 | 11 | В начале того, что показывает интерфейс командной строки, обычно написано, какое место в системе программа считает рабочим. Например, `C:\Users\student`. После этого обычно мы видим знак т.н. *приглашения* командной строки, то есть сигнал того, что программа готова к работе и ждёт действий от пользователя. В Windows это обычно знак *больше*, `>`. 12 | 13 | С помощью набора команд, которые отправляются на исполнение с помощью клавиши `Enter`, можно передвигаться по диску и запускать другие программы. 14 | 15 | Запуск программ обычно выглядит так. Сначала нужно написать путь к запускаемой программе: `C:\some_prog.exe`. Путь может быть и абсолютным, и относительным. Считается он относительно того места, которое показывается перед приглашением. 16 | 17 | Иногда программы нужно запускать не сами по себе, а с определёнными *аргументами* или ещё их называют *параметрами* (похоже на аргументы функции в питоне). Тогда эти аргументы пишутся после пути к программе через пробел. Если сам аргумент тоже содержит пробел, его нужно обернуть в кавычки: `C:\some_prog.exe argument1 "argument 2"` 18 | 19 | Кроме того, среди таких аргументов есть такие, которые принято называть *опциями* или *ключами*, они начинаются с символа дефиса: `C:\some_prog.exe -a -b`. Их можно "склеивать": `C:\some_prog.exe -ab`. 20 | 21 | Какие у программы возможны аргументы и опции, в каком порядке они должны идти при запуске из командной строки, обычно описано в документации к программам. 22 | 23 | Через командную строку можно, например, запускать скрипты на питоне. Тогда запускаемой программой будет интерпретатор питона, а аргументом -- путь к собственно скрипту: `C:\Python34\python.exe C:\myscript.py` 24 | 25 | ## Mystem 26 | 27 | Mystem - это свободно распространяемый морфологический анализатор для русского языка с закрытым исходным кодом. То есть мы можем его бесплатно скачать с сайта и пользоваться им, но не можем посмотреть, что у него внутри и как оно работает. 28 | 29 | Mystem был придуман одним из создателей Яндекса Ильёй Сегаловичем. Некоторый потомок mystem'а до сих пор работает внутри большого поисковика Яндекса, анализируя слова при поиске. 30 | 31 | My-stem значит my stemmer, стемминг -- это разбиение формы на основу и флексию. Но на самом деле Mystem может гораздо больше: устанавливать словарную форму слова, определять часть речи и грамматическую форму слова. В последних версиях Mystem умеет и выбирать из нескольких возможных грамматических разборов один, наиболее верный. 32 | 33 | У Mystem нет графического оконного интерфейса, запустить его можно только из командной строки. 34 | 35 | Скачать Mystem можно [отсюда](https://tech.yandex.ru/mystem/), а [тут](https://tech.yandex.ru/mystem/doc/index-docpage/) лежит его документация. 36 | 37 | Mystem не требует специальной инсталляции в систему. Достаточно, чтобы исполняемый файл с программой, подходящей для вашей версии операционной системы, находился на вашем компьютере. 38 | 39 | ## Как читать документацию Mystem 40 | 41 | [На странице документации](https://tech.yandex.ru/mystem/doc/index-docpage/) описаны разные возможности вызова Mystem из командной строки. В зависимости от параметров, с которыми мы вызовем программу, мы получим разный результат. 42 | 43 | Примеры, которые там приведены, рассчитаны на пользователя Unix-подобной операционной системы. То есть не Windows. 44 | 45 | В начале примеров вызова стоит знак доллара, $. Это не значит, что его вам тоже нужно набирать, если вы хотите воспроизвести эти примеры. Доллар -- это аналог приглашения командной строки, просто не в Windows, а в Unix-подобных системах. Если вы берете примеры вызова со страницы документации за основу, игнорируйте знак доллара. 46 | 47 | В документации написано: `$ mystem input`. На практике для пользователей Windows это будет значить что-то вроде `C:\mystem.exe input.txt`. 48 | 49 | В документации написано "стандартный ввод" и "стандартный вывод", это значит то, что вводится в командной строке или выводится в тот же терминал. Если не используются стандартный ввод и вывод, то используются файлы (выводной файл Mystem способен создать сам). 50 | 51 | В 3-й версии Mystem кодировка по умолчанию -- utf-8. В первых версиях -- cp1251. Кодировка по умолчанию в командной строке Windows -- cp866. Из-за этого Mystem может не понимать слова, которые попадают к нему из стандартного ввода. 52 | 53 | ## Как запускать Mystem 54 | 55 | Об этом довольно хорошо и подробно написано [на странице документации](https://tech.yandex.ru/mystem/doc/index-docpage/), её нужно внимательно изучить и попробовать разные вариант опций. 56 | 57 | Особое внимание нужно уделить опции `-d`, она заставляет анализатор выбирать только один разбор из возможных. При этом выбор происходит только между разными частями речи. Если у одной части речи возможны разные разборы (например, разные падежи одного и того же существительного), то эти разборы не отбрасываются. Иначе говоря, Mystem снимает только частеречную омонимию. Омонимию форм он не снимает. 58 | 59 | ## Как запускать Mystem из питона? 60 | 61 | Для запуска сторонних программ, имеющих интерфейс командной строки, в питоне есть system, предоставляемая модулем os: `os.system("C:\mystem.exe input.txt output.txt")`. Перед добавлением такой конструкции в код, необходимо импортировать модуль os. 62 | 63 | Это нужно, чтобы, например, обработать много файлов циклом: 64 | 65 | ```python 66 | import os 67 | inp = "input_texts" 68 | lst = os.listdir(inp) 69 | for fl in lst: 70 | os.system(r"C:\mystem.exe " + inp + os.sep + fl + " output_texts" + os.sep + fl) 71 | ``` 72 | 73 | Этот код берёт из директории *input_texts* все лежащие в ней файлы, отдаёт на разметку майстему и кладёт результат в соседнюю директорию *output_texts*. 74 | 75 | Есть возможность запускать mystem и с помощью специального модуля, **pymystem3** (и некоторых других), который нужно специально установить (его нет в стандартной сборке питона). Это проще и удобнее, потому что с тем, что выдаёт mystem, можно сразу работать как с питоновскими структурами данных. Но медленнее. Иногда гораздо-гораздо медленнее, чем разметить один файл mystem'ом сразу. 76 | 77 | ## Задание 78 | 79 | Скачать mystem и попробовать разные опции его запуска на практике. Посмотреть, чем результат вызова с одними опциями отличается от результата вызова с другими опциями. 80 | 81 | 82 | -------------------------------------------------------------------------------- /4 Семинар - про проект!.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Собери корпус из газет\n", 8 | "\n", 9 | "## Про проект\n", 10 | "Итак, уже скоро мы завершаем марафон \"Собери корпус из газет\". Через 2 недели, 10-го октября (в 23:59), в репозитории должен лежать готовый код, который умеет делать всё, что перечислено ниже, а также в файле Readme.md в папке с проектом должна находиться ссылка на скачанные и обработанные файлы вашей газеты, лежащие **одним архивом** на Яндекс.Диске, Google Drive, Облаке@Мэйл.ру или на чём угодно подобном (но, пожалуйста, не надо заливать эти архивы на гитхаб).\n", 11 | "\n", 12 | "При этом \n", 13 | "\n", 14 | "1. Общий объём скачанных текстов должен быть не меньше 100 тыс. слов. \n", 15 | "2. В именах файлов не должно быть не-ascii символов.\n", 16 | "3. Структура каталогов, в которых хранятся файлы, должна быть следующей: корень/год/месяц/файлы с материалами за этот месяц\n", 17 | "4. В корне также должна лежать csv-таблица с такими полями (разделитель - знак табуляции):" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": { 24 | "collapsed": true 25 | }, 26 | "outputs": [], 27 | "source": [ 28 | "path\tauthor\tsex\tbirthday\theader\tcreated\tsphere\tgenre_fi\ttype\ttopic\tchronotop\tstyle\taudience_age\taudience_level\taudience_size\tsource\tpublication\tpublisher\tpubl_year\tmedium\tcountry\tregion\tlanguage" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "Имена полей означают следующее:\n", 36 | "\n", 37 | "**path** -- это путь к чистому неразмеченному файлу со статьёй,
\n", 38 | "**author** -- имя автора, если его можно достать со страницы (и вообще если оно есть),
\n", 39 | "**sex** -- поле оставить пустым,
\n", 40 | "**birthday** -- оставить пустым
\n", 41 | "**header** -- название статьи
\n", 42 | "**created** -- дата в формате 07.01.2012 (день.месяц.год)
\n", 43 | "**sphere** -- слово \"публицистика\"
\n", 44 | "**genre_fi** -- оставить пустым
\n", 45 | "**type** -- оставить пустым
\n", 46 | "**topic** -- категория, если мы её можем найти на странице со статьёй
\n", 47 | "**chronotop** -- оставить пустым
\n", 48 | "**style** -- слово \"нейтральный\"
\n", 49 | "**audience_age** -- слово \"н-возраст\"
\n", 50 | "**audience_level** -- слово \"н-уровень\"
\n", 51 | "**audience_size** -- \"районная\", если газета районная, \"республиканская\", если газета республиканская, \"городская\" -- если городская
\n", 52 | "**source** -- URL, откуда статья была скачана
\n", 53 | "**publication** -- название газеты
\n", 54 | "**publisher** -- оставить пустой
\n", 55 | "**publ_year** -- год публикации
\n", 56 | "**medium** -- слово \"газета\"
\n", 57 | "**country** -- слово \"Россия\"
\n", 58 | "**region** -- Название региона, откуда ваша газета
\n", 59 | "**language** -- слово \"ru\"
\n", 60 | "\n", 61 | "Таким образом, со страницы со статьей нужно еще извлечь, если возможно, имя автора, дату публикации, название публикации, приписанные категории.\n", 62 | "\n", 63 | "Кроме того, коллекция текстов с сайта газеты должна быть представлена в трёх видах (каждый вид в отдельной папке):\n", 64 | "\n", 65 | "1. Неразмеченный текст. \n", 66 | "\n", 67 | " В нём перед текстом должны быть такие строки (после собаки и примыкающего к ней слова через пробел нужно написать релевантную информацию):\n", 68 | "\n", 69 | " @au имя автора (если автора нет, пишем Noname)
\n", 70 | " @ti Название статьи
\n", 71 | " @da дата в формате 12.02.2012
\n", 72 | " @topic категория, если мы её можем найти на странице со статьёй
\n", 73 | " @url URL, откуда мы скачали страницу

\n", 74 | "\n", 75 | "2. Размеченный mystem текст в формате XML\n", 76 | "\n", 77 | "3. Размеченный mystem текст в формате plain text\n", 78 | "\n", 79 | "\n", 80 | "Соответственно, сама программа (в одном или в нескольких файлах) должна уметь:\n", 81 | "\n", 82 | "1. Скачивать страницы с выбранного сайта, обходя его по принципу краулера. При этом программа не должна заходить на одни и те же страницы несколько раз.\n", 83 | "2. Извлекать со страниц информацию для метатаблицы (если она доступна) и сам текст.\n", 84 | "3. Раскладывать скачанные тексты по папкам.\n", 85 | "4. Вызывать mystem и делать морфологическую разметку текста (таким образом, чтобы каждому слову была присвоена информация о лемме и грамматическая информация с учётом контекстно снятой омонимии).\n", 86 | "\n", 87 | "\n", 88 | "## Про каталог еще раз\n", 89 | "\n", 90 | "Каталог должен выглядеть вот так:" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": { 97 | "collapsed": true 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "📂газета\n", 102 | "|\n", 103 | "|____ metadata.csv\n", 104 | "|\n", 105 | "|____📂plain\n", 106 | "| |\n", 107 | "| |____📂2016\n", 108 | "| | |\n", 109 | "| | |____📂1\n", 110 | "| | | |\n", 111 | "| | | |____ статья1.txt\n", 112 | "| | | |\n", 113 | "| | | |____ статья2.txt\n", 114 | "| | |\n", 115 | "| | |____📁2\n", 116 | "| |\n", 117 | "| |____📁2015\n", 118 | "|\n", 119 | "|____📂mystem-xml
\n", 120 | "| |\n", 121 | "| |____📂2016\n", 122 | "| | |\n", 123 | "| | |____📂1\n", 124 | "| | | |\n", 125 | "| | | |____ статья1.xml\n", 126 | "| | | |\n", 127 | "| | | |____ статья2.xml\n", 128 | "| | |\n", 129 | "| | |____📁2\n", 130 | "| |\n", 131 | "| |____📁2015\n", 132 | "|\n", 133 | "|____📂mystem-plain\n", 134 | " |\n", 135 | " |____📂2016\n", 136 | " | |\n", 137 | " | |____📂1\n", 138 | " | | |\n", 139 | " | | |____ статья1.txt\n", 140 | " | | |\n", 141 | " | | |____ статья2.txt\n", 142 | " | |\n", 143 | " | |____📁2\n", 144 | " |\n", 145 | " |____📁2015" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "## Про устройство программы\n", 153 | "\n", 154 | "Подумаем, как может быть устроена ваша программа.\n", 155 | "\n", 156 | "**Путь первый** \n", 157 | "\n", 158 | "Для каждой задачи написать отдельную функцию. Написать главную функцию, которая запускает программу.\n", 159 | "\n", 160 | "\n", 161 | "**Путь второй**\n", 162 | "\n", 163 | "Можно для каждой логической группы функций создать отдельный файл, например, файл для функций, работающих с майстемом, файл для функций, выкачивающих газетные страницы и т.д. Создать главный файл, в котором прописана основная логика программы. В этот главный файл импортируются все остальные функции.\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "## Про папки\n", 171 | "\n", 172 | "Вспомним, как на питоне работать с папками. Для этого используется модуль `os`.\n", 173 | "\n", 174 | "Создать папку: `os.mkdir(dirname)` или `os.makedirs(dirname)`. `mkdir` создает директорию и вызывает ошибку, если она существует. `makedirs` создает директории и все несуществующие директории в указанном пути (например, если в `dirname` указан путь `./dir1/dir2/dir3`, и при этом dir1 и dir2 не существуют, то функция их создаст).\n", 175 | " \n", 176 | "Проверить, существует ли папка: `os.path.exists(dirname)` (возвращает `True` или `False`).\n", 177 | "Пройтись по каталогу: `os.walk(dirname)`.\n", 178 | "\n", 179 | "Вот такой маленький сниппет, возможно, вам пригодится:" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": { 186 | "collapsed": true 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "import os\n", 191 | "\n", 192 | "if not os.path.exists(directory):\n", 193 | " os.makedirs(directory)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "## Как просто вставить куски строки в шаблон\n", 201 | "\n", 202 | "Для такого форматирования можно использовать оператор %. \n", 203 | "\n", 204 | "`%s` в строке значит, что в это место вставится строка. `%d` значит, что в это место вставится число. Эти последовательности пишутся в том месте строки, куда нужно что-то вставить. \n", 205 | "\n", 206 | "После строки пишется знак `%` и затем кортеж (в круглых скобочках) из элементов, которые нужно вставить:" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 8, 212 | "metadata": { 213 | "collapsed": false 214 | }, 215 | "outputs": [ 216 | { 217 | "name": "stdout", 218 | "output_type": "stream", 219 | "text": [ 220 | "тут ссылка\tПетя\t\t\tНазвание статьи\t26.09.2016\tпублицистика\t\t\tобразование\t\tнейтральный\tн-возраст\tн-уровень\tрайонная(если районная)\turl\tназвание газеты\t\t2016\tгазета\tРоссия\tкакой-то регион\tru\n" 221 | ] 222 | } 223 | ], 224 | "source": [ 225 | "row = '%s\\t%s\\t\\t\\t%s\\t%s\\tпублицистика\\t\\t\\t%s\\t\\tнейтральный\\tн-возраст\\tн-уровень\\tрайонная(если районная)\\t%s\\tназвание газеты\\t\\t%s\\tгазета\\tРоссия\\tкакой-то регион\\tru'\n", 226 | "\n", 227 | "print(row % ('тут ссылка', 'Петя', 'Название статьи', '26.09.2016', 'образование', 'url', '2016'))" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 9, 233 | "metadata": { 234 | "collapsed": false 235 | }, 236 | "outputs": [ 237 | { 238 | "name": "stdout", 239 | "output_type": "stream", 240 | "text": [ 241 | "тут другая ссылка\tАвтор\t\t\tДругая статья\t27.09.2016\tпублицистика\t\t\tспорт\t\tнейтральный\tн-возраст\tн-уровень\tрайонная(если районная)\turl2\tназвание газеты\t\t2016\tгазета\tРоссия\tкакой-то регион\tru\n" 242 | ] 243 | } 244 | ], 245 | "source": [ 246 | "print(row % ('тут другая ссылка', 'Автор', 'Другая статья', '27.09.2016', 'спорт', 'url2', '2016'))" 247 | ] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "## Задания\n", 254 | "\n", 255 | "\n", 256 | "### Задание 1\n", 257 | "\n", 258 | "Придумать любой словарь, например, Страна -> (столица, население) или Имя -> (город проживания, возраст).\n", 259 | "\n", 260 | "Распечатать словарь, используя форматирование с %.\n", 261 | "\n", 262 | "\n", 263 | "### Задание 2\n", 264 | "\n", 265 | "Для каждого элемента словаря из задания 1 создать папку. Внутри этой папки создать файл, в котором будет записано значение соответствующего элемента в словаре.\n", 266 | "\n", 267 | "Например, у вас есть в словаре Россия -> (Москва, 7000000). Тогда нужно создать папку \"Россия\", а в ней файл с любым названием, содержащий запись \"Москва, 7000000\".\n", 268 | "\n", 269 | "\n", 270 | "### Задание 3\n", 271 | "\n", 272 | "Вспомните адрес сайта своей газеты. =В \n", 273 | "\n", 274 | "Напишите программу, которая извлекает со страницы газетной статьи метаданные: \n", 275 | "\n", 276 | "1. Название статьи\n", 277 | "2. Имя автора (если указано)\n", 278 | "3. Дату публикации\n", 279 | "4. Категории (если есть)\n", 280 | "\n", 281 | "Для начала можно скачать html-код какой-то статьи и попробовать вытаскивать информацию из него. А потом убедитесь, что программа не падает на разных статьях." 282 | ] 283 | } 284 | ], 285 | "metadata": { 286 | "kernelspec": { 287 | "display_name": "Python 3", 288 | "language": "python", 289 | "name": "python3" 290 | }, 291 | "language_info": { 292 | "codemirror_mode": { 293 | "name": "ipython", 294 | "version": 3 295 | }, 296 | "file_extension": ".py", 297 | "mimetype": "text/x-python", 298 | "name": "python", 299 | "nbconvert_exporter": "python", 300 | "pygments_lexer": "ipython3", 301 | "version": "3.4.3" 302 | } 303 | }, 304 | "nbformat": 4, 305 | "nbformat_minor": 0 306 | } 307 | -------------------------------------------------------------------------------- /6 Семинар - Запросы и формы.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Запросы и формы\n", 8 | "\n", 9 | "## HTTP-запросы\n", 10 | "\n", 11 | "Вспомним первый семинар:\n", 12 | "\n", 13 | "> На самом деле, когда мы хотим открыть какую-то страницу в интернете, наш браузер отправляет на сервер запрос (\"Привет, сервер! я хочу код страницы по вот такому адресу!\"), а сервер затем отправляет ответ (\"Привет! Вот код страницы: ...\"). \n", 14 | "\n", 15 | "Такие запросы мы обычно отправляем по протоколу HTTP (протокол - просто набор правил отправки запроса). При отправке запроса нужно сказать серверу, как ему стоит обаботать наш запрос -- каким __методом__. Методы бывают разные, а сейчас мы разберем два основных: GET и POST.\n", 16 | "\n", 17 | "GET\n", 18 | "\n", 19 | "Этот метод является одним из самых распространенных и предназначен для получения требуемой информации и передачи данных в адресной строке. Пары «имя=значение» присоединяются в этом случае к адресу после вопросительного знака и разделяются между собой амперсандом (символ &). Удобство использования метода get заключается в том, что адрес со всеми параметрами можно использовать неоднократно, сохранив его, например, в закладки браузера, а также менять значения параметров прямо в адресной строке.\n", 20 | "\n", 21 | "POST\n", 22 | "\n", 23 | "Метод post посылает на сервер данные в запросе браузера. Это позволяет отправлять большее количество данных, чем доступно методу get, плюс эти данные можно скрывать. Большие объемы данных используются в форумах, почтовых службах, заполнении базы данных, при пересылке файлов и др.\n", 24 | "\n", 25 | "\n", 26 | "## HTML-формы\n", 27 | "\n", 28 | "В коде HTML-страницы можно написать веб-формы, которые генерируют запросы к какому-то ресурсу и отправляют их, используя какой-то метод.\n", 29 | "\n", 30 | "Чтобы создать форму используется тэг `
`. У тэга `` есть атрибуты:\n", 31 | "\n", 32 | "* `action` - кому (какой программе) мы отправляем запрос\n", 33 | "* `method` - какой метод мы используем - GET|POST\n", 34 | "\n", 35 | "Для того, чтобы пользователь мог ввести в форму какие-то данные, используется тэг ``. У тэга `` есть атрибуты:\n", 36 | "\n", 37 | "* `name` - имя поля,\n", 38 | "* `value` - значение поля,\n", 39 | "* `type` - какой тип ввода мы используем. Тип может быть такой:\n", 40 | " * `text` - короткое текстовое поле.\n", 41 | " * `textarea` - большое текстовое поле.\n", 42 | " * `checkbox` - выбор с чекбоксами, которые можно отмечать галочками (можно все или несколько)\n", 43 | " * `radio` - выбор с радио-кнопками, из которых можно выбрать только одну.\n", 44 | " * `submit` - кнопка для отправки формы на сервер.\n", 45 | "\n", 46 | "\n", 47 | "## Примеры\n", 48 | "\n", 49 | "__Вот минимальный пример:__\n", 50 | "\n", 51 | " \n", 52 | " Как вас зовут? \n", 53 | " \n", 54 | "
\n", 55 | " \n", 56 | "\n", 57 | "Когда форма отправляется на сервер, управление данными передается программе, заданной атрибутом `action` тега `
`. Предварительно браузер подготавливает информацию в виде пары «имя=значение», где имя определяется атрибутом `name` тега ``, а значение введено пользователем или установлено в поле формы по умолчанию. Если для отправки данных используется метод GET, то адресная строка будет выглядеть примерно так:\n", 58 | "\n", 59 | "https://github.com/elmiram/2016learnpython/6%20Seminar.ipynb?username=Petya\n", 60 | "\n", 61 | "\n", 62 | "__Вот пример c радио-кнопками:__\n", 63 | "\n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " Кого вы больше любите?\n", 68 | " \n", 69 | " \n", 70 | "\n", 71 | " \n", 72 | "

Как вас зовут?

\n", 73 | "

Кого вы больше любите?

\n", 74 | " Котиков
\n", 75 | " Собачек
\n", 76 | " Птичек
\n", 77 | " Слоников
\n", 78 | "

\n", 79 | "
\n", 80 | "\n", 81 | " \n", 82 | " \n", 83 | "

\n", 84 | " \n" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | " \n", 92 | "__Вот пример c чекбоксами:__\n", 93 | "\n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " Чем вы любите заниматься\n", 98 | " \n", 99 | " \n", 100 | "\n", 101 | "
\n", 102 | "

Как вас зовут?

\n", 103 | "

Сколько вам лет?

\n", 104 | "

Чем вы любите заниматься?

\n", 105 | " Спать
\n", 106 | " Читать книжки
\n", 107 | " Программировать
\n", 108 | " Гамать
\n", 109 | " Писать научные статьи
\n", 110 | " Придумывать лингвистические эксперименты
\n", 111 | " Кушать вкусняшки
\n", 112 | "

\n", 113 | "
\n", 114 | " \n", 115 | " \n", 116 | " \n" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "## Задания\n", 124 | "\n", 125 | "Посмотреть, как работает поиск в гугле, яндексе, яндекс-картах и НКРЯ. Написать веб-страницу, с помощью которой можно искать слово там, там, там или там. \n", 126 | "\n", 127 | "(Если будем успевать)\n", 128 | "Псмотреть, какие запросы отправляет какой-нибудь поисковик, когда ищет подсказки к введённым пользователем буквам, какие ответы он получает (там будет json), и написать программу, которая сама будет выдавать такие подсказки." 129 | ] 130 | } 131 | ], 132 | "metadata": { 133 | "kernelspec": { 134 | "display_name": "Python 3", 135 | "language": "python", 136 | "name": "python3" 137 | }, 138 | "language_info": { 139 | "codemirror_mode": { 140 | "name": "ipython", 141 | "version": 3 142 | }, 143 | "file_extension": ".py", 144 | "mimetype": "text/x-python", 145 | "name": "python", 146 | "nbconvert_exporter": "python", 147 | "pygments_lexer": "ipython3", 148 | "version": "3.4.3" 149 | } 150 | }, 151 | "nbformat": 4, 152 | "nbformat_minor": 0 153 | } 154 | -------------------------------------------------------------------------------- /7 Семинар - flask intro.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# Пишем сайты на питоне\n", 10 | "\n", 11 | "## Вспоминаем, как работает интернет\n", 12 | "\n", 13 | "__Веб-сервер__\n", 14 | "* устройство vs. программа\n", 15 | "* задача — получать запросы от других компьютеров или программ (клиентов, clients) и отправлять запрошенные данные\n", 16 | "* основная функция — размещение сайтов (website hosting)\n", 17 | "\n", 18 | "__Термины__\n", 19 | "* *Запрос (request)* — сообщение от агента, желающего получить или разместить информацию\n", 20 | "* *Ответ (response)* — ответное сообщение сервера с кодом состояния и информацией (HTML, изображения, загружаемые файлы и т. п.)\n", 21 | "* *Протокол (protocol)* — набор правил, по которым составляются запросы и ответы \n", 22 | "* *Сессия (session)* — установка соединения между агентом и сервером и последующая серия запросов и ответов\n", 23 | "* *Гипертекст* — множество текстов, организованных в виде графа при помощи гиперссылок\n", 24 | "* *Протокол HTTP (HyperText Transfer Protocol)* — протокол для передачи гипертекстовых данных (обычно в виде HTML)\n", 25 | "* *URL (Uniform Resource Locator)*, веб-адрес ресурса — строка, представляющая собой уникальное имя, по которому можно найти в сети этот ресурс\n", 26 | "\n", 27 | "__Адресация__\n", 28 | "\n", 29 | "IP\n", 30 | "* *IP-адрес* — (пока что) набор из 4 байт, присваиваемый каждому подключённому к сети устройству\n", 31 | "* Некоторые IP-адреса уникальны, некоторые — нет (внутренние адреса в локальных сетях)\n", 32 | "* Практически любой ресурс (например, сайт) можно получить по его IP-адресу (например, через браузер)\n", 33 | "* Существуют зарезервированные адреса и диапазоны адресов, например, `127.0.0.1` — адрес данного устройства\n", 34 | "\n", 35 | "Порт\n", 36 | "* Каждый запрос обращается не просто к какому-то IP-адресу, а к некоторому порту на этом адресе\n", 37 | "* Веб-сервер имеет 65 535 портов, пронумерованных начиная с 1\n", 38 | "* Веб-сервер может прослушивать некоторые порты (listen to ports) и по-разному обрабатывать сообщения, поступившие на разные порты\n", 39 | "* Если порт не прослушивается, сообщения на этот порт останутся без ответа\n", 40 | "\n", 41 | "\n", 42 | "__URL__\n", 43 | "\n", 44 | "http__://__www.example.com__:__1234__/__directory1/file1.html\n", 45 | "\n", 46 | "протокол__://__доменное имя или IP-адрес__:__порт__/__адрес файла на сервере\n", 47 | "\n", 48 | "* Порт указывать не обязательно: используются стандартные порты (HTTP — 80, FTP — 20 и т. п.)\n", 49 | "* *DNS (Domain Name Servers)* — специальные сервера в сети, на которых хранятся таблицы соответствия между доменными именами и IP-адресами их серверов\n", 50 | "\n", 51 | "\n", 52 | "__HTML и скрипты__\n", 53 | "* страницы, содержимое (HTML-код) которых не изменяется, называются *статическими (static)*\n", 54 | "* страницы, содержимое которых может быть разным в зависимости от введённых пользователем данных, времени, IP-адреса и т. п., называются *динамическими (dynamic)*\n", 55 | "* динамические страницы создаются с помощью скриптов *на стороне сервера (server side scripts)*, написанных, например, на PHP или Python\n", 56 | "* скрипт порождает HTML и посылает его пользователю\n", 57 | "* пользователь не видит кода скрипта, выполняющегося на сервере\n", 58 | "* *скрипт на стороне клиента (client side script)* — вставка в HTML на каком-то языке программирования (например, JavaScript), позволяющая странице вести себя интерактивно\n", 59 | "* код клиентских скриптов посылается клиенту сервером вместе с HTML и не выполняется на сервере\n", 60 | "\n", 61 | "__Питон и сайты__\n", 62 | "\n", 63 | "Написать сайт на питоне значит написать такую программу, которая может работать веб-сервером или использоваться веб-сервером для порождения HTML-кода веб-страниц. Для этого существует несколько модулей, например, Django и Flask\n", 64 | "\n", 65 | "## flask\n", 66 | "\n", 67 | "### Intro\n", 68 | "\n", 69 | "* Программа, написанная с помощью flask, работает как веб-сервер\n", 70 | "* За каждую веб-страницу сайта отвечает какая-то функция, которая возвращает HTML- код этой страницы\n", 71 | "* Естественно, писать длинные куски HTML-кода внутри программы на питоне было бы странно, поэтому функции могут загружать HTML из заранее заготовленных файлов\n", 72 | "* Эти файлы могу содержать готовые страницы или шаблоны страниц на специальном языке\n", 73 | "\n", 74 | "Пример готового сайта на flask:" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": { 81 | "collapsed": true 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "from flask import Flask\n", 86 | "\n", 87 | "app = Flask(__name__)\n", 88 | "\n", 89 | "@app.route('/')\n", 90 | "def index():\n", 91 | " return '

Hello, world!

'\n", 92 | "\n", 93 | "if __name__ == '__main__':\n", 94 | " app.run(debug=True)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "Каждая страница сайта порождается какой-то функцией. Декоратор `@app.route(...)` перед функцией показывает, какой адрес будет у страницы, за которую отвечает эта функция:" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "collapsed": true 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "@app.route('/')\n", 113 | "def index():\n", 114 | " return 'Main page'\n", 115 | "\n", 116 | "@app.route('/hi')\n", 117 | "def hi():\n", 118 | " return 'Hi!'" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "Одна функция может отвечать за несколько разных страниц:\n", 126 | "* Во-первых, декораторов может стоять несколько подряд.\n", 127 | "* Во-вторых, декораторы могут содержать переменные.\n", 128 | "\n", 129 | "Пример:" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": { 136 | "collapsed": true 137 | }, 138 | "outputs": [], 139 | "source": [ 140 | "@app.route('/user/')\n", 141 | "def user_index(user):\n", 142 | " return 'This is the page of' + user" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "Переменные в адресах могут быть разного типа: `int` — целое число, `float` — действительное число, `path` — строка, которая может содержать слэши. Тип переменной можно указать вот так:" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": { 156 | "collapsed": true 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "import datetime\n", 161 | "\n", 162 | "@app.route('/time/')\n", 163 | "def time_page(shift):\n", 164 | " h = datetime.datetime.today().hour\n", 165 | " h += shift\n", 166 | " return 'Time in your country:' + str(h)" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "metadata": {}, 172 | "source": [ 173 | "Страницы часто содержат ссылки друг на друга, но чтобы поставить ссылку, нужно знать адрес. Чтобы узнать адрес страницы, которую создаёт какая-то функция, используется функция `url_for`:" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": { 180 | "collapsed": true 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "from flask import url_for\n", 185 | "\n", 186 | "@app.route('/functions/')\n", 187 | "def f_address(fname):\n", 188 | " return 'The address is ' + url_for(fname)" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "### Обратная связь\n", 196 | "(вспоминаем прошлый семинар)\n", 197 | "\n", 198 | "* Для обратной связи с сервером часто используются HTML-формы\n", 199 | "* Форма должна содержать кнопку `Submit`\n", 200 | "* Формы могут содержать текстовые поля, галочки и т. п.\n", 201 | "* Данные (значения элементов) из формы отправляются на сервер с помощью метода GET или POST\n", 202 | "* При использовании метода GET данные приписываются к URL после знака `?`\n", 203 | "\n", 204 | "__Еще раз про HTML-формы__" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": { 211 | "collapsed": true 212 | }, 213 | "outputs": [], 214 | "source": [ 215 | "
\n", 216 | " Имя:
\n", 217 | " Возраст:
\n", 218 | " Пароль:
\n", 219 | " студент\n", 220 | " \n", 221 | "
" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "При нажатии на кнопку `submit` и использовании GET на сервер отправляется запрос, URL которого содержит все значения параметров после `?`:\n", 229 | "\n", 230 | " www.example.com:5000/some_page.html?name=Petya&age=&student=on\n", 231 | "\n", 232 | "Почти все символы в таком URL, кроме латинских, будут закодированы с помощью percent encoding:\n", 233 | "\n", 234 | " name=%D0%A2%D0%B8%D0%BC%D0%BE%D1%84%D0%B5%D0%B9\n", 235 | "\n", 236 | "Чтобы использовать данные из формы в приложении flask, нужен объект `request`:\n", 237 | "* `request.method` — метод запроса\n", 238 | "* `request.args` — словарь со значениями аргументов из URL" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": { 245 | "collapsed": true 246 | }, 247 | "outputs": [], 248 | "source": [ 249 | "from flask import request\n", 250 | "@app.route('/login')\n", 251 | "def login():\n", 252 | " if request.args['password'] == '123':\n", 253 | " return 'Name: ' + request.args['login']\n" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": {}, 259 | "source": [ 260 | "\n", 261 | "### Шаблоны\n", 262 | "\n", 263 | "Динамические веб-страницы собираются из шаблонов: у шаблона есть постоянная часть и изменяющаяся часть. Flask позволяет хранить в файлах такие шаблоны страниц. __(!!!) Все шаблоны страниц должны находиться в папке `templates`.__\n", 264 | "\n", 265 | "Шаблон состоит из обычного html-кода. Но в этот обычный html могут быть вставлены специальные фрагменты кода, способного порождать разный html в зависимости от значений переменных. \n", 266 | "\n", 267 | "Эти специальные фрагменты пишутся внутри `{% ... %}` или `{{ ... }}`. При порождении HTML эти фрагменты заменяются на какой-то HTML-код по определённым правилам.\n", 268 | "\n", 269 | "Чтобы породить HTML по шаблону, используется функция `render_template()`:" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "metadata": { 276 | "collapsed": true 277 | }, 278 | "outputs": [], 279 | "source": [ 280 | "from flask import render_template\n", 281 | "@app.route('/hello/')\n", 282 | "@app.route('/hello/')\n", 283 | "def hello(name=None):\n", 284 | " return render_template('hello.html', name=name)" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "* Функция `render_template` заменяет все вставки в фигурных скобках на какой-то HTML.\n", 292 | "* Во вставках можно использовать переменные, которые передаются в шаблон через `render_template`.\n", 293 | "\n", 294 | "Есть два вида вставок: `{{ ... }}` и `{% ... %}`.\n", 295 | "\n", 296 | "Язык, на котором пишутся эти вставки, похож на питон, но многие питоновские функции в шаблонах работать не будут\n", 297 | "\n", 298 | "__Вставки `{{ ... }}`__\n", 299 | "\n", 300 | "* `{{ ... }}` означает «вычислить значение выражения в скобках и заменить им вставку»\n", 301 | "* в скобках может быть переменная, например, `{{ name }}`\n", 302 | "* к переменным в скобках __нельзя__ применять произвольные функции: `{{ f(name) }}`\n", 303 | "* существует набор встроенных операций, которые можно выполнять над переменными\n", 304 | "* эти операции отделяются от переменной знаком | и тогда выражение пишется так `{{ name|length }}`, примеры операций: \n", 305 | " * `length`\n", 306 | " * `lower`, `upper`\n", 307 | " * `e` — автоматически экранировать спецсимволы HTML\n", 308 | " * `random` — взять случайный элемент из массива\n", 309 | " * `urlencode` — закодировать строку с помощью percent encoding\n", 310 | "\n", 311 | "__Вставки `{% ... %}`__\n", 312 | "\n", 313 | "В таких `{% ... %}` скобках пишутся специальные команды, аналогичные питоновским `if` и `for`.\n", 314 | "Примеры:\n", 315 | "\n", 316 | "`{% for i in arr %} ... {% endfor %}`\n", 317 | "\n", 318 | "`{% if ... %} ... {% endif %}`\n", 319 | "\n", 320 | "`{% elif ... %}`\n", 321 | "\n", 322 | "`{% else %}`\n" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": null, 328 | "metadata": { 329 | "collapsed": true 330 | }, 331 | "outputs": [], 332 | "source": [ 333 | "{% if username|length > 20 %}\n", 334 | "

Слишком длинное имя!

\n", 335 | "{% else %}\n", 336 | "

{{ username }}

\n", 337 | "{% endif %}" 338 | ] 339 | }, 340 | { 341 | "cell_type": "markdown", 342 | "metadata": {}, 343 | "source": [ 344 | "# Можно посмотреть пример в репозитории в папке `flask_example`." 345 | ] 346 | } 347 | ], 348 | "metadata": { 349 | "kernelspec": { 350 | "display_name": "Python 3", 351 | "language": "python", 352 | "name": "python3" 353 | }, 354 | "language_info": { 355 | "codemirror_mode": { 356 | "name": "ipython", 357 | "version": 3 358 | }, 359 | "file_extension": ".py", 360 | "mimetype": "text/x-python", 361 | "name": "python", 362 | "nbconvert_exporter": "python", 363 | "pygments_lexer": "ipython3", 364 | "version": "3.4.3" 365 | } 366 | }, 367 | "nbformat": 4, 368 | "nbformat_minor": 0 369 | } 370 | -------------------------------------------------------------------------------- /8 Семинар - Снова flask.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Flask - продолжение\n", 8 | "\n", 9 | "## Словари в шаблонах\n", 10 | "\n", 11 | "Предположим, у нас есть словарь, в котором хранятся имена наших друзей и их почтовые адреса. И мы хотим выводить эти имена с адресами на нашем сайте. Мы можем написать что-то такое:" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "from flask import Flask\n", 23 | "from flask import render_template\n", 24 | "\n", 25 | "app = Flask(__name__)\n", 26 | "\n", 27 | "\n", 28 | "@app.route('/')\n", 29 | "def index():\n", 30 | " emailbook = {'Петя': 'petya@example.com',\n", 31 | " 'Вася': 'vasya@example.com',\n", 32 | " 'Катя': 'katya@example.com'}\n", 33 | " return render_template('index.html', emails=emailbook)\n", 34 | "\n", 35 | "if __name__ == '__main__':\n", 36 | " app.run()" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "Тогда в папке `templates` нам нужно создать файл `index.html`, в котором будут перебираться элементы словаря. Делается это с помощью функции `items()`. Вот так будет выглядеть `index.html`:" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": { 50 | "collapsed": true 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "\n", 55 | "\n", 56 | "\n", 57 | " \n", 58 | " Почтовые адреса\n", 59 | "\n", 60 | "\n", 61 | "

Адреса моих друзей

\n", 62 | "
    \n", 63 | " {% for name, email in emails.items() %}\n", 64 | "
  • {{ name }} - {{ email }}
  • \n", 65 | " {% endfor %}\n", 66 | "
\n", 67 | "\n", 68 | "" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "## Перенаправление\n", 76 | "\n", 77 | "Для того, чтобы направить пользователя на другую страницу, используется функция `redirect`. Например, вот приложение, в котором есть страница /time. С помощью функции `redirect` можно, например, реализовать такое:\n", 78 | "\n", 79 | "- если пользователь заходит на страницу /time в рабочее время (с 10 до 18), то он перенаправляется на главную страницу сайта,\n", 80 | "- если пользователь заходит на страницу в другое время, то он перенаправляется на страницу /hi." 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": { 87 | "collapsed": true 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "import datetime\n", 92 | "\n", 93 | "from flask import Flask\n", 94 | "from flask import url_for, render_template, request, redirect\n", 95 | "\n", 96 | "app = Flask(__name__)\n", 97 | "\n", 98 | "\n", 99 | "@app.route('/')\n", 100 | "def index():\n", 101 | " return '

Привет, мир!

'\n", 102 | "\n", 103 | "\n", 104 | "@app.route('/hi')\n", 105 | "@app.route('/hi/')\n", 106 | "def hi(user=None):\n", 107 | " if user is None:\n", 108 | " user = 'friend'\n", 109 | " return '

Привет, ' + user + '!

'\n", 110 | "\n", 111 | "\n", 112 | "@app.route('/time')\n", 113 | "def time_redirect():\n", 114 | " h = datetime.datetime.today().hour\n", 115 | " if 10 < h < 18:\n", 116 | " return redirect(url_for('index'))\n", 117 | " return redirect(url_for('hi'))\n", 118 | "\n", 119 | "\n", 120 | "if __name__ == '__main__':\n", 121 | " app.run(debug=True)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "Когда вы пишете сайт на фласке у себя на компьютере, можно написать вместо `app.run()` вот так: `app.run(debug=True)` (см. выше последнюю строчку). Это значит, что если на сайте возникнет ошибка, то на странице с ошибкой выведется ее описание, и вы легко сможете понять, в каком месте кода все падает. Но когда вы будете выкладывать сайт в Интернет, нужно убедиться, что `debug` отключен." 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "## Задание для тренировки\n", 136 | "\n", 137 | "Скачайте из репозитория [список языковых кодов](https://github.com/elmiram/2016learnpython/blob/master/data/lang_codes.csv). \n", 138 | "После этого нужно создать сайт, в котором:\n", 139 | "1. на главной странице выводится список языков и их кодов (`127.0.0.1:5000`),\n", 140 | "2. если зайти на страницу вида `127.0.0.1:5000/langs/какие-то_буквы`, то на этой странице выведется список только тех языков, коды которых начинаются с этих букв. Например, если зайти на `127.0.0.1:5000/codes/jp`, то выведется японский и еврейско-персидский, а если зайти на `127.0.0.1:5000/codes/j`, то японский, еврейско-персидский, еврейско-арабский и яванский.\n", 141 | "3. есть форма на странице `127.0.0.1:5000/form`, в которой есть поле \"Язык\". В это поле пользователь будет вводить название языка, например \"японский\". Когда пользователь нажимает кнопку \"Отправить\", \n", 142 | " * если введенного языка нет в списке, пользователь попадает на страницу `127.0.0.1:5000/not_found` с соответствующим сообщением,\n", 143 | " * если введенный язык есть в списке, пользователь должен попасть на страницу `127.0.0.1:5000/lang/код_языка`. На этой странице должно выводиться следующее: название введенного языка, код введенного языка, названия всех языков, код которых начинается на ту же букву.\n", 144 | "4. на всех страницах есть кликабельная ссылка на главную и на страницу с формой.\n", 145 | "\n", 146 | "Пример сайта на фласке есть в репозитории [вот тут](https://github.com/elmiram/2016learnpython/tree/master/flask_example). Там есть примеры использования всех функций фласка, которые мы проходили. (*Туда можно подглядывать.*)" 147 | ] 148 | } 149 | ], 150 | "metadata": { 151 | "kernelspec": { 152 | "display_name": "Python 3", 153 | "language": "python", 154 | "name": "python3" 155 | }, 156 | "language_info": { 157 | "codemirror_mode": { 158 | "name": "ipython", 159 | "version": 3 160 | }, 161 | "file_extension": ".py", 162 | "mimetype": "text/x-python", 163 | "name": "python", 164 | "nbconvert_exporter": "python", 165 | "pygments_lexer": "ipython3", 166 | "version": "3.4.3" 167 | } 168 | }, 169 | "nbformat": 4, 170 | "nbformat_minor": 0 171 | } 172 | -------------------------------------------------------------------------------- /9-10 Семинар - Анкета.md: -------------------------------------------------------------------------------- 1 | #Проект №2 - Сайт-анкета 2 | 3 | Нужно написать сайт-анкету для (полевой) работы с информантом. 4 | 5 | На сайте должны быть: 6 | 7 | 1) Главная страница (127.0.0.1), на которой показывается анкета с полями. 8 | Данные, которые будут вводиться в анкету, должны записываться в файл. 9 | 10 | 2) Страница статистики (127.0.0.1/stats), на которой результаты должны систематизироваться и в удобном виде выводиться 11 | на экран (это могут быть таблицы, какие-то подсчеты и тд). 12 | 13 | 3) Страница с выводом всех данных (127.0.0.1/json), на которой возвращается json со всеми введенными на сайте данными. 14 | 15 | 4) Страница поиска (127.0.0.1/search) и результатов поиска (127.0.0.1/results) . 16 | В ней достаточно сделать одно-два поля поиска (например, текстовый ввод и чекбокс или два текстовых ввода или другое). На странице должно быть описано, по каким данным ведется поиск. 17 | 18 | Тема может быть любой, вот примеры: 19 | 20 | - Лексико-типологическая анкета по какому-то лексическому полю (части тела, цвета, предметы мебели, домашняя утварь и всё, на что хватит вашей лингвистической фантазии). Анкета может быть в виде веб-формы с картинками того предмета/цвета, который должен назвать информант, и текстовыми полями, в которых он впишет названия предмета/цвета на своём языке. Кроме полей для названий предметов/цветов форма будет содержать поле, в котором информант должен будет указать свой язык. 21 | - Сбор словаря\грамматики в экспедиции. Например, в анкете могут быть слова из списка Сводеша, а информант должен ввести их перевод на свой язык. Кроме того, у каждого перевода может быть какой-то комментарий или какая-то другая дополнительная информация. 22 | - Сбор бытовой лексики русского языка. (Посмотрите, как это делает Борис Иомдин, например, вконтакте) 23 | - Что-то социолингвистическое, могут быть вопросы типа голосования (твОрог или творОг?) и обязательно информация о заполняющем (родной город, язык, возраст, образование, пол и тд) 24 | - Что-угодно другое лингвистическое и интересующее лично вас. 25 | 26 | 27 | ## Всякое 28 | 29 | ### Как вставить картинку в сайт на фласке? 30 | 31 | Картинки нужно сложить в папку `static`. В html-шаблоне нужно написать: 32 | 33 | `` 34 | 35 | Чтобы просто вставить картинку в html (без фласка), нужно написать так: 36 | `` 37 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # ЧаВо по проектам 2 | 3 | ## Будет ли устная защита проектов? 4 | Нет, нужно только прислать вашему преподавателю 5 | 6 | 1) ссылку на репозиторий с вашим проектом и 7 | 2) ссылку на работающий сайт\бота. 8 | 9 | ## Когда дедлайн? 10 | 11 | **20 июня.** 12 | 13 | После этой даты проекты не принимаются. До 20 июня можно показывать промежуточные версии, задавать вопросы и спрашивать советы. 14 | 15 | ## А на экзамене что? 16 | 17 | А ничего. Экзамен будет 28 июня, но на него приходить не надо. 18 | 19 | ## Чего мы ждем в ваших проектах? 20 | 21 | * Соответствие сделанного сформулированной задаче. Если в задаче значилось "бот сообщает про некоторого пользователя в ВК, откуда он, какой у него возраст, сколько у него друзей, где он учится или работает...", а в итоге бот в лучшем случае говорит имя пользователя, то задача не выполнена. 22 | * Наличие лингвистической обработки пользовательского запроса (if applicable). Плохо, если бот ждёт, что пользователь ему всё скажет идеально по формату, а если не сказал, то и ответа от бота никакого. Хорошо, если подразумевается извлекать из запроса пользователя в свободной форме то что надо, ну или хотя бы попытаться. 23 | * Симпатичный интерфейс приложения / внятные реплики чатбота. 24 | 25 | ## Какие критерии оценивания проектов? 26 | 27 | * Если всё работает круто - 10. 28 | * Если всё работает, но есть мелкие или средние недочёты, которые ухудшают пользовательский опыт - 9 или 8. 29 | * Если не хватает какого-то функционала, крупные недочёты, но в целом неплохо - 7 или 6. 30 | * Если часть проекта не доделана, или код бота/приложения есть, но онлайн не запущен - 5. 31 | * Всё плохо (в разной степени) - 1-4. 32 | -------------------------------------------------------------------------------- /Heroku.md: -------------------------------------------------------------------------------- 1 | # Heroku 2 | 3 | ## PaaS 4 | 5 | Мы с вами научились выкладывать наши сайты и ботов на Pythonanywhere, но это не единственный вариант. Существуют другие подобные сервисы - [Heroku](https://www.heroku.com/), [Scalingo](https://scalingo.com/), [Openshift](https://www.openshift.com/). 6 | 7 | Все они являются PaaS - Platform-as-a-Service, которые позволяют вам очень просто пользоваться облачным хостингом и быстро выкладывать свои приложения вместо того, чтобы самостоятельно настраивать сервер. 8 | 9 | От Pythonanywhere они отличаются тем, что для них разработчиками не всегда предоставлен такой же удобный веб-интерфейс, в котором многое делается кнопочками в панели управления, и нужный функционал мы получем через командную строку Unix. Но ощее с Pythonanywhere у них то, что там есть возможность делать хотя бы что-то из того, что вам нужно, бесплатно. 10 | 11 | Дело в том, что вслед за компанией Amazon многие другие IT-компании стали предоставлять пользователям (в числе которых и частные лица, и компании поменьше) облачные сервисы, то есть возможность не содержать на свои средства большой парк серверов (это часто неудобно, потому что бесперебойную работу серверов обеспечить не так просто), а покупать место на мощных серверах, которые содержит хозяин облака. Основные конкурирующие на этом поле сервисы -- это [Amazon Web Services](https://aws.amazon.com/ru/), [Google Cloud](https://cloud.google.com/) и [Microsoft Azure](https://azure.microsoft.com/ru-ru/). Все они платные (Google предоставляет на своей платформе условно-бесплатный тестовый период в один месяц), но мощные и надёжные. 12 | 13 | ## Почему еще и Heroku? 14 | 15 | У бесплатных аккаунтов Heroku есть некоторое количество преимуществ перед аналогичными аккаунтами Pythonanywhere: 16 | 17 | |Pythonanywhere | Heroku | 18 | | --- | --- | 19 | |только одно приложение | несколько приложений | 20 | | свой домен - платная функция | позволяет использовать собственное доменое имя | 21 | | ограниченный доступ к внешним ресурсам | доступ к внешним ресурсам не ограничен | 22 | 23 | __Подробнее про ограниченный доступ__: на Pythonanywhere можно обращаться лишь к тем сайтам, которые входят в белый список разрешенных ресурсов. Т.е. например, наш сайт, который регулярно скачивает новости с hse.ru не будет работать на Pythonanywhere, потому что hse.ru не включен в белый список. На Heroku таких ограничений нет. 24 | 25 | __Подробнее про количество бесплатных приложений__: на Heroku можно создавать несколько приложений бесплатно лишь до тех пор, пока у вас есть свободные Dyno (про это будет ниже). 26 | 27 | 28 | ## Подготовка к выкладыванию 29 | 30 | 1) Чтобы наш фласк-сайт или фласк-бот заработал на Heroku, нужно убедиться, что в конце основной программы написан вот такой код: 31 | 32 | ```python 33 | if __name__ == '__main__': 34 | import os 35 | app.debug = True 36 | port = int(os.environ.get("PORT", 5000)) 37 | app.run(host='0.0.0.0', port=port) 38 | ``` 39 | 40 | 41 | 2) В директории с вашим фласк-приложением обязательно должны быть следующие три файла: 42 | * __`requirements.txt`__ - в этом файле нужно перечислить все не стандартные питоновские модули, которые необходимы для вашей программы. В списке нужно указать не только название модуля, но и нужную вам версию. Выглядеть это будет примерно так: 43 | 44 | ``` 45 | Flask==0.12.1 46 | pymorphy2==0.8 47 | pymystem3==0.1.5 48 | requests==2.13.0 49 | ``` 50 | 51 | Обратите внимание, что в этот список НЕ НУЖНО писать setuptools и pip! 52 | 53 | Когда вы будете выкладывать приложение, Heroku прочитает файл `requirements.txt` и установит все перечисленные модули командой `pip install -r requirements.txt`. 54 | 55 | * __`runtime.txt`__ - в этом файле нужно написать одну строчку: 56 | 57 | python-3.6.1 58 | 59 | Если в директории нет `runtime.txt`, то Heroku будет по умолчанию думать, что ваше приложение написано на Python 2.7. 60 | 61 | * __`Procfile`__ - обратите внимание, что у файла нет расширения. В этом файле нужно написать одну строчку: 62 | 63 | web: python flask_app.py 64 | 65 | Вместо `flask_app.py` нужно написать название вашего файла с фласк-приложением. `Procfile` - это специальный файл, в котором нужно объяснить Heroku, как именно нужно запускать ваше приложение. `web` значит, что мы написали приложение, которое отвечает на http-запросы, `python flask_app.py` обозначает команду, с помощью которой приложение запускается. 66 | 67 | 68 | 3) Для выкладывания приложений на Heroku используется git. Поэтому если в вашей директории с проектом еще нет git-репозитория, его нужно создать. Открываем терминал (Mac, Linux) или командную строку (Windows) и пишем: 69 | * `cd путь-к-директории` - переходим в директорию с нашим фласк-проектом, 70 | * `git init` - создаем в текущей директории репозиторий, 71 | * `git add .` - говорим гиту следить за всеми файлами в репозитории, 72 | * `git commit -m 'Initial commit'` - делаем первый коммит. 73 | 74 | 75 | ## Как выложить сайт на Heroku? 76 | 77 | 1) Зарегистрироваться на сайте [Heroku](https://www.heroku.com/), ваш email будет логином. Когда вы зарегистрировались, вы попадаете в Dashboard. Сейчас у вас там ничего нет - вам предлагают создать приложение (an app) или пройти туториал по работе с Heroku и выбранного вами языка программирования. Туториал по питону там ориентирован на джанго, мы работаем с фласком, так что наши действия будут слегка отличаться. 78 | 79 | 2) Скачать установщик для вашей ОС и установить Heroku CLI - Command Line Interface - https://devcenter.heroku.com/articles/heroku-cli 80 | 81 | 3) Открыть терминал (Mac, Linux) или командную строку (Windows) и выполнить команды: 82 | 83 | * __`cd путь-к-директории`__ - переходим в директорию с нашим фласк-проектом, 84 | * __`heroku login`__ - логинимся в наш аккаунт Heroku, 85 | * ввести свой емейл и пароль, с которыми мы зарегистрировались, 86 | * __`heroku create`__ - создаем приложение, которое поможет Heroku получить наш код и запустить его. В этот момент Heroku делает сразу несколько вещей. 87 | 88 | Во-первых, он генерирует случайное название для нашего приложения, но можно передать ваше собственное название, например, `heroku create my-app-04062017`. Ваш сайт будет доступен по адресу `имя-приложения.herokuapp.com`. 89 | 90 | Во-вторых, к вашему репозиторию добавляется удаленный репозиторий (git remote), который называется `heroku`. У одного локального репозитория на вашем компьютере может быть несколько удаленных (например, у вас может быть `origin` - это ваш удаленный репозиторий на GitHub, и `heroku` - удаленный репозиторий на Heroku.) 91 | 92 | * __`git push heroku master`__ - эта команда отправляет наш код на облачный хостринг, и Heroku устанавливает нужные модули. 93 | * __`heroku ps:scale web=1`__ - эта команда говорит запустить наш фласк-сайт на одном dyno. 94 | 95 | > A dyno is a lightweight Linux container that runs a single user-specified command. 96 | 97 | То есть ваш сайт или бот будет работать на маленьком виртуальном Линукс-"сервере". Бесплатно вам доступно 550 или 1000 таких dyno. 98 | 99 | * __`heroku open`__ - эта команда открывает ваш сайт в браузере. Ура! Все готово! 100 | * Если по какой-то причине сайт не заработал, то нужно посмотреть логи: 101 | 102 | - __`heroku logs`__ - эта команда распечатает 100 последних строчек логов, т.е. если произошла какая-то ошибка, то информацию об этой ошибке вы увидите в конце логов. 103 | - или __`heroku logs --tail`__ - эта команда показывает логи в реальном времени, т.е. если ваша программа что-то печатает в консоль с помощью `print(...)` или питон печатает сообщение об ошибке, то вы будете в реальном времени видеть, что именно происходит. 104 | - подробнее - https://devcenter.heroku.com/articles/logging 105 | 106 | Когда вы меняете что-то в вашей программе\директории, то изменения нужно отправить на Heroku: 107 | 108 | git add . 109 | git commit -m "Demo" 110 | git push heroku master 111 | heroku ps:scale web=1 112 | 113 | 114 | ## Секретные ключи 115 | 116 | Поскольку мы отправляем все файлы на Heroku через git, возникает небольшая проблема: 117 | 118 | Что если у нас есть секретные файлы (например, API-токены или пароли), которые включены в `.gitignore`? Получается, мы никак не сможем отправить эти секретные файлы на облачный хостинг через git. 119 | 120 | ### Переменные среды 121 | Для работы с секретами рекомендуется использовать [переменные среды](https://en.wikipedia.org/wiki/Environment_variable): ваши ключи будут храниться в текстовых переменных вашей системы и в вашем Heroku-приложении, но при этом они никогда не попадут в открытый git-репозиторий. 122 | 123 | Раньше мы с вами писали секретные ключи в игнорируемый файл (например, `TOKEN = "......"` внутри `conf.py`) и импортировали секретные переменные с помощью `from conf import *`. 124 | 125 | Вместо этого, можно установить переменную среды прямо в терминале\командной строке: 126 | 127 | * Mac, Linux - `export SOME_SECRET_KEY=1c3-cr3am-15-yummy` 128 | * Windows - `set SOME_SECRET_KEY 1c3-cr3am-15-yummy` 129 | * Heroku - `heroku config:set SOME_SECRET_KEY=1c3-cr3am-15-yummy` 130 | 131 | После этого и на вашем компьютере, и в облачном хостинге Heroku переменную среды можно прочитать с помощью питона: 132 | 133 | ```python 134 | >>> import os 135 | >>> os.environ["SOME_SECRET_KEY"] 136 | "1c3-cr3am-15-yummy" 137 | ``` 138 | 139 | 140 | То есть строчку `from conf import *` вам придется заменить на строчки вида: 141 | 142 | ```python 143 | import os 144 | SOME_SECRET_KEY = os.environ["SOME_SECRET_KEY"] 145 | ``` 146 | 147 | 148 | Кроме того, старые вебхуки с адресом на pythonanywhere уже не действительны, и их надо заменить на адрес вашего приложения. В целом, код очень простого бота из первого конспекта по телеграму, адаптированный под Heroku, будет выглядеть вот так: 149 | 150 | ```python 151 | import flask 152 | import telebot 153 | import os 154 | 155 | TOKEN = os.environ["TOKEN"] 156 | 157 | bot = telebot.TeleBot(TOKEN, threaded=False) 158 | 159 | 160 | bot.remove_webhook() 161 | bot.set_webhook(url="https://.herokuapp.com/bot") 162 | 163 | app = flask.Flask(__name__) 164 | 165 | @bot.message_handler(commands=['start', 'help']) 166 | def send_welcome(message): 167 | bot.send_message(message.chat.id, "Здравствуйте! Это бот, который считает длину вашего сообщения.") 168 | 169 | 170 | @bot.message_handler(func=lambda m: True) # этот обработчик реагирует все прочие сообщения 171 | def send_len(message): 172 | bot.send_message(message.chat.id, 'В вашем сообщении {} символов.'.format(len(message.text))) 173 | 174 | @app.route("/", methods=['GET', 'HEAD']) 175 | def index(): 176 | return 'ok' 177 | 178 | # страница для нашего бота 179 | @app.route("/bot", methods=['POST']) 180 | def webhook(): 181 | if flask.request.headers.get('content-type') == 'application/json': 182 | json_string = flask.request.get_data().decode('utf-8') 183 | update = telebot.types.Update.de_json(json_string) 184 | bot.process_new_updates([update]) 185 | return '' 186 | else: 187 | flask.abort(403) 188 | 189 | if __name__ == '__main__': 190 | import os 191 | app.debug = True 192 | port = int(os.environ.get("PORT", 5000)) 193 | app.run(host='0.0.0.0', port=port) 194 | ``` 195 | 196 | 197 | ## Как запустить что-то по расписанию? 198 | 199 | Если вы делаете какой-то веб-сервис, который должен обновлять какие-то данные через определённые промежутки времени (например, каждый день в 12:00 или каждый четверг в 17:25), то вы можете настроить стандартную Unix-утилиту cron, которая умеет запускать на сервере какой-то нужный пользователю процесс в тот момент, когда это ему необходимо. 200 | 201 | Утилита cron обычно уже входит в стандартную сборку дистрибутива и устанавливать её не нужно. Она имеет текстовый интерфейс настройки, то есть расписание придётся писать через консоль, в одном из консольных текстовых редакторов. Наиболее популярный из них -- [Vim](https://ru.wikipedia.org/wiki/Vim). Он не очень прост в обращении, потому что неинтуитивен. Перед тем, как его запускать, узнайте, [как его закрыть](https://ru.wikibooks.org/wiki/Vim) (почти как в сказке, нужно помнить про "горшочек, не вари", прежде чем сказать "горшочек, вари"). Недавно крупный сайт подсказок и ответов на вопросы о программировании Stackoverflow опубликовал [новость](https://stackoverflow.blog/2017/05/23/stack-overflow-helping-one-million-developers-exit-vim/), из которой следует, что вопрос "как выйти из Vim" является чуть ли не самым популярным при поиске вопросом. 202 | 203 | Есть и другой консольный редактор, nano, он проще и все команды, которые вы должны выполнить для тех или иных действий, постоянно показываются внизу экрана ("крышечка" означает "Ctrl"): 204 | 205 | ![nano](https://www.howtoforge.com/images/linux_nano_command/nano-editor-window.png) 206 | 207 | Однако он не установлен по умолчанию во всех дистрибутивах, и, возможно, его придётся установить самостоятельно. 208 | 209 | Создать или отредактировать расписание можно вызовом команды `crontab -e`. В открывшемся окне нужно будет описать [в формате cron](http://www.nncron.ru/nncronlt/help/RU/working/cron-format.htm), что вы хотите, чтобы было сделано, и когда это должно быть сделано. Формат cron подразумевает, что вы напишете `минута час день_месяца месяц день_недели команда`, а потом -- то, что должно будет запуститься, в том виде, в котором бы вы сами это запускали вручную в командной строке. 210 | 211 | Примеры расписания в cron: 212 | 213 | ``` 214 | # нечто будет выполняться каждую минуту: 215 | * * * * * python3 /home/user/script.py 216 | 217 | # только по выходным: 218 | * * * * 6,7 python3 /home/user/weekend.py 219 | 220 | # дважды в день: 221 | 20 11,16 * * * python3 /home/user/twice.py 222 | 223 | # один раз в году, в полночь 1-го января: 224 | 0 0 1 1 * python3 /home/user/HappyNewYear.py 225 | 226 | ``` 227 | 228 | Обратите внимание, что к программам нужно писать полный путь, потому что cron ничего не знает про то, какую директорию вы считаете рабочей. 229 | 230 | ## Полезная ссылка 231 | 232 | [Тут](https://tproger.ru/translations/telegram-bot-create-and-deploy/amp/) можно найти ещё одно руководство по запуску бота на хероку. 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Методы компьютерной обработки лингвистических данных 2 | 3 | Привет! 4 | Сюда будут выкладываться конспекты семинаров по курсу программирования в 2016-2017 для 2го курса. 5 | Домашние задания выкладываются в ваши личные репозитории, ссылки на них нужно сообщить нам в [этой форме](https://docs.google.com/forms/d/e/1FAIpQLSdXLd6WhYxHNU3SG6qLY-HObd6ZXGFfeHLpU-sHz3KsfDiXXw/viewform?c=0&w=1). 6 | 7 | [Список занятий в 1-м и 2-м модулях](https://github.com/elmiram/2016learnpython/blob/master/%D0%97%D0%B0%D0%BD%D1%8F%D1%82%D0%B8%D1%8F%20%D0%B2%201-%D0%BC%20%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80%D0%B5.md). 8 | 9 | [Темы экзаменационных проектов](https://docs.google.com/spreadsheets/d/16_76-1h9oaRiYugsw3n38_ZbyGVMDD8vzP5xlLvpBIY/edit?usp=sharing) 10 | 11 | ### [ЧаВо по проектам](https://github.com/elmiram/2016learnpython/blob/master/FAQ.md) 12 | 13 | Занятия в 4-м модуле: 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
Дата занятияКонспектДомашнее заданиеДополнительное
03.04.2017VK API
10.04.2017Matplotlib Intro
17.04.2017Matplotlib+VK заданияMatplotlib+VK
ДЗ к 1.05
Материалы семинара
39 | Решения заданий 1-4
18.04.2017UNIX, Командная строка
25.04.2017Чатботы. Пишем очень простого бота и выкладываем его на сервер.Задание про очень простого телеграм-бота.
ДЗ к 14 мая
12.05.2017Чатботы 2. Боты посложнее.
15.05.2017Чатботы 3. Пример.
22.05.2017Морфологические анализаторы pymorphy2, pymystem3Задание в конце конспекта про морф. анализаторы.
Дедлайн 2 июня.
23.05.2017Tweepy
26.05.20171) Google Charts, d3.js, geojson
2) NetworkX
30.05.2017Gensim, word2vec
05.06.2017Heroku
97 | -------------------------------------------------------------------------------- /TelegramBot2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Чатботы 2\n", 8 | "\n", 9 | "В прошлый раз мы научились писать очень простого бота: он умеет получать сообщение и отправлять в ответ его же или, например, его длину. \n", 10 | "\n", 11 | "Но возможностей у ботов гораздо больше, и как ими пользоваться описано в [документации модуля](https://github.com/eternnoir/pyTelegramBotAPI#general-api-documentation)." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Обработчики сообщений\n", 19 | "Мы с вами видели вот такой код:" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": { 26 | "collapsed": true 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "@bot.message_handler(commands=['start', 'help'])\n", 31 | "def send_welcome(message):\n", 32 | "\tbot.send_message(message.chat.id, \"Здравствуйте! Это бот, который считает длину вашего сообщения.\")" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "Внутри декоратора `@bot.message_handler(...)` могут находиться фильтры, которые описывают, на какие сообщения реагирует данная функция. Фильтры пишутся так: сначала название фильтра, затем через знак равно его значение. Бывают фильтры четырех типов:\n", 40 | "* `content_types`, значением является массив строк, задающих тип контента - текст, аудио, файл, стикер и т.д. (по умолчанию ['text'])\n", 41 | "* `regexp`, значением является регулярное выражение (строка)\n", 42 | "* `commands`, значением является массив строк, задающих команды без знака /\n", 43 | "* `func`, значением является какая-то функция\n", 44 | "\n", 45 | "Что бывает, когда боту приходит сообщение, которое подходит под несколько наших фильтров, т.е. несколько разных функций? В этом случае запускается функция, которая в вашем коде написана раньше других. \n", 46 | "\n", 47 | "[Примеры обработчиков](https://github.com/eternnoir/pyTelegramBotAPI#message-handlers)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "# Отправка сообщений\n", 55 | "\n", 56 | "Для отправки разных типов сообщений и разного контента используются разные функции.\n", 57 | "Отправка текста:" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": { 64 | "collapsed": true 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "# ответить на сообщение (с цитированием)\n", 69 | "tb.reply_to(message, text)\n", 70 | "\n", 71 | "# отправить текст в чат по ID\n", 72 | "tb.send_message(chat_id, text)\n", 73 | "\n", 74 | "# переслать данное сообщение из одного чата в другой\n", 75 | "tb.forward_message(to_chat_id, from_chat_id, message_id)" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "Отправка файлов (из документации):" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": { 89 | "collapsed": true 90 | }, 91 | "outputs": [], 92 | "source": [ 93 | "# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.\n", 94 | "# sendPhoto\n", 95 | "photo = open('/tmp/photo.png', 'rb')\n", 96 | "tb.send_photo(chat_id, photo)\n", 97 | "tb.send_photo(chat_id, \"FILEID\")\n", 98 | "\n", 99 | "# sendAudio\n", 100 | "audio = open('/tmp/audio.mp3', 'rb')\n", 101 | "tb.send_audio(chat_id, audio)\n", 102 | "tb.send_audio(chat_id, \"FILEID\")\n", 103 | "\n", 104 | "## sendAudio with duration, performer and title.\n", 105 | "tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')\n", 106 | "\n", 107 | "# sendVoice\n", 108 | "voice = open('/tmp/voice.ogg', 'rb')\n", 109 | "tb.send_voice(chat_id, voice)\n", 110 | "tb.send_voice(chat_id, \"FILEID\")\n", 111 | "\n", 112 | "# sendDocument\n", 113 | "doc = open('/tmp/file.txt', 'rb')\n", 114 | "tb.send_document(chat_id, doc)\n", 115 | "tb.send_document(chat_id, \"FILEID\")\n", 116 | "\n", 117 | "# sendSticker\n", 118 | "sti = open('/tmp/sti.webp', 'rb')\n", 119 | "tb.send_sticker(chat_id, sti)\n", 120 | "tb.send_sticker(chat_id, \"FILEID\")\n", 121 | "\n", 122 | "# sendVideo\n", 123 | "video = open('/tmp/video.mp4', 'rb')\n", 124 | "tb.send_video(chat_id, video)\n", 125 | "tb.send_video(chat_id, \"FILEID\")\n", 126 | "\n", 127 | "# sendLocation\n", 128 | "tb.send_location(chat_id, lat, lon)\n", 129 | "\n", 130 | "# sendChatAction\n", 131 | "# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',\n", 132 | "# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.\n", 133 | "tb.send_chat_action(chat_id, action_string)\n", 134 | "\n", 135 | "# getFile\n", 136 | "# Downloading a file is straightforward\n", 137 | "# Returns a File object\n", 138 | "import requests\n", 139 | "file_info = tb.get_file(file_id)\n", 140 | "\n", 141 | "file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "# Клавиатура\n", 149 | "\n", 150 | "Можно отправлять пользователю специальную клавиатуру для ответа или интерактивные кнопки. Клавиатуру можно добавить во все функции, которые начинаются со слова `send_` например так: `tb.send_message(chat_id, text, reply_markup=keyboard)`. При этом `keyboard` - это объект `ReplyKeyboardMarkup`, `ReplyKeyboardRemove` или `ForceReply`. Рассмотрим `ReplyKeyboardMarkup`:" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": { 157 | "collapsed": true 158 | }, 159 | "outputs": [], 160 | "source": [ 161 | "from telebot import types\n", 162 | "\n", 163 | "# создаем клавиатуру\n", 164 | "keyboard = types.ReplyKeyboardMarkup(row_width=2) # row_width говорит сколько кнопок должно поместиться в одном ряду\n", 165 | "# добавляем кнопки\n", 166 | "btn1 = types.KeyboardButton('да')\n", 167 | "btn2 = types.KeyboardButton('нет')\n", 168 | "btn3 = types.KeyboardButton('не знаю')\n", 169 | "keyboard.add(btn1, btn2, btn3)\n", 170 | "# отправляем\n", 171 | "tb.send_message(chat_id, \"Вы студент?\", reply_markup=keyboard)\n", 172 | "\n", 173 | "\n", 174 | "# Можно создавать каджый ряд отдельно\n", 175 | "# создаем клавиатуру \n", 176 | "keyboard = types.ReplyKeyboardMarkup() # здесь нет row_width !!!\n", 177 | "# создаем кнопки\n", 178 | "btn1 = types.KeyboardButton('red')\n", 179 | "btn2 = types.KeyboardButton('blue')\n", 180 | "btn3 = types.KeyboardButton('green')\n", 181 | "btn4 = types.KeyboardButton('brown')\n", 182 | "btn5 = types.KeyboardButton('black')\n", 183 | "# добавляем ряды кнопок в клавиатуру\n", 184 | "keyboard.row(btn1, btn2, btn3)\n", 185 | "keyboard.row(bnt4, btn5)\n", 186 | "# отправляем\n", 187 | "tb.send_message(chat_id, \"Choose color:\", reply_markup=keyboard)" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "[Больше про кнопки в документации](https://github.com/eternnoir/pyTelegramBotAPI#reply-markup) и в [туториале](https://groosha.gitbooks.io/telegram-bot-lessons/content/chapter8.html)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "# Пример бота: разметка\n", 202 | "\n", 203 | "Предположим у вас есть тысячи отзывов на кинофильмы, и вам нужно их разметить как \"положительные\", \"отрицательные\" или \"нейтральные\". Вы можете сделать бота, который будет отправлять юзеру какой-то отзыв из нашего списка и клавиатуру с кнопками \"+\", \"-\", \"=\", а затем бот будет записывать ответы. Бота мы можем отправить друзьям, чтобы они помогли нам с разметкой=)\n", 204 | "\n", 205 | "(строчки, связанные с фласком, в коде ниже отсутствуют)\n", 206 | "Смотрим пример:" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": { 213 | "collapsed": true 214 | }, 215 | "outputs": [], 216 | "source": [ 217 | "# -*- coding: utf-8 -*-\n", 218 | "# импортируем модули\n", 219 | "import telebot\n", 220 | "import conf\n", 221 | "import random\n", 222 | "import shelve\n", 223 | "from telebot import types\n", 224 | "\n", 225 | "bot = telebot.TeleBot(conf.TOKEN, threaded=False)\n", 226 | "\n", 227 | "# предположим, отзывы у нас хранятся в виде csv-файла с номерами отзывов и собственно текстами\n", 228 | "with open('/home/2016learnpython/myapp/reviews.csv', 'r', encoding='utf-8') as f:\n", 229 | " reviews = {} # создадим словать отзывов\n", 230 | " for line in f:\n", 231 | " num, text = line.strip().split('\\t')\n", 232 | " reviews[num] = text\n", 233 | "review_keys = list(reviews.keys()) # и отдельно массив ключей\n", 234 | "\n", 235 | "\n", 236 | "# собираем клавиатуру для разметки (возможно имеет смысл добавить кнопку \"не знаю\"?)\n", 237 | "keyboard = types.ReplyKeyboardMarkup(row_width=3)\n", 238 | "btn1 = types.KeyboardButton('+')\n", 239 | "btn2 = types.KeyboardButton('-')\n", 240 | "btn3 = types.KeyboardButton('=')\n", 241 | "keyboard.add(btn1, btn2, btn3)\n", 242 | "\n", 243 | "\n", 244 | "# shelve используется как мини-база данных, там можно хранить файлы в виде \"ключ-значение\"\n", 245 | "# в этой базе мы будем хранить информацию о том, какой отзыв мы недавно прислали юзеру\n", 246 | "shelve_name = '/home/USERNAME/myapp/shelve.db' # Файл с хранилищем\n", 247 | "\n", 248 | "\n", 249 | "def set_user_review(chat_id, review):\n", 250 | " \"\"\"\n", 251 | " Записываем юзера в базу данных и запоминаем номер отзыва, который мы ему дали\n", 252 | " \"\"\"\n", 253 | " with shelve.open(shelve_name) as storage:\n", 254 | " storage[str(chat_id)] = review\n", 255 | "\n", 256 | "\n", 257 | "def get_user_review(chat_id):\n", 258 | " \"\"\"\n", 259 | " Вспоминаем, какой отзыв мы отправили на разметку\n", 260 | " :return: (str) Номер отзыва / None\n", 261 | " \"\"\"\n", 262 | " with shelve.open(shelve_name) as storage:\n", 263 | " try:\n", 264 | " review = storage[str(chat_id)]\n", 265 | " return review\n", 266 | " # Если человека нет в базе, ничего не возвращаем\n", 267 | " except KeyError:\n", 268 | " return None\n", 269 | "\n", 270 | "# этот обработчик запускает функцию send_welcome, когда пользователь отправляет команду /help\n", 271 | "@bot.message_handler(commands=['help'])\n", 272 | "def send_welcome(message):\n", 273 | " bot.send_message(message.chat.id, \"Здравствуйте! Это бот для разметки отзывов на кинофильмы.\\n Положительные отзывы отмечаются плюсом +, отрицательные минусом -, а нейтральные знаком равно =.\")\n", 274 | "\n", 275 | "\n", 276 | "# этот обработчик запускает функцию, когда пользователь отправляет команду /start\n", 277 | "@bot.message_handler(commands=['start'])\n", 278 | "def send_first_review(message):\n", 279 | " review_num = random.choice(review_keys)\n", 280 | " bot.send_message(message.chat.id, reviews[review_num], reply_markup=keyboard)\n", 281 | " set_user_review(message.chat.id, review_num)\n", 282 | "\n", 283 | "\n", 284 | "@bot.message_handler(regexp='[-+=]') # этот обработчик реагирует на символы разметки и записывает результаты\n", 285 | "def get_answer(message):\n", 286 | " review_num = get_user_review(message.chat.id) # проверяем, есть ли юзер в базе данных\n", 287 | " if review_num:\n", 288 | " # если есть, открываем файл с результатами и записываем туда разметку\n", 289 | " with open('/home/USERNAME/myapp/results.csv', 'a', encoding='utf-8') as results:\n", 290 | " results.write(review_num + '\\t' + message.text + '\\n')\n", 291 | " # и сразу отправляем новый отзыв\n", 292 | " review_num = random.choice(review_keys)\n", 293 | " bot.send_message(message.chat.id, reviews[review_num], reply_markup=keyboard)\n", 294 | " set_user_review(message.chat.id, review_num)\n", 295 | " else:\n", 296 | " # если нет, то что-нибудь говорим об этом\n", 297 | " bot.send_message(message.chat.id, 'Вы не разметили отзыв.')\n" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "## Задания\n", 305 | "1) Попробуйте запустить этого бота у себя на компьютере.\n", 306 | "\n", 307 | "2) Добавьте функции, которые позволят пользователю завершить разметку по команде \"`/stop`\" и удалиться из базы данных.\n", 308 | "\n", 309 | "3) Попробуйте сделать так, чтобы пользователю отправлялись только те отзывы, которые еще не были размечены ни разу." 310 | ] 311 | } 312 | ], 313 | "metadata": { 314 | "kernelspec": { 315 | "display_name": "Python 3", 316 | "language": "python", 317 | "name": "python3" 318 | }, 319 | "language_info": { 320 | "codemirror_mode": { 321 | "name": "ipython", 322 | "version": 3 323 | }, 324 | "file_extension": ".py", 325 | "mimetype": "text/x-python", 326 | "name": "python", 327 | "nbconvert_exporter": "python", 328 | "pygments_lexer": "ipython3", 329 | "version": "3.5.3" 330 | } 331 | }, 332 | "nbformat": 4, 333 | "nbformat_minor": 1 334 | } 335 | -------------------------------------------------------------------------------- /VK API Часть 1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# VK API\n", 8 | "\n", 9 | "## Введение\n", 10 | "\n", 11 | "Мы уже [учились выкачивать Интернет](https://github.com/elmiram/2016learnpython/blob/master/1%20%D0%A1%D0%B5%D0%BC%D0%B8%D0%BD%D0%B0%D1%80%20-%20urllib.ipynb). Однако этот способ добычи текстов (шахтёрская терминология тут неслучайна, по-английски и интеллектуальный анализ текста, и процесс нахождения данных для этого анализа называется *mining*) подходит не для всех сайтов. Некоторые ресурсы либо прямо запрещают автоматическое обращение к своему контенту, потому что это для них невыгодно (зарабатывают-то они на рекламе, которую показывают **людям**, а с роботов отдача нулевая) и излишне загружает сервера, либо просто организовывают свой сайт так, чтобы сама по себе загрузка страницы клиентом не давала ничего существенного, а всё нужное подгружалось уже потом с помощью программ на языке JavaScript. Запускать такие программы из питона несколько сложнее, так что всё это создаёт для компьютерного лингвиста дополнительные трудности. К таким сайтам относятся, например, и социальные сети: VK, Facebook, Instagram и пр.\n", 12 | "\n", 13 | "Однако многие крупные ресурсы либо по доброте душевной, либо потому что это даёт и им кое-какую выгоду, встраивают в свою систему API: application programming interface, то есть средство для автоматизированного обращения к приложению (сайту). Через такую систему можно решать иногда довольно широкий спектр задач. Может быть, всё, что таким образом можно сделать, нам не нужно. Но вот получать тексты было бы полезно: в тех же социальных сетях люди пишут, во-первых, много (а для компьютерного лингвиста чем больше данных, тем лучше), во-вторых, на таком варианте языка, который приближен к разговорному (другие способы намайнить себе текстов такого рода гораздо затратнее). Попробуем познакомиться с инструментарием API на примере vk.com\n", 14 | "\n" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Как выглядит обращение к VK API\n", 22 | "\n", 23 | "В принципе, VK API, как следует из [документации](https://vk.com/dev/openapi) придуман не для выкачивания текстов, а для создания веб-приложений на сторонних сайтах, которые бы могли взаимодействовать с vk.com. Но это всё равно не мешает нам воспользоваться такой возможностью в своих целях.\n", 24 | "\n", 25 | "Практически всё выглядит следующим образом. На сайте vk.com есть специальные страницы, которые не предназначены для того, чтобы их открывать браузером, они ожидают именно автоматического обращения. Что значит *автоматическое обращение*? Это то самое, что мы проходили, когда учились выкачивать Интернет: программа на питоне (но в теории может быть и не на питоне) посылает серверу запрос, договаривается с ним и получает ответ. Вспомним, что для произвольной страницы это выглядит так:" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 1, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "import urllib.request # импортируем модуль \n", 37 | "req = urllib.request.Request('https://habrahabr.ru/') # посылаем запрос\n", 38 | "with urllib.request.urlopen(req) as response: # открываем соединение с сайтом\n", 39 | " html = response.read().decode('utf-8') # \"читаем\" ответ сервера (сайта) в переменную html" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "Для взаимодействия с vk.com нам потребуется тот же самый модуль urllib.request, а страницы, к которым мы будем обращаться, описаны в [документации VK API](https://vk.com/dev/openapi).\n", 47 | "\n", 48 | "Но есть одна хитрость. Она в том, что страницы, к которым мы будем обращаться, ожидают не просто обращения, а передачи определённых параметров. Это логично: мы же должны сказать системе, что мы точно хотим сделать. Например, мы хотим получить какое-то количество записей со стены определённого пользователя. Тогда нужно сообщить, какой это пользователь. Сайт vk.com много чего может, но не читать наши мысли.\n", 49 | "\n", 50 | "### Передача параметров\n", 51 | "\n", 52 | "Как передать эти параметры сайту? Для этого тоже есть стандартные средства, но уже не придуманные разработчиками сайта, а прописанные в [протоколе HTTP](https://www.tutorialspoint.com/http/http_parameters.htm) давным-давно. Выглядит это так. В строке адреса, например, в браузере мы сначала пишем имя протокола (http или https), которое отделяется от всего, что идёт дальше последовательностью \"://\", потом пишется доменное имя (по сути, основной адрес сайта), к которому мы хотим обратиться (например, vk.com, после точки следует т.н. *доменная зона*), после косой черты далее следует адрес собственно страницы на сайте, к которой мы хотим обратиться: https://api.vk.com/method/wall.get А вот после адреса страницы мы можем в той же адресной строке передать уже собственно параметры. Место, где передаются параметры, отделяется от адреса страницы с помощью знака вопроса, а сами выглядят как пары ключ-значение, где ключ отделяется от значения с помощью знака равно: https://api.vk.com/method/wall.get?owner_id=1 Здесь есть параметр owner_id, который указывает на пользователя, стену которого мы хотим скачать, и значение этого параметра 1, то есть речь идёт об основателе соцсети и первом её пользователе Павле Дурове, страница которого открывается по адресу [https://vk.com/id1](https://vk.com/id1). Такой номер, то есть уникальный идентификатор (*id*) есть у каждого пользователя и у каждого сообщества. \n", 53 | "\n", 54 | "Если есть необходимость передать сразу несколько параметров, то они должны отделяться друг от друга знаком \"аперсанд\", то есть **&**: https://api.vk.com/method/wall.get?owner_id=1&count=10. Здесь появился второй параметр count, который говорит, что мы хотим скачать именно 10 записей со стены пользователя.\n", 55 | "\n", 56 | "Так же выглядит и строка адреса при поисковом запросе, например, в Яндексе: https://yandex.ru/search/?text=соцсети\n" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "### Пробуем!\n", 64 | "\n", 65 | "Итак, поехали, давайте скачаем две записи со стены Дурова. Чтобы скачивать записи со стен, у нас есть специальный метод VK API, он называется wall.get и выглядит как специальная страница на сайте vk.com: https://api.vk.com/method/wall.get Как работает этот метод, рассказано на его странице в документации: https://vk.com/dev/wall.get Среди полезного там есть список обязательных параметров, которые нужно передать методу, чтобы он сработал.\n", 66 | " \n", 67 | "Часть адреса https://api.vk.com/method/ можно запомнить. Все остальные методы просто добавляются к нему: https://api.vk.com/method/wall.getComments, https://api.vk.com/method/wall.getById и т.д. " 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": { 74 | "collapsed": true 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "import urllib.request # импортируем модуль \n", 79 | "req = urllib.request.Request('https://api.vk.com/method/wall.get?owner_id=1&count=2') \n", 80 | "response = urllib.request.urlopen(req) # да, так тоже можно, не обязательно делать это с with, как в примере выше\n", 81 | "result = response.read().decode('utf-8')" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Посмотрим, что получилось:" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": { 95 | "collapsed": false 96 | }, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "{\"response\":[234,{\"id\":1674764,\"from_id\":1,\"to_id\":1,\"date\":1491046870,\"post_type\":\"post\",\"text\":\"К 1 апреля в Трижды Краснознаменный завезли новые стикеры.\",\"attachment\":{\"type\":\"photo\",\"photo\":{\"pid\":456263811,\"aid\":-7,\"owner_id\":1,\"src\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a2f\\/6e-bqVFCKiw.jpg\",\"src_big\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a30\\/Grb21yg2mfA.jpg\",\"src_small\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a2e\\/027Nqr_T3iw.jpg\",\"src_xbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a31\\/s2pr5YnsQ-w.jpg\",\"src_xxbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a32\\/vqXD6TjwUrc.jpg\",\"src_xxxbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a33\\/HO2a_wZlzd4.jpg\",\"width\":1200,\"height\":1200,\"text\":\"\",\"created\":1491046798,\"post_id\":1674764,\"access_key\":\"6b81100d8d5e38e742\"}},\"attachments\":[{\"type\":\"photo\",\"photo\":{\"pid\":456263811,\"aid\":-7,\"owner_id\":1,\"src\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a2f\\/6e-bqVFCKiw.jpg\",\"src_big\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a30\\/Grb21yg2mfA.jpg\",\"src_small\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a2e\\/027Nqr_T3iw.jpg\",\"src_xbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a31\\/s2pr5YnsQ-w.jpg\",\"src_xxbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a32\\/vqXD6TjwUrc.jpg\",\"src_xxxbig\":\"https:\\/\\/pp.userapi.com\\/c836333\\/v836333001\\/30a33\\/HO2a_wZlzd4.jpg\",\"width\":1200,\"height\":1200,\"text\":\"\",\"created\":1491046798,\"post_id\":1674764,\"access_key\":\"6b81100d8d5e38e742\"}}],\"comments\":{\"count\":4653},\"likes\":{\"count\":8594},\"reposts\":{\"count\":127}},{\"id\":1665636,\"from_id\":1,\"to_id\":1,\"date\":1490879782,\"post_type\":\"copy\",\"text\":\"Если кто ещё не использует телеграм, то вот вам немножко доводов http:\\/\\/telegra.ph\\/Vozmozhnosti-Telegram-03-30

АПД: Звонки уже запустили\",\"copy_post_date\":1490873169,\"copy_post_type\":\"post\",\"copy_owner_id\":164116,\"copy_post_id\":12173,\"copy_text\":\"Обзор на удивление годный.\",\"attachment\":{\"type\":\"link\",\"link\":{\"url\":\"http:\\/\\/telegra.ph\\/Vozmozhnosti-Telegram-03-30\",\"title\":\"Возможности Telegram. Мессенджер, каким он должен быть\",\"description\":\"\",\"target\":\"external\",\"image_src\":\"https:\\/\\/pp.userapi.com\\/c626216\\/v626216116\\/6e71b\\/HYxH0GjTCPQ.jpg\"}},\"attachments\":[{\"type\":\"link\",\"link\":{\"url\":\"http:\\/\\/telegra.ph\\/Vozmozhnosti-Telegram-03-30\",\"title\":\"Возможности Telegram. Мессенджер, каким он должен быть\",\"description\":\"\",\"target\":\"external\",\"image_src\":\"https:\\/\\/pp.userapi.com\\/c626216\\/v626216116\\/6e71b\\/HYxH0GjTCPQ.jpg\"}}],\"comments\":{\"count\":5386},\"likes\":{\"count\":2631},\"reposts\":{\"count\":256}}]}\n" 103 | ] 104 | } 105 | ], 106 | "source": [ 107 | "print (result)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "Если вглядеться, то выйдет, что это просто питоновский словарь с ключом \"response\" и значением в виде массива с тремя элементами. Но так ли это?" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 4, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "data": { 126 | "text/plain": [ 127 | "str" 128 | ] 129 | }, 130 | "execution_count": 4, 131 | "metadata": {}, 132 | "output_type": "execute_result" 133 | } 134 | ], 135 | "source": [ 136 | "type(result)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "Нет, оказывается, это просто строка :( Неужели её ещё нужно парсить? Вспоминать регулярные выражения? Нет! Вспомним лучше [формат json и модуль для работы с ним](https://github.com/elmiram/2016learnpython/blob/master/5%20%D0%A1%D0%B5%D0%BC%D0%B8%D0%BD%D0%B0%D1%80%20-%20json.ipynb)." 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": { 150 | "collapsed": false 151 | }, 152 | "outputs": [ 153 | { 154 | "name": "stdout", 155 | "output_type": "stream", 156 | "text": [ 157 | "\n" 158 | ] 159 | } 160 | ], 161 | "source": [ 162 | "import json\n", 163 | "data = json.loads(result) \n", 164 | "print(type(data))" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "Теперь, кажется, всё в порядке. Собственно, на том же занятии про json мы уже работали с api, только другого сайта: API GitHub.\n" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "## Извлечение информации\n", 179 | "\n", 180 | "Мы получили какой-то ответ от API и даже превратили его в структуру данных, с которой удобно работать в питоне. Но что в этой структуре лежит? Обычно это json, в котором всё устроено как вложенные друг в друга словари и массивы, где ключи -- это какое-то общепонятное слово, а значение -- собственно информация. Подробно это описано в документации к API, но часто всё понятно и без дополнительных объяснений. Например, вот тут:\n", 181 | "\n", 182 | "{\n", 183 | "\n", 184 | "    \"post_type\": \"post\",\n", 185 | "\n", 186 | "    \"text\": \"К 1 апреля в Трижды Краснознаменный завезли новые стикеры.\",\n", 187 | "\n", 188 | "    \"attachment\":\n", 189 | "\n", 190 | "        {\n", 191 | "\n", 192 | "        \"type\":\"photo\"\n", 193 | "\n", 194 | "...\n", 195 | "\n", 196 | "ключ \"post_type\" говорит, что это за тип записи, а ключ \"attachment\" содержит информацию о том, что к этому посту приложено. Оказывается, фото (точнее, просто какая-то картинка).\n", 197 | "\n" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "Попробуем это извлечь." 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 7, 210 | "metadata": { 211 | "collapsed": false 212 | }, 213 | "outputs": [ 214 | { 215 | "name": "stdout", 216 | "output_type": "stream", 217 | "text": [ 218 | "К 1 апреля в Трижды Краснознаменный завезли новые стикеры.\n" 219 | ] 220 | } 221 | ], 222 | "source": [ 223 | "print(data[\"response\"][1][\"text\"])" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "Получилось! \n", 231 | "\n", 232 | "А если второй пост?" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 8, 238 | "metadata": { 239 | "collapsed": false 240 | }, 241 | "outputs": [ 242 | { 243 | "name": "stdout", 244 | "output_type": "stream", 245 | "text": [ 246 | "Если кто ещё не использует телеграм, то вот вам немножко доводов http://telegra.ph/Vozmozhnosti-Telegram-03-30

АПД: Звонки уже запустили\n" 247 | ] 248 | } 249 | ], 250 | "source": [ 251 | "print(data[\"response\"][2][\"text\"])" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "Снова получилось!" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "## Задания\n", 266 | "\n", 267 | "### 1. Смотрим на другие параметры метода wall.get\n", 268 | "\n", 269 | "Что ещё можно передать методу wall.get, кроме id пользователя и числа постов? Как это применить?\n", 270 | "\n", 271 | "### 2. Скачиваем комментарии\n", 272 | "\n", 273 | "Комментарии к постам скачиваются с помощью другого метода: https://vk.com/dev/wall.getComments\n", 274 | "\n", 275 | "Ему нужно передавать идентификаторы записи, комментарии к которой мы хотим получить (эти идентификаторы нам поставляет метод wall.get). Обратите внимание, что VK API позволяет за одно обращение скачать не больше 100 записей и 100 комментариев. Дурова комментируют много, так что всё сразу достать не получится. Но если немного подумать, то можно сделать и это. Как?\n", 276 | "\n", 277 | "Если ваша собственная стена открыта и записи там доступны без авторизации, можете скачать её, и посчитать, кто вас больше комментирует. Какие у комментаторов самые частотные слова?" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "metadata": { 284 | "collapsed": true 285 | }, 286 | "outputs": [], 287 | "source": [] 288 | } 289 | ], 290 | "metadata": { 291 | "celltoolbar": "Raw Cell Format", 292 | "kernelspec": { 293 | "display_name": "Python 3", 294 | "language": "python", 295 | "name": "python3" 296 | }, 297 | "language_info": { 298 | "codemirror_mode": { 299 | "name": "ipython", 300 | "version": 3 301 | }, 302 | "file_extension": ".py", 303 | "mimetype": "text/x-python", 304 | "name": "python", 305 | "nbconvert_exporter": "python", 306 | "pygments_lexer": "ipython3", 307 | "version": "3.4.2" 308 | } 309 | }, 310 | "nbformat": 4, 311 | "nbformat_minor": 1 312 | } 313 | -------------------------------------------------------------------------------- /bot_example/bot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import telebot 3 | import conf 4 | import random 5 | import shelve 6 | from telebot import types 7 | 8 | bot = telebot.TeleBot(conf.TOKEN, threaded=False) 9 | 10 | with open('reviews.csv', 'r', encoding='utf-8') as f: 11 | reviews = {} 12 | for line in f: 13 | num, text = line.strip().split('\t') 14 | reviews[num] = text 15 | review_keys = list(reviews.keys()) 16 | 17 | keyboard = types.ReplyKeyboardMarkup(row_width=3) 18 | btn1 = types.KeyboardButton('+') 19 | btn2 = types.KeyboardButton('-') 20 | btn3 = types.KeyboardButton('=') 21 | keyboard.add(btn1, btn2, btn3) 22 | 23 | shelve_name = 'shelve.db' # Файл с хранилищем 24 | 25 | 26 | def set_user_review(chat_id, review): 27 | """ 28 | Записываем юзера в игроки и запоминаем, что он должен ответить. 29 | :param chat_id: id юзера 30 | :param estimated_answer: правильный ответ (из БД) 31 | """ 32 | with shelve.open(shelve_name) as storage: 33 | storage[str(chat_id)] = review 34 | 35 | 36 | def finish_user_review(chat_id): 37 | """ 38 | Заканчиваем игру текущего пользователя и удаляем правильный ответ из хранилища 39 | :param chat_id: id юзера 40 | """ 41 | with shelve.open(shelve_name) as storage: 42 | del storage[str(chat_id)] 43 | 44 | 45 | def get_user_review(chat_id): 46 | """ 47 | Получаем правильный ответ для текущего юзера. 48 | В случае, если человек просто ввёл какие-то символы, не начав игру, возвращаем None 49 | :param chat_id: id юзера 50 | :return: (str) Правильный ответ / None 51 | """ 52 | with shelve.open(shelve_name) as storage: 53 | try: 54 | review = storage[str(chat_id)] 55 | return review 56 | # Если человек не играет, ничего не возвращаем 57 | except KeyError: 58 | return None 59 | 60 | 61 | # этот обработчик запускает функцию send_welcome, когда пользователь отправляет команды /start или /help 62 | @bot.message_handler(commands=['help']) 63 | def send_welcome(message): 64 | bot.send_message(message.chat.id, 65 | "Здравствуйте! Это бот для разметки отзывов на кинофильмы.\n Положительные отзывы отмечаются плюсом +, отрицательные минусом -, а нейтральные знаком равно =.") 66 | 67 | 68 | @bot.message_handler(commands=['start']) 69 | def send_first_review(message): 70 | review_num = random.choice(review_keys) 71 | bot.send_message(message.chat.id, reviews[review_num], reply_markup=keyboard) 72 | set_user_review(message.chat.id, review_num) 73 | 74 | 75 | @bot.message_handler(regexp='[-+=]') # этот обработчик реагирует на символы разметки 76 | def get_answer(message): 77 | review_num = get_user_review(message.chat.id) 78 | if review_num: 79 | with open('results.csv', 'a', encoding='utf-8') as results: 80 | results.write(review_num + '\t' + message.text + '\n') 81 | review_num = random.choice(review_keys) 82 | bot.send_message(message.chat.id, reviews[review_num], reply_markup=keyboard) 83 | set_user_review(message.chat.id, review_num) 84 | else: 85 | bot.send_message(message.chat.id, 'Вы не разметили отзыв.') 86 | 87 | 88 | if __name__ == '__main__': 89 | bot.polling(none_stop=True) -------------------------------------------------------------------------------- /bot_example/conf.py: -------------------------------------------------------------------------------- 1 | TOKEN = "390595991:AAG_4wqdfxtSMnJKow8YjcSxNOxjyAWY3PU" 2 | -------------------------------------------------------------------------------- /bot_example/results.csv: -------------------------------------------------------------------------------- 1 | 5 + 2 | 3 = 3 | 4 - 4 | 5 + 5 | 10 - 6 | 4 - 7 | 1 - 8 | 3 - 9 | 5 + 10 | 4 - 11 | 8 = 12 | 8 = 13 | 3 - 14 | 9 + 15 | 9 + 16 | 4 - 17 | 8 + 18 | 7 = 19 | 3 - 20 | 7 = 21 | 3 = 22 | 4 - 23 | 5 + 24 | 10 - 25 | 8 + 26 | 2 + 27 | 4 - 28 | 9 + 29 | 7 = 30 | 2 + 31 | 4 - 32 | 5 + 33 | 9 + 34 | 4 - 35 | 4 - 36 | 7 - 37 | 2 + 38 | 6 + 39 | 8 + 40 | 3 = 41 | 7 + 42 | 10 - 43 | 4 - 44 | -------------------------------------------------------------------------------- /bot_example/reviews.csv: -------------------------------------------------------------------------------- 1 | 1 Скучно и примитивно... 2 | 2 Зря так пугают !Мне мультик был мил и даже приятен. Для двд дома сойдёт!Есть фильмы попустее ))... 3 | 3 В детстве была фанатом серии книг Волкова о девочке Элли. Ожидала чего-то ближе к тексту. Поэтому и разочаровалась, наверное, в "современной" версии. Обычный , ничем не примечательный детский мульт, коих штампуется множество ежедневно.... 4 | 4 Плохо... 5 | 5 Супер! Мне показалось что он даже лучше первой части, сами съемки широкоформатные просто как в жизни , 3D эффект тоже хорошо сделан правда в динамических сценах не до него , юмор простой , фильм смотрится легко и не жаль ни времени ни денег, буду рекомендовать всем друзьям. Вот почему его не реклами... 6 | 6 Отличный фильм, сильнее первой части. Юмор, экшн, игра актеров - все на высшем уровне.... 7 | 7 Да уж, такого трешака от Марвел я еще не видел... В целом, конечно, графика космическая (в прямом и переносном смысле), бабла на нее не пожалели, да и технологии видно шагают вперед, все уже как настоящее. Плюс в фильме неплохое 3д. Но.. что это было??? Весь фильм они друг с другом рубятся, юморят, ... 8 | 8 Нормуль. Своеобразная трактовка легенды. Запомнился побег в лондиниуме. Музыка норм. Спец. Эффекты супер. Юмор присутствует.... 9 | 9 Здравствуйте!Фильм "Дом летающих кинжалов " понравился. Несравненная актриса Чжан Цзыи, чьи роли замечательные.Спасибо.Порадовали.... 10 | 10 не тратте своё время... -------------------------------------------------------------------------------- /data/lang_codes.csv: -------------------------------------------------------------------------------- 1 | Абазинский,abq 2 | Абхазский,abk 3 | Авадхи,awa 4 | Аварский,ava 5 | Авестийский,ave 6 | Адангме,ada 7 | Адыгейский,ady 8 | Азербайджанский,aze 9 | Аймара,aym 10 | Айну,ain 11 | Акан,aka 12 | Аккадский,akk 13 | Албанский,sqi 14 | Алеутский,ale 15 | Алтайский,alt 16 | Амхарский,amh 17 | Английский,eng 18 | Арабский,ara 19 | Аравакский,arw 20 | Арамейский,arc 21 | Арапахо,arp 22 | Арауканский,arn 23 | Армянский,hye/axm/xcl 24 | Ассамский,asm 25 | Ассирийский,aii 26 | Атапачские,ath 27 | Афарский,aar 28 | Африкаанс,afr 29 | Африхили,afh 30 | Ахвахский,akv 31 | Ацтекский,nah 32 | Ачехский,ace 33 | Ачоли,ach 34 | Балийский,ban 35 | Бамбара,bam 36 | Банда,bad 37 | Баса,bas 38 | Баскский,eus 39 | Башкирский,bak 40 | Беджа,bej 41 | Белорусский,bel 42 | Белуджский,bal 43 | Бемба,bem 44 | Бенгальский,ben 45 | Бикольский,bik 46 | Бини,bin 47 | Бирманский,mya 48 | Бислама,bis 49 | Болгарский,bul 50 | Боснийский,bos 51 | Брауи,brh 52 | Бретонский,bre 53 | Бугийский,bug 54 | Бурятский,bua 55 | Бходжпури,bho 56 | Ваи,vai 57 | Валлийский,cym 58 | Варай,war 59 | Вашо,was 60 | Венгерский,hun 61 | Венда,ven 62 | Вепсский,vep 63 | Воламо,wal 64 | Волапюк,vol 65 | Волоф,wol 66 | Вьетнамский,vie 67 | Га,gaa 68 | Гавайский,haw 69 | Гагаузский,gag 70 | Гайо,gay 71 | Галисийский,glg 72 | Ганда,lug 73 | Гереро,her 74 | Геэз,gez 75 | Гилбертский,gil 76 | Гонди,gon 77 | Готский,got 78 | Гребо,grb 79 | Гренландский,kal 80 | Греческий (новогреческий),ell 81 | Грузинский,kat 82 | Гуарани,grn 83 | Гуджарати,guj 84 | Гэльский,gla 85 | Дакота,dak 86 | Даргинский,dar 87 | Датский,dan 88 | Делавэрский,del 89 | Дзонг-кэ,dzo 90 | Дивехи (Мальдивский),div 91 | Динка,din 92 | Диула (Дьюла),dyu 93 | Догри,doi 94 | Древнегреческий,grc 95 | Древнеегипетский,egy 96 | Древнерусский,orv 97 | Древнесаксонский,osx 98 | Дуала,dua 99 | Дунганский,dng 100 | Еврейско-арабский,jrb 101 | Еврейско-персидский,jpr 102 | Зенагский,zen 103 | Зулу,zul 104 | Зуньи,zun 105 | Ибанский,iba 106 | Иврит,heb 107 | Игбо,ibo 108 | Идиш,yid 109 | Илоко,ilo 110 | Ингушский,inh 111 | Индонезийский,ind 112 | Интерлингва,ina 113 | Интерлингве,ile 114 | Инуктитут,iku 115 | Инупиак,ipk 116 | Ирландский,gle 117 | Исландский,isl 118 | Испанский,spa 119 | Итальянский,ita 120 | Ительменский,itl 121 | Йоруба,yor 122 | Кабардино-черкесский,kbd 123 | Кабильский,kab 124 | Кави,kaw 125 | Каддо,cad 126 | Казахский,kaz 127 | Калмыцкий,xal 128 | Камба,kam 129 | Каннада,kan 130 | Канури,kau 131 | Караимский,kdr 132 | Каракалпакский,kaa 133 | Карачаево-балкарский,krc 134 | Карельский,krl 135 | Кариб,car 136 | Каталанский,cat 137 | Качинский,kac 138 | Кашмири,kas 139 | Кечуа,que 140 | Кикуйю,kik 141 | Киньяма,kua 142 | Киргизский,kir 143 | Китайский,zho 144 | Коми,kom 145 | Коми-пермяцкий,koi 146 | Конго,kon 147 | Конкани,kok 148 | Коптский,cop 149 | Корейский,kor 150 | Корнский,cor 151 | Корсиканский,cos 152 | Корякский,kpy 153 | Коса,xho 154 | Кпелле,kpe 155 | Крик,mus 156 | Крымско-татарский,crh 157 | Кумыкский,kum 158 | Курдский,kur 159 | Курух,kru 160 | Кусайе,kos 161 | Кутенай,kut 162 | Кхаси,kha 163 | Кхмерский,khm 164 | Ладино,lad 165 | Лакский,lbe 166 | Ламба,lam 167 | Лаосский,lao 168 | Латинский,lat 169 | Латышский,lav 170 | Лахнда,lah 171 | Лезгинский,lez 172 | Лингала,lin 173 | Литовский,lit 174 | Лози,loz 175 | Луба-катанга,lub 176 | Луисеньо,lui 177 | Лунда,lun 178 | Луо (Кения и Танзания),luo 179 | Люксембургский,ltz 180 | Магахи,mag 181 | Мадурский,mad 182 | Майтхили,mai 183 | Макассарский,mak 184 | Македонский,mkd 185 | Малагасийский,mlg 186 | Малайский,msa 187 | Малаялам,mal 188 | Мальтийский,mlt 189 | Мандинго,man 190 | Манипури,mni 191 | Мансийский (Вогульский),mns 192 | Маори,mri 193 | Маратхи,mar 194 | Марвари,mwr 195 | Марийский (черемисский),chm 196 | Маршалльский,mah 197 | Масаи,mas 198 | Менде,men 199 | Микмак,mic 200 | Минангкабау,min 201 | Мокшанский,mdf 202 | Молдавский,mol 203 | Монго,lol 204 | Монгольский,mon 205 | Мооре,mos 206 | Мохаук,moh 207 | Мэнский (Мэнкский),glv 208 | Навахо,nav 209 | Нанайский (гольдский),gld 210 | Науру,nau 211 | Ндебеле северный,nde 212 | Ндебеле южный,nbl 213 | Ндунга,ndo 214 | Неварский,new 215 | Неидентифицированный,und 216 | Немецкий,deu 217 | Ненецкий (юрако-самоедский),yrk 218 | Непальский,nep 219 | Нзима,nzi 220 | Нивхский (гиляцкий),niv 221 | Нидерландский (Голландский),nld 222 | Нидерландский средневековый,dum 223 | Ниуэ,niu 224 | Ногайский,nog 225 | Норвежский [Norsk (bokm?l)]?,nor 226 | Ньоро,nyo 227 | Ньямвези,nym 228 | Ньянджа,nya 229 | Ньянколе,nyn 230 | Нюнорск (новонорвежский) [Norsk (nynorsk)]?,nno 231 | Оджибве,oji 232 | Окситанский,oci 233 | Ория,ori 234 | Оромо,orm 235 | Оседжи,osa 236 | Осетинский,oss 237 | Палау,pau 238 | Пали,pli 239 | Пампанга,pam 240 | Пангасинан,pag 241 | Папьяменто,pap 242 | Пенджабский,pan 243 | Персидский,fas 244 | Пехлевийский,pal 245 | Польский,pol 246 | Понапе,pon 247 | Португальский,por 248 | Пушту,pus 249 | Раджастхани,raj 250 | Разных семей языки,mul 251 | Раротонга,rar 252 | Ретороманский,roh 253 | Руанда,kin 254 | Румынский,ron 255 | Рунди,run 256 | Русский,rus 257 | Саамский,smi 258 | Самаритянский арамейский,sam 259 | Самоанский,smo 260 | Санго,sag 261 | Сандаве,sad 262 | Санскрит,san 263 | Сапотекский,zap 264 | Сардинский,srd 265 | Свази,ssw 266 | Себуано,ceb 267 | Селькупский,sel 268 | Сербский,srp 269 | Серер,srr 270 | Сибирскотатарский,sty 271 | Сидама,sid 272 | Сиксика,bla 273 | Сингальский,sin 274 | Синдхи,snd 275 | Сирийский,syr 276 | Словацкий,slk 277 | Словенский,slv 278 | Согдийский,sog 279 | Сомали,som 280 | Сото северный,nso 281 | Сото южный,sot 282 | Среднеанглийский,enm 283 | Средневерхненемецкий,gmh 284 | Среднеирландский,mga 285 | Среднефранцузский,frm 286 | Староанглийский,ang 287 | Староверхненемецкий,goh 288 | Староирландский,sga 289 | Старонорвежский,non 290 | Староперсидский,peo 291 | Старопровансальский,pro 292 | Старотурецкий,ota 293 | Старофранцузский,fro 294 | Суахили,swa 295 | Сукума,suk 296 | Сунданский,sun 297 | Сусу,sus 298 | Табасаранский,tab 299 | Тагальский,tgl 300 | Таджикский,tgk 301 | Таитянский,tah 302 | Тайский,tha 303 | Талышский,tly 304 | Тамашек,tmh 305 | Тамильский,tam 306 | Татарский,tat 307 | Тви,twi 308 | Телугу,tel 309 | Темне,tem 310 | Терено,ter 311 | Тибетский,bod 312 | Тиви,tiw 313 | Тигре,tig 314 | Тигринья,tir 315 | Тлингит,tli 316 | Тонга (Ньяса),tog 317 | Тонганский,ton 318 | Трукский,chk 319 | Тсвана,tsn 320 | Тсонга,tso 321 | Тувинский,tyv 322 | Тумбука,tum 323 | Турецкий,tur 324 | Туркменский,tuk 325 | Угаритский,uga 326 | Удмуртский (вотяцкий),udm 327 | Удэгейский,ude 328 | Узбекский,uzb 329 | Уйгурский,uig 330 | Украинский,ukr 331 | Ульчский,ulc 332 | Умбунду,umb 333 | Урду,urd 334 | Фанг,fan 335 | Фанти,fat 336 | Фарерский,fao 337 | Фиджи,fij 338 | Филиппинский,fil 339 | Финикийский,phn 340 | Финский (Suomi),fin 341 | Фон,fon 342 | Французский,fra 343 | Фризский,fry 344 | Фулах,ful 345 | Хайда,hai 346 | Хакасский,kjh 347 | Хантыйский (остяцкий),kca 348 | Хауса,hau 349 | Хилигайнон,hil 350 | Хинди,hin 351 | Хиримоту,hmo 352 | Хорватский,hrv 353 | Хотанский,kho 354 | Хупа,hup 355 | Цахурский,tkr 356 | Церковнославянский (Старославянский),chu 357 | Цимшиан,tsi 358 | Цыганский,rom 359 | Чагатайский,chg 360 | Чаморро,cha 361 | Чероки,chr 362 | Чеченский,che 363 | Чешский,ces 364 | Чжуанский,zha 365 | Чибча,chb 366 | Чинук жаргон,chn 367 | Чоктав,cho 368 | Чувашский,chv 369 | Чукотский,ckt 370 | Шайенн (Чейенн),chy 371 | Шанский,shn 372 | Шведский,swe 373 | Шона,sna 374 | Шорский,cjs 375 | Шотландский (англо-шотландский),sco 376 | Шугнанский,sgh 377 | Шумерский,sux 378 | Эве,ewe 379 | Эвенкийский (тунгусский),evn 380 | Эвенский,eve 381 | Эвондо,ewo 382 | Экаджук,eka 383 | Эламский,elx 384 | Эрзянский,myv 385 | Эсперанто,epo 386 | Эстонский,est 387 | Эфик,efi 388 | Яванский,jav 389 | Якутский (Саха),sah 390 | Яо,yao 391 | Яп,yap 392 | Японский,jpn 393 | -------------------------------------------------------------------------------- /data/text.json: -------------------------------------------------------------------------------- 1 | {"text":"Сначала","analysis":[{"lex":"сначала","gr":"ADV="}]} 2 | {"text":", "} 3 | {"text":"по","analysis":[{"lex":"по","gr":"PR="}]} 4 | {"text":" "} 5 | {"text":"сведениям","analysis":[{"lex":"сведение","gr":"S,n,inan=dat,pl"}]} 6 | {"text":" «"} 7 | {"text":"Известий","analysis":[{"lex":"известие","gr":"S,n,inan=gen,pl"}]} 8 | {"text":"», "} 9 | {"text":"возник","analysis":[{"lex":"возникать","gr":"V,intr=praet,sg,indic,m,pf"}]} 10 | {"text":" "} 11 | {"text":"пожар","analysis":[{"lex":"пожар","gr":"S,m,inan=acc,sg"},{"lex":"пожар","gr":"S,m,inan=nom,sg"}]} 12 | {"text":". "} 13 | {"text":"\\s"} 14 | {"text":"К","analysis":[{"lex":"к","gr":"PR="}]} 15 | {"text":" "} 16 | {"text":"месту","analysis":[{"lex":"место","gr":"S,n,inan=dat,sg"}]} 17 | {"text":" "} 18 | {"text":"происшествия","analysis":[{"lex":"происшествие","gr":"S,n,inan=acc,pl"},{"lex":"происшествие","gr":"S,n,inan=gen,sg"},{"lex":"происшествие","gr":"S,n,inan=nom,pl"}]} 19 | {"text":" "} 20 | {"text":"оперативно","analysis":[{"lex":"оперативно","gr":"ADV="}]} 21 | {"text":" "} 22 | {"text":"прибыли","analysis":[{"lex":"прибывать","gr":"V,intr=praet,pl,indic,pf"}]} 23 | {"text":" "} 24 | {"text":"около","analysis":[{"lex":"около","gr":"PR="}]} 25 | {"text":" "} 26 | {"text":"10"} 27 | {"text":" "} 28 | {"text":"пожарных","analysis":[{"lex":"пожарный","gr":"S,m,anim=abl,pl"},{"lex":"пожарный","gr":"S,m,anim=acc,pl"},{"lex":"пожарный","gr":"S,m,anim=gen,pl"}]} 29 | {"text":" "} 30 | {"text":"машин","analysis":[{"lex":"машина","gr":"S,f,inan=gen,pl"}]} 31 | {"text":", "} 32 | {"text":"но","analysis":[{"lex":"но","gr":"CONJ="}]} 33 | {"text":" "} 34 | {"text":"только","analysis":[{"lex":"только","gr":"PART="}]} 35 | {"text":" "} 36 | {"text":"они","analysis":[{"lex":"они","gr":"SPRO,pl=nom"}]} 37 | {"text":" "} 38 | {"text":"приступили","analysis":[{"lex":"приступать","gr":"V,intr=praet,pl,indic,pf"}]} 39 | {"text":" "} 40 | {"text":"к","analysis":[{"lex":"к","gr":"PR="}]} 41 | {"text":" "} 42 | {"text":"работе","analysis":[{"lex":"работа","gr":"S,f,inan=abl,sg"},{"lex":"работа","gr":"S,f,inan=dat,sg"}]} 43 | {"text":", "} 44 | {"text":"как","analysis":[{"lex":"как","gr":"CONJ="}]} 45 | {"text":" "} 46 | {"text":"начали","analysis":[{"lex":"начинать","gr":"V,tran=praet,pl,indic,pf"}]} 47 | {"text":" "} 48 | {"text":"рваться","analysis":[{"lex":"рваться","gr":"V,ipf,intr=inf"}]} 49 | {"text":" "} 50 | {"text":"снаряды","analysis":[{"lex":"снаряд","gr":"S,m,inan=acc,pl"},{"lex":"снаряд","gr":"S,m,inan=nom,pl"}]} 51 | {"text":" "} 52 | {"text":"и","analysis":[{"lex":"и","gr":"CONJ="}]} 53 | {"text":" "} 54 | {"text":"пожарных","analysis":[{"lex":"пожарный","gr":"S,m,anim=abl,pl"},{"lex":"пожарный","gr":"S,m,anim=acc,pl"},{"lex":"пожарный","gr":"S,m,anim=gen,pl"}]} 55 | {"text":" "} 56 | {"text":"срочно","analysis":[{"lex":"срочно","gr":"ADV="}]} 57 | {"text":" "} 58 | {"text":"отозвали","analysis":[{"lex":"отзывать","gr":"V,tran=praet,pl,indic,pf"}]} 59 | {"text":". "} 60 | {"text":"\\s"} 61 | {"text":"Официально","analysis":[{"lex":"официально","gr":"ADV="}]} 62 | {"text":" "} 63 | {"text":"о","analysis":[{"lex":"о","gr":"PR="}]} 64 | {"text":" "} 65 | {"text":"причинах","analysis":[{"lex":"причина","gr":"S,f,inan=abl,pl"}]} 66 | {"text":" "} 67 | {"text":"происшествия","analysis":[{"lex":"происшествие","gr":"S,n,inan=acc,pl"},{"lex":"происшествие","gr":"S,n,inan=gen,sg"},{"lex":"происшествие","gr":"S,n,inan=nom,pl"}]} 68 | {"text":" "} 69 | {"text":"военные","analysis":[{"lex":"военный","gr":"A=acc,pl,plen,inan"},{"lex":"военный","gr":"A=nom,pl,plen"}]} 70 | {"text":" "} 71 | {"text":"пока","analysis":[{"lex":"пока","gr":"CONJ="}]} 72 | {"text":" "} 73 | {"text":"молчат","analysis":[{"lex":"молчать","gr":"V,ipf,intr=inpraes,pl,indic,3p"}]} 74 | {"text":". "} 75 | {"text":"\\s"} 76 | {"text":"Как","analysis":[{"lex":"как","gr":"ADVPRO="}]} 77 | {"text":" "} 78 | {"text":"удалось","analysis":[{"lex":"удаваться","gr":"V,intr=praet,sg,indic,n,pf"}]} 79 | {"text":" "} 80 | {"text":"выяснить","analysis":[{"lex":"выяснивать","gr":"V=inf,pf"}]} 81 | {"text":" «"} 82 | {"text":"Известиям","analysis":[{"lex":"известие","gr":"S,n,inan=dat,pl"}]} 83 | {"text":"», "} 84 | {"text":"саперы","analysis":[{"lex":"сапер","gr":"S,m,anim=nom,pl"}]} 85 | {"text":" "} 86 | {"text":"проводили","analysis":[{"lex":"проводить","gr":"V=praet,pl,indic,ipf,tran"}]} 87 | {"text":" "} 88 | {"text":"штатную","analysis":[{"lex":"штатный","gr":"A=acc,sg,plen,f"}]} 89 | {"text":" "} 90 | {"text":"работу","analysis":[{"lex":"работа","gr":"S,f,inan=acc,sg"}]} 91 | {"text":" "} 92 | {"text":"по","analysis":[{"lex":"по","gr":"PR="}]} 93 | {"text":" "} 94 | {"text":"подрыву","analysis":[{"lex":"подрыв","gr":"S,m,inan=dat,sg"}]} 95 | {"text":" "} 96 | {"text":"неразорвавшихся","analysis":[{"lex":"неразорвавшийся","gr":"A,plen=abl,pl"},{"lex":"неразорвавшийся","gr":"A,plen=gen,pl"},{"lex":"неразорвавшийся","gr":"A,plen=acc,pl,anim"}]} 97 | {"text":" "} 98 | {"text":"боеприпасов","analysis":[{"lex":"боеприпас","gr":"S,m,inan=gen,pl"}]} 99 | {"text":", "} 100 | {"text":"находящихся","analysis":[{"lex":"находиться","gr":"V,intr=inpraes,abl,pl,partcp,plen,ipf,act"},{"lex":"находиться","gr":"V,intr=inpraes,gen,pl,partcp,plen,ipf,act"},{"lex":"находиться","gr":"V,intr=inpraes,acc,pl,partcp,plen,ipf,act,anim"}]} 101 | {"text":" "} 102 | {"text":"в","analysis":[{"lex":"в","gr":"PR="}]} 103 | {"text":" "} 104 | {"text":"данном","analysis":[{"lex":"данный","gr":"A,plen=abl,sg,m"},{"lex":"данный","gr":"A,plen=abl,sg,n"}]} 105 | {"text":" "} 106 | {"text":"районе","analysis":[{"lex":"район","gr":"S,m,inan=abl,sg"}]} 107 | {"text":". "} 108 | {"text":"\\s"} 109 | {"text":"Всего","analysis":[{"lex":"всего","gr":"PART="}]} 110 | {"text":" "} 111 | {"text":"этих","analysis":[{"lex":"этот","gr":"APRO=abl,pl"},{"lex":"этот","gr":"APRO=gen,pl"},{"lex":"этот","gr":"APRO=acc,pl,anim"}]} 112 | {"text":" "} 113 | {"text":"снарядов","analysis":[{"lex":"снаряд","gr":"S,m,inan=gen,pl"}]} 114 | {"text":" "} 115 | {"text":"было","analysis":[{"lex":"быть","gr":"V,intr=praet,sg,indic,n,ipf"}]} 116 | {"text":" "} 117 | {"text":"три","analysis":[{"lex":"три","gr":"NUM=nom"},{"lex":"три","gr":"NUM=acc,inan"}]} 118 | {"text":" "} 119 | {"text":"с","analysis":[{"lex":"с","gr":"PR="}]} 120 | {"text":" "} 121 | {"text":"половиной","analysis":[{"lex":"половина","gr":"S,f,inan=ins,sg"}]} 122 | {"text":" "} 123 | {"text":"тысячи","analysis":[{"lex":"тысяча","gr":"S,f,inan=acc,pl"},{"lex":"тысяча","gr":"S,f,inan=gen,sg"},{"lex":"тысяча","gr":"S,f,inan=nom,pl"}]} 124 | {"text":". "} 125 | {"text":"\\s"} 126 | {"text":"Их","analysis":[{"lex":"их","gr":"APRO=abl,pl"},{"lex":"их","gr":"APRO=dat,pl"},{"lex":"их","gr":"APRO=gen,pl"},{"lex":"их","gr":"APRO=ins,pl"},{"lex":"их","gr":"APRO=nom,pl"},{"lex":"их","gr":"APRO=nom,sg,f"},{"lex":"их","gr":"APRO=acc,sg,m,anim"},{"lex":"их","gr":"APRO=gen,sg,m"},{"lex":"их","gr":"APRO=gen,sg,n"},{"lex":"их","gr":"APRO=acc,sg,n"},{"lex":"их","gr":"APRO=nom,sg,n"},{"lex":"их","gr":"APRO=abl,sg,f"},{"lex":"их","gr":"APRO=dat,sg,f"},{"lex":"их","gr":"APRO=gen,sg,f"},{"lex":"их","gr":"APRO=ins,sg,f"},{"lex":"их","gr":"APRO=abl,sg,m"},{"lex":"их","gr":"APRO=abl,sg,n"},{"lex":"их","gr":"APRO=dat,sg,m"},{"lex":"их","gr":"APRO=dat,sg,n"},{"lex":"их","gr":"APRO=acc,sg,f"},{"lex":"их","gr":"APRO=acc,pl,inan"},{"lex":"их","gr":"APRO=acc,sg,m,inan"},{"lex":"их","gr":"APRO=nom,sg,m"},{"lex":"их","gr":"APRO=ins,sg,m"},{"lex":"их","gr":"APRO=ins,sg,n"},{"lex":"их","gr":"APRO=acc,pl,anim"},{"lex":"их","gr":"APRO=abl,pl,n"},{"lex":"их","gr":"APRO=acc,pl,n"},{"lex":"их","gr":"APRO=dat,pl,n"},{"lex":"их","gr":"APRO=gen,pl,n"},{"lex":"их","gr":"APRO=ins,pl,n"},{"lex":"их","gr":"APRO=nom,pl,n"},{"lex":"их","gr":"APRO=abl,pl,m"},{"lex":"их","gr":"APRO=acc,pl,m"},{"lex":"их","gr":"APRO=acc,sg,m"},{"lex":"их","gr":"APRO=dat,pl,m"}]} 127 | {"text":" "} 128 | {"text":"осколки","analysis":[{"lex":"осколок","gr":"S,m,inan=acc,pl"},{"lex":"осколок","gr":"S,m,inan=nom,pl"}]} 129 | {"text":" "} 130 | {"text":"попали","analysis":[{"lex":"попадать","gr":"V,intr=praet,pl,indic,pf"}]} 131 | {"text":" "} 132 | {"text":"на","analysis":[{"lex":"на","gr":"PR="}]} 133 | {"text":" "} 134 | {"text":"другую","analysis":[{"lex":"другой","gr":"APRO=acc,sg,f"}]} 135 | {"text":" "} 136 | {"text":"площадку","analysis":[{"lex":"площадка","gr":"S,f,inan=acc,sg"}]} 137 | {"text":" "} 138 | {"text":"для","analysis":[{"lex":"для","gr":"PR="}]} 139 | {"text":" "} 140 | {"text":"хранения","analysis":[{"lex":"хранение","gr":"S,n,inan=acc,pl"},{"lex":"хранение","gr":"S,n,inan=gen,sg"},{"lex":"хранение","gr":"S,n,inan=nom,pl"}]} 141 | {"text":", "} 142 | {"text":"где","analysis":[{"lex":"где","gr":"ADVPRO="}]} 143 | {"text":" "} 144 | {"text":"находилось","analysis":[{"lex":"находиться","gr":"V,intr=praet,sg,indic,n,ipf"}]} 145 | {"text":" "} 146 | {"text":"еще","analysis":[{"lex":"еще","gr":"ADV="}]} 147 | {"text":" "} 148 | {"text":"около","analysis":[{"lex":"около","gr":"ADV="}]} 149 | {"text":" "} 150 | {"text":"5"} 151 | {"text":" "} 152 | {"text":"тысяч","analysis":[{"lex":"тысяча","gr":"S,f,inan=gen,pl"}]} 153 | {"text":" "} 154 | {"text":"боеприпасов","analysis":[{"lex":"боеприпас","gr":"S,m,inan=gen,pl"}]} 155 | {"text":"."} 156 | {"text":"\n"} 157 | -------------------------------------------------------------------------------- /data/text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Сначала, 6 | по 7 | сведениям « 8 | Известий», 9 | возник 10 | пожар. 11 | 12 | 13 | К 14 | месту 15 | происшествия 16 | оперативно 17 | прибыли 18 | около 10 19 | пожарных 20 | машин, 21 | но 22 | только 23 | они 24 | приступили 25 | к 26 | работе, 27 | как 28 | начали 29 | рваться 30 | снаряды 31 | и 32 | пожарных 33 | срочно 34 | отозвали. 35 | 36 | 37 | Официально 38 | о 39 | причинах 40 | происшествия 41 | военные 42 | пока 43 | молчат. 44 | 45 | 46 | Как 47 | удалось 48 | выяснить « 49 | Известиям», 50 | саперы 51 | проводили 52 | штатную 53 | работу 54 | по 55 | подрыву 56 | неразорвавшихся 57 | боеприпасов, 58 | находящихся 59 | в 60 | данном 61 | районе. 62 | 63 | 64 | Всего 65 | этих 66 | снарядов 67 | было 68 | три 69 | с 70 | половиной 71 | тысячи. 72 | 73 | 74 | Их 75 | осколки 76 | попали 77 | на 78 | другую 79 | площадку 80 | для 81 | хранения, 82 | где 83 | находилось 84 | еще 85 | около 5 86 | тысяч 87 | боеприпасов. 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /flask_example/my_app.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from flask import Flask 4 | from flask import url_for, render_template, request, redirect 5 | 6 | app = Flask(__name__) 7 | 8 | 9 | @app.route('/') 10 | def index(): 11 | urls = {'главная (эта страница)': url_for('index'), 12 | 'привет (переменные в url)': url_for('hi'), 13 | 'форма (форма и ответ на одном url)': url_for('form'), 14 | 'форма про книги (просто форма)': url_for('books'), 15 | 'спасибо (попадаем сюда только после формы про книги)': url_for('thanks'), 16 | 'время (используем redirect)': url_for('time_redirect'),} 17 | return render_template('index.html', urls=urls) 18 | 19 | 20 | @app.route('/hi') 21 | @app.route('/hi/') 22 | def hi(user=None): 23 | if user is None: 24 | user = 'friend' 25 | return '

Привет, ' + user + '!

' 26 | 27 | 28 | @app.route('/form') 29 | def form(): 30 | if request.args: 31 | name = request.args['name'] 32 | age = request.args['age'] 33 | st = True if 'student' in request.args else False 34 | return render_template('answer.html', name=name, age=age, student=st) 35 | return render_template('question.html') 36 | 37 | 38 | @app.route('/books') 39 | def books(): 40 | return render_template('books.html') 41 | 42 | 43 | @app.route('/thanks') 44 | def thanks(): 45 | if request.args: 46 | name = request.args['name'] 47 | book = request.args['book'] 48 | return render_template('thanks.html', name=name, book=book) 49 | return redirect(url_for('books')) 50 | 51 | 52 | @app.route('/time') 53 | def time_redirect(): 54 | h = datetime.datetime.today().hour 55 | if 10 < h < 18: 56 | return redirect(url_for('index')) 57 | return redirect(url_for('hi')) 58 | 59 | 60 | if __name__ == '__main__': 61 | app.run(debug=True) -------------------------------------------------------------------------------- /flask_example/templates/answer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Привет! 6 | 7 | 8 |

Привет, {{ name }}!

9 |

Твой возраст: {{ age }}

10 |

11 | Ты 12 | {% if not student %} 13 | не 14 | {% endif %} 15 | студент. 16 |

17 | 18 | -------------------------------------------------------------------------------- /flask_example/templates/books.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Книги 6 | 7 | 8 |
9 |
10 |
11 | 12 |
13 | 14 | -------------------------------------------------------------------------------- /flask_example/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Вот мой сайт 6 | 7 | 8 |

Привет, мир!

9 |

Список страниц на этом сайте:

10 |
    11 | {% for name, url in urls.items() %} 12 |
  • {{ name }}
  • 13 | {% endfor %} 14 |
15 | 16 | -------------------------------------------------------------------------------- /flask_example/templates/question.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Форма 6 | 7 | 8 |
9 |
10 |
11 |
12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /flask_example/templates/thanks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Спасибо за ответ! 6 | 7 | 8 |

Спасибо за ответ, {{ name }}!

9 |

Мы записали, что это ваша любимая книга: {{ book }}.

10 | 11 | -------------------------------------------------------------------------------- /img/one_does_not_mystem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmiram/2016learnpython/1a83c46f479135aceef5d411d32c928c83404f09/img/one_does_not_mystem.jpg -------------------------------------------------------------------------------- /img/tedbot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmiram/2016learnpython/1a83c46f479135aceef5d411d32c928c83404f09/img/tedbot1.png -------------------------------------------------------------------------------- /img/tedbot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmiram/2016learnpython/1a83c46f479135aceef5d411d32c928c83404f09/img/tedbot2.png -------------------------------------------------------------------------------- /matplotlib+vk+homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание 2 | ## Matplotlib + VK 3 | 4 | В домашнем задании вам нужно написать программу, которая обращается к открытому 5 | (то есть такому, чтобы для просмотра контента не нужна была авторизация) сообществу 6 | VK.com, выкачивает посты со стены и комментарии к ним. 7 | Выберите сообщество поживее, чтобы там было много постов и много комментариев к 8 | ним. Обязательное условие: программа должна уметь скачивать со стены больше, чем 100 постов, и больше, чем 100 комментариев к посту 9 | (если их действительно больше 100). Кроме выкачивания нужно сделать следующее: 10 | 11 | * Посчитайте питоном длину каждого поста и каждого комментария в словах. 12 | 13 | * Создайте график, описывающий, как длина поста соотносится со средней длиной комментариев. 14 | 15 | * Помимо выкачивания постов и комментариев, программа должна смотреть в профиль пользователя, который написал пост или сделал комментарий, 16 | и узнавать о нём социолингвистическую информацию: возраст и город (если они указаны). 17 | Для города достаточно id (то есть название узнавать не обязательно, хотя это можно сделать [средствами API](https://vk.com/dev/database.getCitiesById), а возраст нужно уметь вычислять. 18 | 19 | * Для каждого возраста нужно вычислить среднюю длину поста и комментария, нарисовать график, отражающий эту информацию. 20 | 21 | * Аналогичные графики нужно нарисовать для каждого города. 22 | 23 | * Выложить скачанные тексты, построенные графики и сам код. 24 | 25 | ## Дедлайн - 1 мая 23:59 26 | -------------------------------------------------------------------------------- /matplotlib+vk.md: -------------------------------------------------------------------------------- 1 | # Задания на семинар 2 | 3 | 1. Скачать список пользователей, состоящих в группе https://vk.com/dormitory8hse. Нарисовать столбчатую диаграмму, показывающую, сколько человек из каких городов состоит в группе. 4 | 2. В той же группе скачать 1000 записей со стены. Нарисовать график, показывающий, сколько записей было написано в каждый час. Вывести записи, набравшие наибольшее количество лайков и с наибольшим числом комментариев. 5 | 3. Скачать список пользователей, состоящих в группе https://vk.com/hse_university. Нарисовать столбчатую диаграмму, показывающую распределение пользователей по их году рождения. 6 | 4. Найти, сколько в этой группе состоит пользователей, чьим университетом является не Вышка. Нарисовать диаграмму, показывающую распределение этих пользователей по университетам (если все не влезут, то 10 самых частотных университетов). 7 | 4. Собрать корпус записей в группе Вышки и комментариев к ним (можно взять первые 10000 записей). Найти самые частотные биграммы в этом корпусе. 8 | -------------------------------------------------------------------------------- /skolkovo_retweets.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from collections import Counter 3 | 4 | with open('skolkovo.txt', 'r', encoding='utf-8') as f: 5 | lines = f.readlines() 6 | 7 | names = Counter([line.split()[6][:-1] 8 | for line in lines if 'RT ' in line]) 9 | names = dict(names.most_common(10)) 10 | 11 | labels = sorted(names.keys()) 12 | nums = range(len(names)) 13 | twit_nums = [names[key] for key in labels] 14 | 15 | plt.bar(nums, twit_nums, align='center') 16 | plt.xticks(nums, labels, rotation='vertical') 17 | plt.subplots_adjust(bottom=0.3) 18 | plt.show() 19 | -------------------------------------------------------------------------------- /telegram_bot.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание про бота в Telegram 2 | 3 | В этом домашнем задании нужно написать очень простого Telegram-бота, который считает, сколько слов в сообщении от пользователя. 4 | * код нужно выложить в свой репозиторий (помните, что пароли-явки и токены от ботов выкладывать не надо!) 5 | * бота нужно захостить на pythonanywhere. 6 | * ник вашего бота нужно записать в [форму](https://docs.google.com/forms/d/e/1FAIpQLSc5rtveKEKM1336dBelB37JIfVVQ8deRdTIsxkcfDOhyJikjg/viewform?usp=sf_link). 7 | 8 | ## Дедлайн - 14 мая 23:59 9 | -------------------------------------------------------------------------------- /word2vec.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Gensim, word2vec\n", 8 | "\n", 9 | "Gensim - вообще библиотека для тематического моделирования текстов. Один из компонентов в ней - реализация на python алгоритмов из библиотеки word2vec (которая в оригинале была написана на C++).\n", 10 | "\n", 11 | "Если gensim у вас не стоит, то ставим:\n", 12 | "\n", 13 | "`pip install gensim`\n", 14 | "\n", 15 | "Поскольку иногда тренировка модели занимает много времени, то можно ещё вести лог событий." 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": { 22 | "collapsed": true 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "import sys\n", 27 | "import gensim, logging\n", 28 | "\n", 29 | "logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "## Процесс тренировки модели \n", 37 | "\n", 38 | "На вход модели даем текстовый файл, каждое предложение на отдельной строчке." 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 2, 44 | "metadata": { 45 | "collapsed": true 46 | }, 47 | "outputs": [], 48 | "source": [ 49 | "f = 'text.txt'\n", 50 | "data = gensim.models.word2vec.LineSentence(f)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "Инициализируем модель. Параметры в скобочках:\n", 58 | "* data - данные, \n", 59 | "* size - размер вектора, \n", 60 | "* window - размер окна наблюдения,\n", 61 | "* min_count - мин. частотность слова в корпусе, которое мы берем,\n", 62 | "* sg - используемый алгоритм обучение (0 - CBOW, 1 - Skip-gram))" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 3, 68 | "metadata": { 69 | "collapsed": false 70 | }, 71 | "outputs": [ 72 | { 73 | "name": "stderr", 74 | "output_type": "stream", 75 | "text": [ 76 | "2017-05-30 00:13:36,174 : INFO : collecting all words and their counts\n", 77 | "2017-05-30 00:13:36,178 : INFO : PROGRESS: at sentence #0, processed 0 words, keeping 0 word types\n", 78 | "2017-05-30 00:13:36,304 : INFO : collected 30172 word types from a corpus of 68570 raw words and 5432 sentences\n", 79 | "2017-05-30 00:13:36,307 : INFO : Loading a fresh vocabulary\n", 80 | "2017-05-30 00:13:36,408 : INFO : min_count=2 retains 6793 unique words (22% of original 30172, drops 23379)\n", 81 | "2017-05-30 00:13:36,409 : INFO : min_count=2 leaves 45191 word corpus (65% of original 68570, drops 23379)\n", 82 | "2017-05-30 00:13:36,467 : INFO : deleting the raw counts dictionary of 30172 items\n", 83 | "2017-05-30 00:13:36,471 : INFO : sample=0.001 downsamples 34 most-common words\n", 84 | "2017-05-30 00:13:36,474 : INFO : downsampling leaves estimated 37321 word corpus (82.6% of prior 45191)\n", 85 | "2017-05-30 00:13:36,478 : INFO : estimated required memory for 6793 words and 500 dimensions: 30568500 bytes\n", 86 | "2017-05-30 00:13:36,535 : INFO : resetting layer weights\n", 87 | "2017-05-30 00:13:36,897 : INFO : training model with 3 workers on 6793 vocabulary and 500 features, using sg=0 hs=0 sample=0.001 negative=5 window=10\n", 88 | "2017-05-30 00:13:37,933 : INFO : PROGRESS: at 74.04% examples, 137499 words/s, in_qsize 6, out_qsize 0\n", 89 | "2017-05-30 00:13:38,133 : INFO : worker thread finished; awaiting finish of 2 more threads\n", 90 | "2017-05-30 00:13:38,147 : INFO : worker thread finished; awaiting finish of 1 more threads\n", 91 | "2017-05-30 00:13:38,164 : INFO : worker thread finished; awaiting finish of 0 more threads\n", 92 | "2017-05-30 00:13:38,166 : INFO : training on 342850 raw words (186531 effective words) took 1.3s, 147698 effective words/s\n" 93 | ] 94 | } 95 | ], 96 | "source": [ 97 | "model = gensim.models.Word2Vec(data, size=500, window=10, min_count=2, sg=0)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "Можно нормализовать вектора, тогда модель будет занимать меньше RAM. Однако после этого её нельзя дотренировывать." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 4, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [ 114 | { 115 | "name": "stderr", 116 | "output_type": "stream", 117 | "text": [ 118 | "2017-05-30 00:13:40,834 : INFO : precomputing L2-norms of word weight vectors\n" 119 | ] 120 | } 121 | ], 122 | "source": [ 123 | "model.init_sims(replace=True)" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "Смотрим, сколько в модели слов:" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 6, 136 | "metadata": { 137 | "collapsed": false 138 | }, 139 | "outputs": [ 140 | { 141 | "name": "stdout", 142 | "output_type": "stream", 143 | "text": [ 144 | "6793\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "print(len(model.wv.vocab))" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "И сохраняем!" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 7, 162 | "metadata": { 163 | "collapsed": false 164 | }, 165 | "outputs": [ 166 | { 167 | "name": "stderr", 168 | "output_type": "stream", 169 | "text": [ 170 | "2017-05-30 00:15:07,615 : INFO : saving Word2Vec object under my.model, separately None\n", 171 | "2017-05-30 00:15:07,621 : INFO : not storing attribute syn0norm\n", 172 | "2017-05-30 00:15:07,623 : INFO : not storing attribute cum_table\n", 173 | "2017-05-30 00:15:08,144 : INFO : saved my.model\n" 174 | ] 175 | } 176 | ], 177 | "source": [ 178 | "model.save('my.model')" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "## Работа с моделью\n", 186 | "\n", 187 | "Для каких-то своих индивидуальных нужд и экспериментов бывает полезно самому натренировать модель на нужных данных и с нужными параметрами. Но для каких-то общих целей модели уже есть как для русского языка, так и для английского.\n", 188 | "\n", 189 | "Модели для русского скачать можно здесь - http://rusvectores.org/ru/models\n", 190 | "\n", 191 | "Скачаем модель для русского языка, созданную на основе НКРЯ. Поскольку модели бывают разных форматов, то для них написаны разные функции загрузки; бывает полезно учитывать это в своем скрипте:" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 8, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "name": "stderr", 203 | "output_type": "stream", 204 | "text": [ 205 | "2017-05-30 00:15:15,307 : INFO : loading projection weights from ruscorpora_1_300_10.bin.gz\n", 206 | "2017-05-30 00:15:35,994 : INFO : loaded (184973, 300) matrix from ruscorpora_1_300_10.bin.gz\n" 207 | ] 208 | } 209 | ], 210 | "source": [ 211 | "m = 'ruscorpora_1_300_10.bin.gz'\n", 212 | "if m.endswith('.vec.gz'):\n", 213 | " model = gensim.models.KeyedVectors.load_word2vec_format(m, binary=False)\n", 214 | "elif m.endswith('.bin.gz'):\n", 215 | " model = gensim.models.KeyedVectors.load_word2vec_format(m, binary=True)\n", 216 | "else:\n", 217 | " model = gensim.models.KeyedVectors.load(m)" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 9, 223 | "metadata": { 224 | "collapsed": false 225 | }, 226 | "outputs": [ 227 | { 228 | "name": "stderr", 229 | "output_type": "stream", 230 | "text": [ 231 | "2017-05-30 00:15:40,106 : INFO : precomputing L2-norms of word weight vectors\n" 232 | ] 233 | } 234 | ], 235 | "source": [ 236 | "model.init_sims(replace=True)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "markdown", 241 | "metadata": {}, 242 | "source": [ 243 | "Скажем, нам интересны такие слова (пример для русского языка):" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 10, 249 | "metadata": { 250 | "collapsed": true 251 | }, 252 | "outputs": [], 253 | "source": [ 254 | "words = ['день_NOUN', 'ночь_NOUN', 'человек_NOUN', 'семантика_NOUN', 'студент_NOUN']" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "Частеречные тэги нужны, поскольку это специфика скачанной модели - она была натренирована на словах, аннотированных их частями речи (и лемматизированных).\n", 262 | "\n", 263 | "Попросим у модели 10 ближайших соседей для каждого слова и коэффициент косинусной близости для каждого:" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 11, 269 | "metadata": { 270 | "collapsed": false 271 | }, 272 | "outputs": [ 273 | { 274 | "name": "stdout", 275 | "output_type": "stream", 276 | "text": [ 277 | "день_NOUN\n", 278 | "[ 0.02765099 -0.06994063 0.02292976 0.00478499 -0.0373687 0.06778284\n", 279 | " 0.01259792 -0.09310838 0.10806882 -0.01369707]\n", 280 | "неделя_NOUN 0.7186020016670227\n", 281 | "утро_NOUN 0.6839577555656433\n", 282 | "месяц_NOUN 0.6676345467567444\n", 283 | "вечер_NOUN 0.6635110378265381\n", 284 | "час_NOUN 0.619712233543396\n", 285 | "полдень_NOUN 0.6096612215042114\n", 286 | "суббота_NOUN 0.6011918783187866\n", 287 | "ночь_NOUN 0.5900086164474487\n", 288 | "накануне_ADV 0.5895365476608276\n", 289 | "понедельник_NOUN 0.5813597440719604\n", 290 | "\n", 291 | "\n", 292 | "ночь_NOUN\n", 293 | "[ 0.00090496 -0.09171917 0.06209332 0.00910732 -0.03711092 0.0487812\n", 294 | " 0.05916326 -0.12525907 0.02495096 -0.07734441]\n", 295 | "вечер_NOUN 0.7375167608261108\n", 296 | "рассвет_NOUN 0.7365224361419678\n", 297 | "утро_NOUN 0.6998549103736877\n", 298 | "полночь_NOUN 0.6829675436019897\n", 299 | "напролет_ADV 0.6194927096366882\n", 300 | "сумерки_NOUN 0.6130234003067017\n", 301 | "ночной_ADJ 0.6043827533721924\n", 302 | "бессонный_ADJ 0.5970311760902405\n", 303 | "спать_VERB 0.5932828187942505\n", 304 | "полдень_NOUN 0.5922213196754456\n", 305 | "\n", 306 | "\n", 307 | "человек_NOUN\n", 308 | "[-0.13945162 0.00953103 0.09669054 -0.0521334 -0.03127262 0.05055281\n", 309 | " -0.02525845 0.01031411 0.0670734 -0.00500829]\n", 310 | "женщина_NOUN 0.5500056743621826\n", 311 | "мужчина_NOUN 0.5161216855049133\n", 312 | "человеческий_ADJ 0.5005477666854858\n", 313 | "идолопоклонствовать_VERB 0.4838884770870209\n", 314 | "высокопорядочный_ADJ 0.4818764925003052\n", 315 | "правдознатец_NOUN 0.48151782155036926\n", 316 | "некорыстолюбивый_ADJ 0.4798555374145508\n", 317 | "народ_NOUN 0.477242112159729\n", 318 | "старое::стариться_VERB 0.4748774468898773\n", 319 | "людишки_NOUN 0.4739970862865448\n", 320 | "\n", 321 | "\n", 322 | "семантика_NOUN\n", 323 | "[ 0.04162881 0.03207609 -0.00509921 0.10019507 -0.03660124 -0.03263019\n", 324 | " -0.01842243 -0.02713792 0.02342849 -0.09310298]\n", 325 | "семантический_ADJ 0.7749344110488892\n", 326 | "синтаксический_ADJ 0.6857521533966064\n", 327 | "лексический::семантика_NOUN 0.6853606104850769\n", 328 | "грамматикализация_NOUN 0.6655354499816895\n", 329 | "семантически_ADV 0.6494707465171814\n", 330 | "аспектуальный_ADJ 0.63737553358078\n", 331 | "семантический::метаязык_NOUN 0.6367851495742798\n", 332 | "синтаксический::актант_NOUN 0.6321083307266235\n", 333 | "wierzbicka_NOUN 0.6281548738479614\n", 334 | "сочетаемость_NOUN 0.6265926361083984\n", 335 | "\n", 336 | "\n", 337 | "студент_NOUN\n", 338 | "[-0.02158827 0.0493357 -0.0394694 0.00178204 0.02384631 -0.05032327\n", 339 | " 0.03192617 -0.05180086 0.00875538 0.05471088]\n", 340 | "преподаватель_NOUN 0.6941744089126587\n", 341 | "университет_NOUN 0.683201014995575\n", 342 | "студентка_NOUN 0.6738083362579346\n", 343 | "аспирант_NOUN 0.6682658195495605\n", 344 | "студенческий_ADJ 0.665415346622467\n", 345 | "факультет_NOUN 0.6491575241088867\n", 346 | "первокурсник_NOUN 0.6433141231536865\n", 347 | "универсант_NOUN 0.6419535875320435\n", 348 | "профессор_NOUN 0.6393071413040161\n", 349 | "ректор_NOUN 0.6145827174186707\n", 350 | "\n", 351 | "\n" 352 | ] 353 | } 354 | ], 355 | "source": [ 356 | "for word in words:\n", 357 | " # есть ли слово в модели? Может быть, и нет\n", 358 | " if word in model:\n", 359 | " print(word)\n", 360 | " # смотрим на вектор слова (его размерность 300, смотрим на первые 10 чисел)\n", 361 | " print(model[word][:10])\n", 362 | " # выдаем 10 ближайших соседей слова:\n", 363 | " for i in model.most_similar(positive=[word], topn=10):\n", 364 | " # слово + коэффициент косинусной близости\n", 365 | " print(i[0], i[1])\n", 366 | " print('\\n')\n", 367 | " else:\n", 368 | " # Увы!\n", 369 | " print(word + ' is not present in the model')" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "metadata": {}, 375 | "source": [ 376 | "Находим косинусную близость пары слов:" 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": 12, 382 | "metadata": { 383 | "collapsed": false 384 | }, 385 | "outputs": [ 386 | { 387 | "name": "stdout", 388 | "output_type": "stream", 389 | "text": [ 390 | "0.330222839481\n" 391 | ] 392 | } 393 | ], 394 | "source": [ 395 | "print(model.similarity('человек_NOUN', 'обезьяна_NOUN'))" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "Найди лишнее!" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 13, 408 | "metadata": { 409 | "collapsed": false 410 | }, 411 | "outputs": [ 412 | { 413 | "name": "stdout", 414 | "output_type": "stream", 415 | "text": [ 416 | "картофель_NOUN\n" 417 | ] 418 | } 419 | ], 420 | "source": [ 421 | "print(model.doesnt_match('яблоко_NOUN груша_NOUN виноград_NOUN банан_NOUN лимон_NOUN картофель_NOUN'.split()))" 422 | ] 423 | }, 424 | { 425 | "cell_type": "markdown", 426 | "metadata": {}, 427 | "source": [ 428 | "Реши пропорцию!" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 14, 434 | "metadata": { 435 | "collapsed": false 436 | }, 437 | "outputs": [ 438 | { 439 | "name": "stdout", 440 | "output_type": "stream", 441 | "text": [ 442 | "пельмень_NOUN\n" 443 | ] 444 | } 445 | ], 446 | "source": [ 447 | "print(model.most_similar(positive=['пицца_NOUN', 'россия_NOUN'], negative=['италия_NOUN'])[0][0])" 448 | ] 449 | }, 450 | { 451 | "cell_type": "markdown", 452 | "metadata": {}, 453 | "source": [ 454 | "Это конечно все хорошо, но как понять, какая модель лучше? Или вот например я сделал свою модель, насколько она хорошая?\n", 455 | "\n", 456 | "Для этого существуют специальные датасеты для оценки качества дистрибутивных моделей. Их два: один измеряет точность решения задач на аналогии (про Россию и пельмени), а второй используется для оценки коэффициента семантической близости. Подробнее читаем тут: \n", 457 | "* http://www.aclweb.org/aclwiki/index.php?title=Google_analogy_test_set_(State_of_the_art)\n", 458 | "* https://www.aclweb.org/aclwiki/index.php?title=SimLex-999_(State_of_the_art)\n", 459 | "\n", 460 | "Датасеты для русского языка можно скачать на странице с моделями на RusVectores. Посчитаем качество нашей модели НКРЯ на датасете про аналогии:" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": null, 466 | "metadata": { 467 | "collapsed": false 468 | }, 469 | "outputs": [], 470 | "source": [ 471 | "model.accuracy('ruanalogy_upos.txt', restrict_vocab=3000000)" 472 | ] 473 | } 474 | ], 475 | "metadata": { 476 | "kernelspec": { 477 | "display_name": "Python 3", 478 | "language": "python", 479 | "name": "python3" 480 | }, 481 | "language_info": { 482 | "codemirror_mode": { 483 | "name": "ipython", 484 | "version": 3 485 | }, 486 | "file_extension": ".py", 487 | "mimetype": "text/x-python", 488 | "name": "python", 489 | "nbconvert_exporter": "python", 490 | "pygments_lexer": "ipython3", 491 | "version": "3.5.3" 492 | } 493 | }, 494 | "nbformat": 4, 495 | "nbformat_minor": 2 496 | } 497 | -------------------------------------------------------------------------------- /Занятия в 1-м семестре.md: -------------------------------------------------------------------------------- 1 | # Занятия в 1-м семестре 2 | 3 | 1 и 2 модуль курса "Методы компьютерной обработки лингвистических данных" 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
Дата занятияКонспектДомашнее заданиеДополнительное
12.09.2016Выкачиваем Интернет - urllib.requestСкачать заголовки статей с сайта газетыСписок газет
19.09.2016Выкачиваем Интернет (эпизод второй) - краулеры
19.09.2016КИЛИ: Mystem
26.09.2016Про проектсм. конспект
дедлайн 10.10.2016
03.10.2016JSON
03.10.2016Запросы и формы
10.10.2016flask intro
17.10.2016flask продолжение
31.10.2016Про проект №2см. конспект
дедлайн 14.11.2016
28.11.2016 Просто множества Найти характеризующие новостной сюжет словоформы
дедлайн 6.12.2016
75 | -------------------------------------------------------------------------------- /Интерактивные графики и карты в вебе.md: -------------------------------------------------------------------------------- 1 | # Интерактивные графики и карты в вебе, Google Charts, D3, geojson 2 | 3 | ## Введение 4 | 5 | Мы уже умеем строить статические графики с помощью matplotlib. Но если мы делаем веб-сервис, гораздо удобнее было бы представлять на сайте интерактивные графики, которые отзывались бы на жесты пользователя, то есть чтобы при наведении мыши появлялся бы какой-то дополнительный текст, линии подсвечивались, части графиков можно было бы перетаскивать. Со статическими картинками в форматах .png или .jpg это невозможно. 6 | 7 | Интерактивными элементы на веб-страницы делает специальный язык -- JavaScript, который мы в нашем курсе программирования не изучаем (потому что он не так хорошо умеет обрабатывать языковые массивы, как Python), но если мы хотим сделать веб-страницу красивой и функциональной, без JavaScript совсем нам не обойтись. 8 | 9 | Делать интерактивный график на чистом JavaScript (особенно без предварительного изучения языка) довольно сложно. Но у нас в руках есть инструменты, которые оказывают в этом существенную помощь -- это специальные подключаемые библиотеки для работы с графиками, прежде всего, Google Chart и d3. 10 | 11 | ## Google Chart 12 | 13 | [Здесь](https://developers.google.com/chart/interactive/docs/gallery) можно посмотреть на то, какие типы графиков можно встроить в интернет-страницу, используя Google Chart. [На самом деле таких типов гораздо больше](https://developers.google.com/chart/interactive/docs/more_charts). 14 | 15 | График встраивается в HTML довольно просто. Нужно всего лишь: 16 | 17 | 1. внутри тега `` поместить код подгрузки библиотеки: `` 18 | 2. внутри тега `` в нужном месте поместить код того слоя, в котором будет отображаться график. Размеры слоя можно регулировать: 19 | `
` 20 | 3. выдать библиотеке данные, которые мы хотим отобразить на графике. Сделать это можно разными способами. Самое простое -- это зашить данные в коде JavaScript. Данные в нём представляется в очень похожем на питоновский синтаксисе: 21 | 22 | ``` 23 | [ 24 | ['Year', 'Sales', 'Expenses'], 25 | ['2004', 1000, 400], 26 | ['2005', 1170, 460], 27 | ['2006', 660, 1120], 28 | ['2007', 1030, 540] 29 | ] 30 | ``` 31 | Знающий питон легко узнаёт в этом коде 5 массивов, вложенных в ещё один массив. 32 | 33 | Если поместить эту структуру внутрь JavaScript-функции, даже не очень разбираясь в принципах её действия, можно получить желаемый результат: 34 | 35 | ``` 36 | function drawChart() { 37 | var data = google.visualization.arrayToDataTable([ 38 | ['Year', 'Sales', 'Expenses'], 39 | ['2004', 1000, 400], 40 | ['2005', 1170, 460], 41 | ['2006', 660, 1120], 42 | ['2007', 1030, 540] 43 | ]); 44 | ``` 45 | 46 | Получить из питона строку, которую можно было бы вставить внутрь JavaScript-кода, можно с помощью функции json.dumps() (вспомните, что и сам формат данных JSON появился благодаря языку JavaScript). 47 | 48 | ``` 49 | import json 50 | 51 | js_function = 'function drawChart() { var data = google.visualization.arrayToDataTable({{data_to_display}});' 52 | 53 | data = [['Year', 'Sales', 'Expenses'], ['2004', 1000, 400], ['2005', 1170, 460], ['2006', 660, 1120], ['2007', 1030, 540]] 54 | 55 | data_for_visualization = json.dumps(data) 56 | js_function = js_function.replace('{{data_to_display}}', data_for_visualization) 57 | 58 | ``` 59 | ## d3 60 | 61 | Другой способ создавать разнообразные интерактивные графики на веб-странице -- это библиотека d3.js. Галерея примеров того, что можно сделать с её помощью, [лежит здесь](https://github.com/d3/d3/wiki/Gallery). Как видно, возможности здесь практически безграничны, нужно только выбрать подходящий способ визуализации. 62 | 63 | [Здесь можно](https://www.jasondavies.com/wordcloud/) заранее сделать статическую картинку облака слов в svg (удобный для веба формат) и вставить её на ваш сайт. А [здесь](https://bl.ocks.org/mbostock/4062006) можно подсмотреть, как рисовать хордовую диаграмму, отражающую связи между группами объектов. 64 | 65 | Вообще-то d3 -- это очень мощный инструмент, с помощью которого можно создавать [свои достаточно сложные визуализации](https://github.com/d3/d3/blob/master/API.md), для этого просто нужно достаточно подробно [изучить устройство](https://github.com/d3/d3/wiki/Tutorials) библиотеки. Но если вам хватает уже существующих инструментов, можно воспользоваться ими в том же духе, как мы работали с Google Chart: взять готовый образец и подставить в него свои данные. 66 | 67 | ### Географические данные, GIS 68 | 69 | В d3 и Google Chart есть много способов отобразить географические данные и построить на них карту (интерактивную и отображаемую на интернет-странице). Более сложный (но в чём-то и более простой) и настраиваемый способ -- это использовать формат geojson. 70 | 71 | Для питона и этого формата есть [специальная библиотека](https://pypi.python.org/pypi/geojson), но она нужна в том случае, если работа с данными автоматизирована. Если же вы работаете с обозримым объёмом данных вручную, их можно сформировать на сайте [geojson.io](http://geojson.io/), он представляет собой простой визуальный редактор, в котором слева вы видите карту, а справа -- geojson. Когда вы отмечаете что-то слева, оно тут же автоматически появляется в этом самом формате справа. 72 | 73 | Например, мы можем поставить на карте какой-то маркер или нарисовать полигон, а щелчок по тому объекту, который мы добавили на карту, вызывает облачко с табличкой дополнительных признаков, которые можно прикрепить к маркеру. Например, в левой колонке таблички можно вписать слово `title`, а в той же строке справа название объекта ("метро Автозаводская" или что-то другое). Точно так же можно написать слева слово `description`, а справа что-то содержательное: "Лучшее место на свете!" 74 | 75 | Когда вы сделали таким образом свою карту в http://geojson.io/, нужно сохранить получившийся результат в виде файла с расширением .geojson 76 | Это можно сделать с помощью меню "Save" в левой верхней части экрана. Из предложенных вариантов нужно выбрать "GeoJSON". 77 | 78 | Если вы загрузите этот файл на github, то сайт по умолчанию будет не отображать содержимое файла, а сразу отрисовывать карту, как [вот тут](https://github.com/nevmenandr/DigitalHumanitiesMinorFeatures/blob/master/MoskowPetushkiWay.geojson). Правда, это работает только если geojson-файл не больше 10 мегабайт. Кроме того, такую карту можно вставить и на вашу страницу в Интернете, если в html-код вставить строку: 79 | 80 | ``` 81 | 82 | 83 | ``` 84 | 85 | Для примера выше эта строка будет выглядеть так: 86 | 87 | ``` 88 | 89 | ``` 90 | 91 | ## Задания 92 | 93 | 1. Нарисуйте с помощью Google Charts график, который мы рисовали с [matplotlib](https://github.com/elmiram/2016learnpython/blob/master/Matplotlib.ipynb). 94 | 2. Нарисуйте карту, взяв данные из [WALS](http://wals.info/feature) 95 | -------------------------------------------------------------------------------- /Командная строка UNIX, логин на сервере.md: -------------------------------------------------------------------------------- 1 | # Командная строка UNIX, удалённый логин на сервере 2 | ## О чём речь? 3 | 4 | Помимо пользовательских компьютеров, которые мы используем в своей жизни для личных нужд и для работы (они называются "десктопами"), важное место в компьютерной инфраструктуре занимают так называемые "серверы". Это тоже компьютеры, обычно чуть более мощные и надёжные, чем десктопы, они могут располагаться в специальных [ЦОДах или дата-центрах](https://ru.wikipedia.org/wiki/%D0%94%D0%B0%D1%82%D0%B0-%D1%86%D0%B5%D0%BD%D1%82%D1%80) а ещё часто они постоянно подключены к Сети 24/7 и именно они обеспечивают работоспособность сайтов в Интернете и многих других сервисов, может быть, напрямую не связанных именно с Интернетом, например, банковских переводов. 5 | 6 | Когда мы через какой-нибудь браузер *заходим на сайт*, на самом деле за этим процессом стоит соединение по сети нашего компьютера с таким вот сервером (он называется "удалённым", потому что физически удалён от нас, может быть, на тысячи километров), который обрабатывает наш запрос, а одновременно с ним в ту же секунду может так же отвечать ещё тысячам других пользователей. 7 | 8 | Поэтому, чтобы, например, сделать свой собственный сайт, нужно иметь под рукой такой *сервер*, то есть компьютер, который будет, как минимум, постоянно подключён к Интернету, а ещё будет мощным и надёжным. 9 | 10 | ## UNIX и интерфейсы 11 | 12 | Из-за требований к надёжности и по ряду других исторических и коммерческих причин большинство серверов управляется не привычными для десктопа операционными системами семейства Windows или Mac OS, а операционными системами, которые называют UNIX-подобными, то есть воспроизводящими некоторые концептуальные идеи придуманной когда-то давно ОС UNIX (на самом деле, Mac OS X тоже является UNIX-подобной, и многое из того, о чём мы говорим ниже, справедливо и для неё). Прежде всего, это операционные системы (т.н. "дистрибутивы") на основе ядра Linux, а также в последнее время теряющие в популярности ОС семейства BSD. На практике (помимо других важных отличий) это обычно означает, что пользователю во всех этих системах предоставляется более-менее сходный интерфейс управления. И этот интерфейс не графический, то есть в нём нет привычных нам кнопочек и окон. То есть в принципе кнопочки и окошки (GUI -- graphic user interface) для UNIX-подобных систем возможны, но на серверах они не используются. 13 | 14 | Таким образом, те, кто взаимодействует с серверами: выкладывает на них свои сайты или занимается их обслуживанием ("администрирует"), пользуются интерфейсом командной строки, command line interface. Все вы немного имели с ним дело, когда изучали git и Mystem. Но для git есть и графические клиенты. С серверами и Mystem альтернативы командной строке почти нет. 15 | 16 | ## Командная строка 17 | 18 | Командная строка позволяет делать всё то, что пользователь привык делать, щёлкая мышкой: переходить между директориями, создавать и удалять файлы и папки, запускать программы. На самом деле, возможности командной строки даже шире и многообразнее, чем в графическом интерфейсе. 19 | 20 | Все эти возможности предоставляются таким образом. В текстовое поле вводится какая-то команда, представляющая собой зарезервированное слово, а для исполнения нажимается клавиша "Enter". Каждое такое слово -- это имя какой-нибудь маленькой программы, которая таким образом исполняется и делает то, что нам нужно. Например, она в каждый момент времени пользователь находится в каком-то конкретном месте файловой системы (дерева каталогов), и чтобы переместиться по ней вверх или вниз, нужно исполнить программу `cd` ("change directory"), передав ей параметр -- целевой каталог, куда нужно перейти. Например, ввод `cd text` приведёт к переходу пользователя во вложенную в текущую директорию "text" (если та и правда существует). 21 | 22 | ## Пробуем! 23 | 24 | Чтобы попробовать, как это работает, нужно загрузить реальную UNIX-подобную операционную систему. Мы будем тренироваться на дистрибутиве на основе ядра Linux, который называется Ubuntu, он действительно установлен в качестве основной операционки на многих серверах по всему миру. Запускать отдельный компьютер при этом не обязательно, операционную систему можно запустить внутри так называемой "виртуальной машины", то есть программы, которая делает вид, что она настоящий компьютер, а операционная система управляет этой программой и при этом думает, что управляет настоящим компьютером. 25 | 26 | ![Man In Black](https://vignette2.wikia.nocookie.net/aliens/images/4/42/Look_at_the_Reflection.jpg/revision/latest/scale-to-width-down/300?cb=20100729120113) 27 | 28 | 29 | 30 | *Кадр из фильма Man In Black: видна галактика, которая думает, что она находится в настоящей вселенной* 31 | 32 | В этом классе установлен менеджер виртуальных машин VirtualBox. Нужно запустить его и включить там виртуальную машину с Ubuntu. 33 | 34 | Если у вас всё получилось, то нужно щёлкнуть по логотипу Ubuntu в левом верхнем углу и в строке поиска напечатать Terminal (или нажать одновременно `Ctrl + Alt + T`). Здесь можно вводить команды для работы с интерфейсом командной строки. Команд этих существует великое множество, но начать нужно с простого. 35 | 36 | Прежде всего, нужно осмотреться. Слева от курсора написано, какую директорию командная строка считает текущей. Если там содержится символ \~, то это указание на так называемую "домашнюю директорию", то есть "порт приписки" того пользователя, под именем которого вы залогинились в системе. Там же вы увидите символ доллара, который можете помнить по документации к Mystem. 37 | 38 | Для UNIX-систем очень важно, под каким именем вы логинитесь в системе, и одна из центральных идей этой операционной системы резко отличается от того, что можно наблюдать в Windows. В Windows обычно кто угодно может сделать всё, что угодно. В UNIX за каждым пользователем чётко закреплены его права: что он может делать и чего не может. Скажем, пользователь обычно может создавать, изменять и удалять файлы и папки в своей домашней директории, которая располагается по адресу `/home/`, но не может никак изменять или удалять файлы в директориях, созданных другими пользователями. Это помогает сделать систему более безопасной, и надёжной, потому что в такой ситуации гораздо труднее случайно испортить что-нибудь важное. Поэтому не пытайтесь что-то изменять за пределами своей папки `/home/`, всё равно ничего не получится (в принципе, возможности для этого есть, но только при наличии специальных привилегий). 39 | 40 | Попробуем какие-нибудь команды. Выведем на экран список вложенных файлов и каталогов (если там ничего нет, то на экране ничего и не появится): 41 | 42 | ``` 43 | ls 44 | ``` 45 | 46 | (ls -- от "list") 47 | 48 | 49 | Теперь перейдём на уровень вверх и снова посмотрим на все лежащие тут файлы и папки: 50 | 51 | ``` 52 | cd ../ 53 | ls 54 | ``` 55 | 56 | Распечатаем полный путь к текущей директории: 57 | 58 | ``` 59 | pwd 60 | ``` 61 | 62 | Теперь вернёмся в домашнюю директорию, ведь только там мы можем делать что-нибудь законно (то есть не имея каких-то специальных прав): 63 | 64 | ``` 65 | cd 66 | ``` 67 | 68 | Теперь попробуем сделать то, что обычно делаем через графический интерфейс на своих десктопных компьютерах: 69 | 70 | Создадим директорию с именем "dir1": 71 | 72 | ``` 73 | mkdir dir1 74 | ``` 75 | 76 | Перейдём в неё: 77 | 78 | ``` 79 | cd dir1 80 | ``` 81 | 82 | Теперь скачаем сюда какой-нибудь файл из Интернета (без urllib.request!) и убедимся в том, что он действительно появился и выведем его содержимое на экран: 83 | 84 | ``` 85 | wget http://web-corpora.net/Test1/hello.py 86 | ls 87 | cat 88 | ``` 89 | 90 | Это файл на питоне. Попробуем его запустить из командной строки: 91 | 92 | ``` 93 | python hello.py 94 | ``` 95 | 96 | Получилось! 97 | 98 | Теперь копируем этот файл на уровень выше, а исходный удалим (последовательно разными командами, хотя можно было бы, конечно, и переместить файл, для этого используется команда `mv`): 99 | 100 | ``` 101 | cp hello.py ../ 102 | rm hello.py 103 | ``` 104 | 105 | Теперь удалим директорию "dir1" (мы сможем это сделать только если она будет пустой): 106 | 107 | ``` 108 | cd ../ 109 | rmdir dir1 110 | ``` 111 | 112 | Что ещё можно сделать из командной строки? [Очень-очень много всего!](http://forum.ubuntu.ru/index.php?topic=14535.15) 113 | 114 | Потренироваться можно даже не имея под рукой виртуальной машины под VirtualBox. На [этом сайте](http://bellard.org/jslinux/) вы найдёте Linux-систему без графической оболочки прямо в своём браузере. Там работает большинство команд, которые используются в интерфейсе командной строки UNIX-подобных систем. 115 | 116 | Если на сервере установлен git, то можно склонировать на него репозиторий с гитхаба. 117 | 118 | ## Удалённый доступ, SSH 119 | 120 | Поскольку UNIX-подобные системы чаще всего установлены на удалённых серверах, а не на компьютерах, которые есть у нас под рукой, нужно уметь соединяться (логиниться) с такими компьютерами удалённо. Для этого нужно знать логин и пароль доступа к удалённому серверу, его адрес (например, IP-адрес) и иметь программу, которая знает специальный протокол, по которому обычно происходит такое соединение. Этот протокол (не единственный, но самый распространённый) называется SSH (secure shell). На юникс-подобных системах такие программы уже установлены по умолчанию, а вот на Windows их нужно специально устанавливать. Самый распространённый инструмент такого рода -- программа PuTTY, она есть на компьютерах в этом классе. 121 | 122 | ![PuTTY](http://www.losergeek.org/tutorials/ssh-proxy/PuTTY1.PNG) 123 | 124 | В поле Host Name нужно ввести IP-адрес сервера, поставить галочку на "SSH" и нажать "Open". Дальше сервер запросит у вас логин и пароль (их, как и IP-адрес сервера вам сообщит преподаватель, писать их в открытом виде на гитхабе небезопасно). Обратите внимание, что когда вы будете печатать пароль, на экране ничего не будет происходить. Ни звёздочек, ни точек появляться не будет. Так и нужно. Пароль всё равно печатается. Введите его и нажмите "Enter". Если всё пройдёт успешно, вы окажетесь в домашней директории пользователя. 125 | 126 | 127 | -------------------------------------------------------------------------------- /Лекция по дистрибутивной семантике.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmiram/2016learnpython/1a83c46f479135aceef5d411d32c928c83404f09/Лекция по дистрибутивной семантике.pdf --------------------------------------------------------------------------------