Closed dyakovri closed 2 years ago
Парсер расписания – больная тема, есть несколько версий и единственная, которая справлялась с 100% случаев, была утеряна. Нужно написать его заново, а чтобы она была поддерживаемой есть несколько красивых функциональных решений. Но для начала...
Хочется сделать код читаемым и поддерживаемым, поэтому существует этот раздел
return day, groups
.__init__(...)
и parse(...)
. Первый принимает HTML код структурного элемента, а второй ничего не принимает и возвращает список или словарь заданной выше структуры. Список структурных элементов:
parse
бьёт на дни и запускает структурный элемент нижеРабота с интернетом - тема, которая для меня покрыта мраком и загадкой. Я просто напросто даже не представляю, как все это работает. Ты написал, что есть несколько версий этого парсера? Можешь, пожалуйста, их прислать? Ну или просто покидать туториалы по Beautiful Soup 4 и request. А то без примеров я не справлюсь.
Ты написал, что есть несколько версий этого парсера? Можешь, пожалуйста, их прислать?
Ну или просто покидать туториалы по Beautiful Soup 4 и request
В целом из requests тебе нужно:
user_agent = 'Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36'
headers = {'User-Agent': user_agent}
response = requests.get(f'http://ras.phys.msu.ru/table/1/1/1.htm', headers=headers)
html_for_soup = response.test
Документация Beautiful Soup 4 https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Прочитай, как работает HTML перед тем, как начать: теги, классы, id, селекторы
Как сделать хранение календаря, поэма в трех частях:
Сделать таблицу decode_patterns, в которую можно записывать регулярные выражения для получения нужной инфы. Например с помощью этой таблицы потом можно сделать так:
import re
m = re.match(
r'.*?>(?P<subject>.+)<nobr>(?P<place>.+)</nobr>(?P<teaher>.+)</.*',
'<td align="center" class="tditem1">СФЕРИЧЕСКАЯ АСТРОНОМИЯ<nobr>ГАИШ 48</nobr>проф. Жаров В. Е.</td>''',
re.IGNORECASE | re.DOTALL
)
m.groupdict()
Даст результат
{'subject': 'СФЕРИЧЕСКАЯ АСТРОНОМИЯ',
'place': 'ГАИШ 48',
'teaher': 'проф. Жаров В. Е.'}
Добавляя регулярку в базу можно будет без изменения кода получать интересные нам параметры. Надо только им еще приоритет будет добавить, следовательно 3 столбца в таблице: id, regex, priority.
А пайп для Airflow: читать таблицу регулярок, читать таблицу расписания, к каждой строке расписания пытаться применить регулярку до первого получившегося результата по приоритету. Результат класть в новую таблицу.
Нашу таблицу, думаю, стоит построить следующим образом:
id, (предмет, время, параметры), время изменения, флаг удаления
Заполняется она следующим образом:
предмет, время, параметры
, добавляем новую строку с новым id, текущим временем изменения, флаг удаления = FALSE
2.2. Если среди строк актуального расписания есть предмет, время, параметры
, ничего не добавляем
2.3. Если среди строк актуального расписания есть предмет, время, параметры
, которого нет в обновляемом расписании, то устанавливается последний ID этого предмета, текущее время изменения и флаг удаления = TRUEТам довольно сложный запрос есть для создания такой таблицы с историей, для чтения чуть проще, но тоже сложный. Я тебе скину пример. Это очень стандартный подход к работе с историчными данными
Вставка выглядит примерно так:
INSERT INTO {ods_table} ({columns}, is_deleted, tech_load_ts)
WITH LAST_ODS_VERSION AS (
SELECT *
FROM (
SELECT *
FROM {ods_table} ods
LIMIT 1 OVER (
PARTITION BY {primary_keys}
ORDER BY ods.tech_load_ts DESC
)
) s
WHERE is_deleted != 1
),
DIFFERENCE_NEW_ITEMS AS (
SELECT {columns} FROM {stg_table}
EXCEPT
SELECT {columns} FROM LAST_ODS_VERSION
),
DIFFERENCE_DELETE_ITEMS AS (
SELECT ods.*
FROM LAST_ODS_VERSION ods
LEFT JOIN {stg_table} stg
ON {pk_compare}
WHERE {pk_exists}
),
DIFFERENCE_ALL AS (
SELECT
{columns},
0 AS is_deleted,
STATEMENT_TIMESTAMP() AS tech_load_ts
FROM DIFFERENCE_NEW_ITEMS
UNION ALL
SELECT
{columns},
1 AS is_deleted,
STATEMENT_TIMESTAMP() AS tech_load_ts
FROM DIFFERENCE_DELETE_ITEMS
)
SELECT {columns}, t.is_deleted, t.tech_load_ts
FROM DIFFERENCE_ALL t;
Соответственно
SELECT *
FROM (
SELECT *
FROM {ods_table} ods
LIMIT 1 OVER (
PARTITION BY {primary_keys}
ORDER BY ods.tech_load_ts DESC
)
) s
WHERE is_deleted != 1
используется для получения последней (актуальной) версии таблицы
ид, имя пользователя, данные для входа в гугл аккаунт, время последней синхронизации
и
ид пользователя, ид предмета, ид мероприятия в календаре
Надо будет из таблицы истории доставать изменения. Проверять, каких мероприятий коснулись эти изменения, логиниться и делать изменения в календарях пользователей. Добавлять в лог новые мероприятия, удалять старые и менять время последней синхронизации. Тут довольно просто, но до этого еще далеко
Подгрузить расписание физфака в базу данных
Комментарии по отдельным задачам ниже