Как я пытался разоблачить посредственных учителей из моей школы (и как я потерпел неудачу)

История программиста

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

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

  • Оценка в этом разделе позволила мне продемонстрировать результаты обучения.
  • Я старался изо всех сил участвовать в этом отряде
  • В целом я остался доволен этим устройством

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

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

Именно тогда в моей голове начали крутиться шестеренки. Все эти данные хранятся на веб-сайте университетов, золотая жила информации о работе каждого подразделения, преподаваемого в университете, остается нетронутой, неиспользованной, неограниченной. Никто даже не знает о его существовании. Самое интересное, что результаты опроса по этим агрегатам далеко не единообразны! «Общая удовлетворенность этим устройством» оценивается в диапазоне от 2/5 до 4,5 / 5 в зависимости от класса. Определенные единицы имеют постоянно низкие рейтинги. Университет использует эти опросы для принятия решений, но они так глубоко похоронены в дальнем углу веб-сайта университета на странице, которая выглядит так, как будто она из темных веков, что ни один студент, кроме, может быть, какого-то ботаника (меня), никогда не получит какую-либо полезную информацию. от него.

В опросе также предлагается оценить работу ваших учителей и преподавателей, но эти ответы доступны только избранным преподавателям… облом. Но я задумался. Если бы я только мог найти, какой лектор преподает каждую единицу в каждом конкретном семестре, я мог бы сопоставить это с результатами опроса и сделать вывод об успеваемости лекторов. Руководство по установке! У каждого раздела в университете есть «руководство по разделу», в котором вы найдете краткий обзор раздела, результаты обучения, оценочные задания И… преподавателя, преподающего раздел. Кроме того, справочник модулей - онлайн-справочник наших университетов, в котором индексируются все модули, - напрямую связан с руководствами по модулям. Чем больше я думал об этом, тем больше убеждался, что это может сработать.

Идея заключалась в том, чтобы создать простой веб-сайт, на котором можно было бы искать любое подразделение или лектора. Он будет показывать балл для каждого модуля на основе данных опроса и балл для каждого лектора на основе баллов модулей, которые они преподавали в прошлом. У меня довольно большой опыт фронтенд-разработки, поэтому создание чего-то быстрого и грязного в React не должно быть слишком сложным. Затем мне понадобится API, чтобы запросить мою базу данных и получить всю информацию, которая мне нужна для интерфейса. Бэкенд-работа - не моя сильная сторона, поэтому для этого потребуется немного повышения квалификации, но я смогу это сделать. Затем мне нужно было развернуть все это на сервере, придумать броское доменное имя, опубликовать его в группе студентов частных университетов в Facebook и позволить ему проникнуть на экран каждого студента (да, я был полностью погружен в вся идея на тот момент).

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

Создание набора данных

Мне нужно выяснить, как я собираюсь структурировать данные, чтобы я мог начать думать о том, как я собираюсь их собирать. Я выхожу из excel и начинаю составлять свою схему.

К сожалению, ни на одном из этих университетских веб-сайтов нет кнопки «экспорт в CSV», поэтому мне придется создать свою собственную. Все это придется соскрести с HTML-кода веб-сайтов, моим лучшим другом будет python. Я никогда раньше не занимался парсингом веб-страниц, но мне приходилось слышать термин Beautiful Soup, поэтому я проверю эту библиотеку. Я прочитал документацию; он написан лаконично и обладает всеми необходимыми мне функциями. Идеально.

Сначала мне нужно очистить страницу справочника по каждому модулю (тот индекс, о котором я говорил). Каждый год выпускается новое руководство, и в нем есть страница для каждого раздела с указанием его названия, кампусов, в которых оно преподается и в каком семестре; но, что наиболее важно, он связан со всеми руководствами по модулям для этого модуля (есть одно руководство на семестр). У нас есть доступ к архивам всех справочников, начиная с 1998 года, поэтому получить прошлые данные не составит труда.

Итак, я начал собирать свой первый парсер. Я беру все необходимые данные: код единицы, название единицы, кампус, семестр и ссылку на руководство для этого семестра. Тестирую на нескольких страницах .. работает. Я могу пролистать все страницы в справочнике, собрать все данные вместе и выгрузить их в CSV. Есть только одна проблема ...

requesting page 1…
(1 second wait)
parsing..
requesting page 2…
(1 second wait)
parsing…

