├── .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 |
- Не все ссылки могут быть рабочими (сайты закрываются, а список составлялся давно), так что лучше не просто выбрать себе газету, а попробовать открыть и посмотреть, что там есть, работает ли сайт. Работающих хороших газет может на всех не хватить. Но список постепенно будет пополняться.
9 | - Выбранная газета останется с вами надолго, и задание с ней будет не одно. Так что лучше сразу полюбить то, что выбрали =)
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",
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 | ""
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"
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 | 
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",
55 | " \n",
56 | "\n",
57 | "Когда форма отправляется на сервер, управление данными передается программе, заданной атрибутом `action` тега `\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",
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 | ""
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 | 
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 |
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 |
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 |
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 |
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 | 
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 | 
123 |
124 | В поле Host Name нужно ввести IP-адрес сервера, поставить галочку на "SSH" и нажать "Open". Дальше сервер запросит у вас логин и пароль (их, как и IP-адрес сервера вам сообщит преподаватель, писать их в открытом виде на гитхабе небезопасно). Обратите внимание, что когда вы будете печатать пароль, на экране ничего не будет происходить. Ни звёздочек, ни точек появляться не будет. Так и нужно. Пароль всё равно печатается. Введите его и нажмите "Enter". Если всё пройдёт успешно, вы окажетесь в домашней директории пользователя.
125 |
126 |
127 |
--------------------------------------------------------------------------------
/Лекция по дистрибутивной семантике.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elmiram/2016learnpython/1a83c46f479135aceef5d411d32c928c83404f09/Лекция по дистрибутивной семантике.pdf
--------------------------------------------------------------------------------