Год назад я начал писать ботов для всеми любимого Телеграма. На Питоне, конечно. И вот недавно мой сын пошёл в школу, где, как оказалось, был электронный дневник под названием МРКО. Как вы могли догадаться, самая первая мысль — сделать бота (пока для личного пользования), который смог бы присылать в Телеграм оценки, домашнее задание и комментарии. Кому интересно — прошу под кат.
Пишем парсер
Сначала, понятное дело, нужно написать парсер для самого дневника. Для тех, кто не знает, поясню. Система входа примерно такая: ученик/родитель входит на портал mos.ru, авторизуется на нем и уже с этого портала входит на основной mrko.mos.ru. Вы можете подумать — почему же просто сразу не войти на mrko.mos.ru? Проблема в том, что сервер отвечает нам таким сообщением:
Вход для родителей и обучающихся производится только с сайта Портала Госуслуг Москвы.
Тут то и получается основная загвоздка. Понятное дело, нужно совершать как можно меньше запросов, чтобы скорость ответа была больше.
Исследованием сниффера исходящего трафика Я понял, что сначала происходит GET-запрос к https://mrko.mos.ru/dnevnik/services/index.php?login=ЛОГИН&password=ПАРОЛЬ_В_MD5
, ставятся необходимые куки и потом можно заходить на https://mrko.mos.ru/dnevnik/services/dnevnik.php?r=1&first=1
. Начал я с импортирования моей любимой библиотеки для работы с HTTP в Питоне — Requests. Далее — создание элементарной сессии:
import requests
def diary():
session = requests.Session()
headers = {'Referer': 'https://www.mos.ru/pgu/ru/application/dogm/journal/'}
auth_url = "https://mrko.mos.ru/dnevnik/services/index.php"
auth_req = session.get(auth_url, headers=headers, params={"login": ЛОГИН, "password": ПАРОЛЬ_В_MD5}, allow_redirects=False)
Сразу хочу обратить внимание на заголовок Referer. Как позже выяснилось, его необходимо указывать, иначе дневник не даст нам войти, думая что мы вошли напрямую. Я нам надо замаскироваться, будто бы мы вошли с mos.ru. Теперь основной запрос к дневнику:
main_req = session.get("https://mrko.mos.ru/dnevnik/services/dnevnik.php?r=1&first=1")
Разбираем данные
Отлично. В дневник зашли. Теперь самая сложная часть — разбор (парсинг) данных. Для этой простой задачи я решил использовать BeautifulSoup, т.к. раньше имел дело с ним работать.
from bs4 import BeautifulSoup
parsed_html = BeautifulSoup(main_req.content, "lxml")
НеДолгим копанием с помощью Chrome Developer Tools в DOM-дереве дневника, вычислил div с необходимой информацией.
columns = parsed_html.body.find_all('div', 'b-diary-week__column')
final_ans = []
Теперь у нас есть массив с данными на каждый день дневника, начиная от понедельника и заканчивая субботой и пустой массив с финальными данными. Очевидно, что для обхода массива я использую цикл for
:
for column in columns:
Опять же, нашел элементы с нужной мне информацией. А именно: день недели, число, домашнее задание, оценки и комментарии к урокам. Получилось примерно так.
date_number = column.find("span", "b-diary-date").text
date_word = column.find("div", "b-diary-week-head__title").find_all("span")[0].text
Теперь записываем данные о дате и перебираем каждую "ячейку" в таблице
lessons_table = column.find("div", "b-diary-lessons_table")
all_lists = lessons_table.find_all("div", "b-dl-table__list")
for lesson in all_lists:
lesson_columns = lesson.find_all("div", "b-dl-td_column")
lesson_number = lesson_columns[0].span.text
lesson_name = lesson_columns[1].span.text
# Если название урока пусто, пропускаем
if lesson_name == "":
pass
else:
lesson_dz = lesson_columns[2].find("div", "b-dl-td-hw-section").span.text
lesson_mark = lesson_columns[3].span.text[0:1]
lesson_comment = lesson_columns[4].find("div", "b-dl-td-hw-comments").span.text
final_ans.append(
"<b>{0}. {1}</b>. Домашнее задание:\n"
"<i>{2}</i>\n"
"Оценка за урок: <i>{3}</i>\n\n".format(lesson_number,
lesson_name,
lesson_dz,
lesson_mark))
final_ans.append("\n-------------------\n\n")
В итоге у нас получился парсер, который может выдавать примерно это:
Ну, на этом все. Спасибо за прочтение. Надеюсь я сэкономил вам много времени. Следующую статью напишу про интеграцию этого парсера с Телеграм-ботом.
Ссылки
Комментарии (13)
svv27
14.03.2017 17:37Так как у дневника нет нормального решения по оповещению родителей, а раздавать пароль от ГосУслуг (по которому настроен вход) не правильно, придётся каждому желающему удобно работать с этим «чудом» писать свой «велосипед».
Для себя добавил бы следующее:
1. Косметически допилить (убрать информационный шум — информацию с прочерками).
Можно попробовать разделить отметки и ДЗ, потому как отметки нужны на сегодня (и пару дней назад), а ДЗ на завтра (и на пару дней вперёд).
2. Периодичность «прочёсывания» дневника.
У нас часто, что принести на труд завтра любят написать только днём сегодня. Очень удобно.
PS Спасибо за наводку.MonsterAndrew
16.03.2017 10:18Согласен. По оформлению много чего можно сделать, но я сконцентрировался на тех. части. Оформление — дело каждого:)
bano-notit
14.03.2017 18:10+1Не работает больше такой способ авторизации, ну по крайней мере у меня. Ибо теперь у него 3 url, с первого он получается свой определённый токен, потом получает ещё какие-то данные, и только потом обращается к МРКО, с этим идиотским токеном и какими-то данными.
Да, в прошлом году такой способ аутентификации у них прокатывал, я тогда сделал экстеншон для хрома, который тупо со страницы мос.ру посылал форму на этот дурацкий МРКО. Из-за усложнения в этом году операции аутентификации я это дело забросил.
dremdem
16.03.2017 10:19Круть.
У меня почему-то реферер не сработал и пришлось писать обработку всей авторизации с mos.ru
С получением всяких токенов и идентификаторов сессий.
Причем надавно на mos.ru перешли на oauth 2.0 и пришлось переделывать.
Телеграм 2 года назад был мне малоизвестен, так что я все это дела повесил на малину ( Raspberry PI ) к которой подцеплен GSM-модем. Настроил систему уведомлений для меня, жены и 2-их детей )
Идут SMS и почта на все настроенные адреса.
SMS-ки нужны были т.к у детей телефоны были типа бабушкофон.
Есть сайтик на Flask который показывает домашку и статистику с рейтингом оценок детей.
В планах написать систему монетной мотивации оценок.
MonkAlex
А зачем записи без оценок? =)
IDMan
— Сын, ты уроки сделал?
— а нам не задавали… (звуки стрельбы из наушников)
— как не задавали, смотри сюда, у меня в телеграмме все записано
LoadRunner
Не знаю, видели ли Вы дневник, но там есть список уроков и не по всем задают домашнее задание, а значит, и оценку не поставить — там прочерк. Дневник же весь парсится.
MonkAlex
А, в самом посте об этом не было, не подумал про домашку =)
Меня просто никто не проверял, делаю я её или нет, вот и в голову даже не пришло.