Каждый запрос к справочнику занимает около одной секунды, и, поскольку я возвращаюсь на три года назад, мне нужно просмотреть около 15 000 страниц. Это более четырех часов запросов. Я мог бы просто оставить его включенным на ночь, но ни у кого нет такого времени. Кроме того, я не хочу запускать его в течение четырех часов, чтобы понять, что есть крайний случай, о котором я не думал, который заполняет весь мой набор данных (и всегда есть крайние случаи, о которых вы не задумываетесь). Мне нужно сделать несколько запросов одновременно.

К счастью, asyncio, фреймворк параллелизма на основе цикла событий и сопрограмм для python, делает это довольно легко. Я установил цикл событий со 100 рабочими и жду свои запросы в асинхронной сопрограмме. Оказалось, что это намного проще, чем я предполагал. Я заключаю все это в небольшую служебную функцию batch_requests () и тестирую ее.

requesting pages 1…100
parsing
(10 second wait)
requesting pages 2…200
parsing
(10 second wait)

Намного лучше! Теперь больше всего времени занимает синтаксический анализ, поскольку он должен пройти по 100 страниц одна за другой. Теперь все это занимает 25 минут. Это намного лучше, примерно в десять раз меньше, но все равно меня передергивает. Выполнение большего количества запросов за раз на данном этапе мало что изменит, поскольку синтаксический анализ действительно замедляет работу программы, поэтому, если я хочу ускорить работу еще больше, мне нужно найти способ ускорить синтаксический анализ.

Оказывается, библиотека многопроцессорности в Python очень удобна для пользователя. Вы создаете объект multiprocessing.Pool (процессы), сообщаете ему, сколько процессов вы хотите, а затем можете вызвать его pool.map (function, iterable) для запустите любую функцию в пуле процессов. Работает так же, как обычный метод map (), за исключением того, что делает это параллельно. Я дал ему свою функцию синтаксического анализа и список всех ответов, которые я получил от моего batch_requests (), и позволил ему творить чудеса.

requesting pages 1…100
parsing
(5 second wait)
requesting pages 100…200
parsing
(5 second wait)

В половине случаев! Но подождите ... Я почти уверен, что у моего процессора четыре ядра? Разве мне не следует повысить производительность в четыре раза? Ну, без моего ведома, оказалось, что процессор использует нечто, называемое гиперпоточностью. Несмотря на то, что у моего Mac четыре логических ядра (представьте, что это виртуальное ядро), у него только два физических ядра. Я не вдавался в подробности, но очевидно, что это помогает компьютерам повысить их производительность. Вот почему я получаю только двухфакторное увеличение производительности. Ну что ж, достаточно хорошо.

Итак, теперь данные, которые у меня есть, выглядят так:

Теперь все, что мне нужно сделать, это очистить лекторов от руководства по модулю и очистить данные опроса.

Поэтому я сразу же запрыгиваю на одного из гидов, чтобы посмотреть, с чем я имею дело. Ах, ладно, чтобы перейти к руководству по отряду, мне нужно пройти аутентификацию с помощью моей университетской учетной записи. Я думал об автоматизации всего процесса аутентификации в моей программе - что, по общему признанию, было бы действительно круто - но чем больше я вглядывался в это, я понял, что во всем процессе задействовано много javascript-рендеринга, и мне это не нужно. функциональности. Я ввожу URL-адрес для руководства по устройству, открываю инструменты разработчика в моем браузере, пару раз захожу в систему и выясняю, какие два файла cookie используются для аутентификации. Поэтому я болтаю с клавиатурой и пишу новый скрипт, чтобы пройти по ссылкам руководства по устройствам из исходных данных, а затем попросить меня добавить новые файлы cookie, когда аутентификация не удалась.

$ python unit_guide_scraper.py
JSESSIONID: nijrNPEKQWWtmgFoOdUQAPjGMlbZq7U1
AWSELB: FD9E75345F81579D

Оно работает! Цунами HTML наводняет мою консоль, уровень серотонина достигает пика. Теперь, когда я знаю, что запросы работают, пришло время очистить данные. Страница руководства по установке выглядит примерно так:

В каждом семестре есть одно руководство по разделу, поэтому, если его преподают в нескольких кампусах, руководство по подразделениям перечисляет каждый из них, а затем дает вам лектора для этого кампуса. У меня уже есть все ссылки на руководство по модулю, поэтому мой сценарий может просто просмотреть каждую из них и найти в руководстве по модулю место с надписью Campus Lecturer (s). Если модуль преподается в нескольких кампусах, тогда я могу найти заголовок, который указывает на кампус, который я хочу, затем нужно добавить лектора к существующей записи и выгрузить все это в новый CSV. Очень просто. Правильно?

Итак, я беру руководство по модулю, просматриваю html и начинаю писать свою функцию очистки. Заголовок лектора - это <h2>, да круто, заголовок кампуса, да, это <h4>. Выглядит неплохо. Хорошо. Давай запустим.

Ладно, получаю много ошибок, интересно. Я немного изменяю свой код, так что каждый раз, когда я получаю ошибку в моей функции очистки, он записывает ссылку на руководство по модулю в файл errors.log вместе с возникшим исключением. Я начинаю просматривать их один за другим, чтобы понять, что происходит.

Аааа, я вижу, что происходит в этом. Они использовали <h3> вместо <h2> в заголовке Campus Lecturer (s)! Мой скребок искал <h2>, поэтому, вероятно, все они не работают. Хорошо, это руководство по устройству немного другое. Этот модуль преподается в трех разных кампусах, но они опустили заголовок университетского городка, вместо этого у них просто есть небольшой подзаголовок под лектором, говорящий, к какому университетскому городку они принадлежат. Подождите, это подразделение преподается в двух разных университетских городках, но в руководстве по подразделению вообще нет указаний на кампус! Невозможно узнать, какой преподаватель за какой университетский городок отвечает. И этот преподается только в одном кампусе, но перечислено несколько лекторов!

Я понимаю, что есть некоторые сложности, которые я не учел. Я достал свой блокнот и начал записывать все различные типы руководств по подразделениям, с которыми я мог бы столкнуться, все время думая о том, как бы я справился с каждым случаем: один кампус и один лектор, один университетский городок и несколько лекторов, использует <h2>, использует <h3>, использует небольшие субтитры, совсем не указывает университетский городок и т. д. После прохождения каждого раздела руководства с помощью тонкой гребешки я просматриваю свои записи. Тридцать два различных возможных сценария. Как обидно 😳😩😭

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

Начало конца

Я думал об этом пару дней, но в конце концов сдался. Университет, используя разное форматирование для всех своих руководств по разделам, непреднамеренно разрушил мои планы по привлечению учителей к ответственности за предоставляемое ими образование. Я был немного разочарован из-за того, что не реализовал свою цель, но, как и в случае с любым другим проектом с высоким уровнем неопределенности, я понял, что столкновение с препятствием, подобным этому, может произойти. В конце концов, когда вы проектируете систему с нуля, вы контролируете множество переменных. Мой проект зависел от множества внешних факторов, которые были вне моего контроля. Тем не менее, я должен был прежде всего попытаться очистить направляющие устройства. Поскольку они, по сути, написаны самими преподавателями, а затем отправлены в университет и конвертированы в html (я полагаю), их, вероятно, было трудно очистить. Прочитав много руководств по модулям в прошлом, я предположил, что «все они выглядят одинаково, их будет легко разобрать».

Тем не менее, работая над этим проектом, я узнал много полезного и / или интересного:

  1. Веб-парсинг очень полезен, а Beautiful Soup делает его очень простым.
  2. Многопроцессорность в Python - это здорово! Три строки кода для программы, которая работает в два раза быстрее - и, возможно, больше, в зависимости от количества физических ядер в вашем ЦП.
  3. Гиперпоточность - это вещь

Программирование - это 10% кодирования и 90% исправления ошибок. Вот как мы учимся. Каждый раз, когда мы чешем в затылке, пытаясь понять, почему! & $ # This не работает, мы добавляем запись в длинный список ошибок, которые, надеюсь, больше не повторим. Я хотел исправить эту ошибку в системе образования, и я был близок к этому. В следующий раз я сначала прочту руководство по установке.

Приложения

Если вы все еще продолжаете читать, я хотел бы прояснить пару вещей. Я действительно рассмотрел этические последствия индивидуального рейтинга лекторов и влияние, которое это могло бы на них оказать, если бы этот сайт действительно стал популярным. Однако я также считаю, что с образованием нельзя связываться. Когда мой парикмахер делает мне плохую стрижку, это отстой, но, эй, я буду жить. И все же я, вероятно, смогу найти его звездный рейтинг где-нибудь на Yelp. Но когда у вас плохой учитель, это портят вашу карьеру. Твое будущее. Я думаю, что когда студент выбирает свои разделы, он имеет право знать, был ли этот модуль или его преподаватель плохо оценен в прошлом.