From e9fb5ff4d4ce4b6aa85ad6d6672615b4cb09e524 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:04 +0300 Subject: [PATCH 01/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D1=8F=D1=8E=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=20=D1=82=D0=B5=D0=BA=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 7ad73b2c..58e23870 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ безгранично гибкий код — это как сферический конь в вакууме. Теоретически возможен, но практической ценности не несет. +Если мы заранее будем знать как изменятся требования к коду через год, два или +десять лет, то уже сейчас можно заложить необходимые механизмы расширения +функциональности. К сожалению, такие сведения сложно получить, а зачастую +просто невозможно. Приходится опираться на свой опыт, опыт товарищей, +прорабатывать разные сценарии развития проекта, подстраховываться. + Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. @@ -108,3 +114,10 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. +Кроме того, часть кода удалось превратить в чистые функции, что облегчит +тестирование и повторное использование. + +Стратегия по отделению операций ввода/вывода от обработки данных встречается +повсеместно, в самых разных программах: от небольших скриптов до серьезных и +крупных проектов. Это один из базовых приемов, нужно уверенно им владеть. + From 52a1d2b42b14cd16e1b6a5d2b36b0cf7c751f898 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 17 Oct 2018 12:33:38 +0300 Subject: [PATCH 02/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58e23870..5a2b6610 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. -Кроме того, часть кода удалось превратить в чистые функции, что облегчит +Кроме того, часть кода удалось превратить в [чистые функции](https://devman.org/encyclopedia/decomposition/decomposition_pure_functions/), что облегчит тестирование и повторное использование. Стратегия по отделению операций ввода/вывода от обработки данных встречается From 4846789cce2932f1eae848269be88efd106e3eae Mon Sep 17 00:00:00 2001 From: Alina Date: Wed, 17 Oct 2018 12:34:44 +0300 Subject: [PATCH 03/14] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=B8=D0=BD=D0=BA=D1=83?= =?UTF-8?q?=20=D1=81=D0=BE=20=D1=81=D1=85=D0=B5=D0=BC=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7ad73b2c..c26851c5 100644 --- a/README.md +++ b/README.md @@ -108,3 +108,4 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. +![alt text](https://devman.org/assets/images/7_40__data_flow.png) \ No newline at end of file From 4fb288681d3dac182c839de6afd30812775372d3 Mon Sep 17 00:00:00 2001 From: dvmn-tasks <44063469+dvmn-tasks@users.noreply.github.com> Date: Tue, 7 Jul 2020 15:10:43 +0300 Subject: [PATCH 04/14] Update image link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c26851c5..d3c84658 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,4 @@ days_before_start ? 1)от источника данных; 2)от формата вывода в файл. -![alt text](https://devman.org/assets/images/7_40__data_flow.png) \ No newline at end of file +![image](https://dvmn.org/filer/canonical/1594117412/678/) From 55311abf4df0818b7b38fd2a133add1ab314d612 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 19:34:38 +0700 Subject: [PATCH 05/14] Corrected heading --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b9b06393..be5f5fde 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Отделяйте ввод/вывод от обработки +#Отделяйте ввод/вывод от обработки# Ключевой критерий качества кода — это стоимость внесения в него изменений. @@ -16,7 +16,7 @@ Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. -Пример. Подбор онлайн-курса +##Пример. Подбор онлайн-курса## По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из @@ -46,7 +46,7 @@ def get_courses_list(courses_url): сценариях вывод в консоль зависит от общей логики загрузки данных и многократных вызовов def get_courses_list. -Что еще может потребоваться в скором будущем? +##Что еще может потребоваться в скором будущем?## Отладить и покрыть тестами парсер HTML страницы. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком @@ -71,7 +71,7 @@ def get_course_info(html): # .... parsing logic return course_data -Что может произойти с кодом дальше? +##Что может произойти с кодом дальше?## Если рейтинга нет — надо искать его на другом сайте. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. @@ -106,7 +106,7 @@ days_before_start ? и повторной отладки всей программы от начала до конца, ведь изменения локальны и изолированы. -Вместо заключения +##Вместо заключения## В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: From a49e02a1b18a48d8b935ae572af3a066b7a3f35c Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 19:41:16 +0700 Subject: [PATCH 06/14] Corected lists --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index be5f5fde..4a95bd6a 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ def get_courses_list(courses_url): Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: -В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем +- В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем подождать еще 30 секунд и так далее. -В случае если адрес недоступен - постучаться по другому url в зеркало сайта. -В случае ошибки сделать запись в лог и взять данные из ранее подготовленного +- В случае если адрес недоступен - постучаться по другому url в зеркало сайта. +- В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. Как все это сделать когда def get_courses_list сама завершает программу ?! От вызова exit() надо отказаться. Можно выбросить исключение и таким образом @@ -48,8 +48,8 @@ def get_courses_list(courses_url): ##Что еще может потребоваться в скором будущем?## -Отладить и покрыть тестами парсер HTML страницы. -Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком +- Отладить и покрыть тестами парсер HTML страницы. +- Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж хорошая идея. Жить будет легче если передать в def get_courses_list строку с @@ -111,8 +111,8 @@ days_before_start ? В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: -1)от источника данных; -2)от формата вывода в файл. +1. )от источника данных; +2. )от формата вывода в файл. ![image](https://dvmn.org/filer/canonical/1594117412/678/) From 9b5a20049c872f5a42c77e8e44a98fcc86dff802 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 19:42:27 +0700 Subject: [PATCH 07/14] Corrected code --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4a95bd6a..bdf1fc41 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: -def get_courses_list(courses_url): +```def get_courses_list(courses_url): html = fetch_html(courses_url) if html: # .... parsing logic return courses_list else: print("can't load list of courses") - exit() + exit()``` Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: @@ -61,7 +61,7 @@ HTML разметкой вместо courses_url. Вуаля, мы решили def get_course_info(html): # ... parsing logic - rating = soup.find_all('div', attrs={'class': 'ratings-text'}) + ```rating = soup.find_all('div', attrs={'class': 'ratings-text'}) if rating: # check if rating is not empty list rating = rating[0].contents[0].text else: @@ -70,7 +70,7 @@ def get_course_info(html): # .... parsing logic - return course_data + return course_data``` ##Что может произойти с кодом дальше?## Если рейтинга нет — надо искать его на другом сайте. @@ -87,13 +87,13 @@ def get_course_info(html): # ... more parsing logic is here # number prefix is usefull for simple sorting data before output to xlsx - return { + ```return { '1_title': title, '2_date': start_date, '3_language': language, '4_weeks': duration, "5_rating": rating - } + }``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с другим порядком столбцов, как это сделать? Как заменить столбец 2_date на days_before_start ? From 0e0b841f8e7898fac95912b1ae58f7f8f2ddbad9 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 19:45:44 +0700 Subject: [PATCH 08/14] Recorrected lists --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bdf1fc41..8e40f7c5 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,10 @@ Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: -- В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем +1. В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем подождать еще 30 секунд и так далее. -- В случае если адрес недоступен - постучаться по другому url в зеркало сайта. -- В случае ошибки сделать запись в лог и взять данные из ранее подготовленного +2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. +3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. Как все это сделать когда def get_courses_list сама завершает программу ?! От вызова exit() надо отказаться. Можно выбросить исключение и таким образом @@ -48,8 +48,8 @@ ##Что еще может потребоваться в скором будущем?## -- Отладить и покрыть тестами парсер HTML страницы. -- Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком +1. Отладить и покрыть тестами парсер HTML страницы. +2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж хорошая идея. Жить будет легче если передать в def get_courses_list строку с @@ -111,8 +111,8 @@ days_before_start ? В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: -1. )от источника данных; -2. )от формата вывода в файл. +1. от источника данных; +2. от формата вывода в файл. ![image](https://dvmn.org/filer/canonical/1594117412/678/) From 80944269f20433cb6d44054a6a81f4e28ccc8548 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 19:54:55 +0700 Subject: [PATCH 09/14] Error correction --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8e40f7c5..0ed30f4c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -#Отделяйте ввод/вывод от обработки# + +# Отделяйте ввод/вывод от обработки # Ключевой критерий качества кода — это стоимость внесения в него изменений. @@ -16,13 +17,14 @@ Один из часто встречающихся и оправданных приемов — это отделение обработки данных от процесса ввода/вывода. Рассмотрим несколько примеров. -##Пример. Подбор онлайн-курса## +## Пример. Подбор онлайн-курса ## По условию задачи нужно скачать из сети данные об онлайн-курсах, выбрать из них лучшие и сохранить результат в xlsx файл. Вот фрагмент кода: -```def get_courses_list(courses_url): +```python +def get_courses_list(courses_url): html = fetch_html(courses_url) if html: # .... parsing logic @@ -46,7 +48,7 @@ сценариях вывод в консоль зависит от общей логики загрузки данных и многократных вызовов def get_courses_list. -##Что еще может потребоваться в скором будущем?## +## Что еще может потребоваться в скором будущем? ## 1. Отладить и покрыть тестами парсер HTML страницы. 2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком @@ -59,9 +61,11 @@ HTML разметкой вместо courses_url. Вуаля, мы решили Пойдем дальше. Код другой функции: def get_course_info(html): + # ... parsing logic - ```rating = soup.find_all('div', attrs={'class': 'ratings-text'}) + ```python + rating = soup.find_all('div', attrs={'class': 'ratings-text'}) if rating: # check if rating is not empty list rating = rating[0].contents[0].text else: @@ -71,7 +75,8 @@ def get_course_info(html): # .... parsing logic return course_data``` -##Что может произойти с кодом дальше?## + +## Что может произойти с кодом дальше? ## Если рейтинга нет — надо искать его на другом сайте. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. @@ -87,7 +92,8 @@ def get_course_info(html): # ... more parsing logic is here # number prefix is usefull for simple sorting data before output to xlsx - ```return { + ```python + return { '1_title': title, '2_date': start_date, '3_language': language, @@ -106,7 +112,7 @@ days_before_start ? и повторной отладки всей программы от начала до конца, ведь изменения локальны и изолированы. -##Вместо заключения## +## Вместо заключения ## В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: From afb9dd110c1aa0ea1d03852e6c788ac507fc25d2 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 20:01:03 +0700 Subject: [PATCH 10/14] Correction code display --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0ed30f4c..14083167 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,9 @@ def get_courses_list(courses_url): return courses_list else: print("can't load list of courses") - exit()``` + exit() +``` + Теперь примерим на себя роль провидца и подумаем какой функционал потребуется через месяц: @@ -64,7 +66,7 @@ def get_course_info(html): # ... parsing logic - ```python +```python rating = soup.find_all('div', attrs={'class': 'ratings-text'}) if rating: # check if rating is not empty list rating = rating[0].contents[0].text @@ -74,7 +76,8 @@ def get_course_info(html): # .... parsing logic - return course_data``` + return course_data +``` ## Что может произойти с кодом дальше? ## @@ -92,14 +95,15 @@ def get_course_info(html): # ... more parsing logic is here # number prefix is usefull for simple sorting data before output to xlsx - ```python +```python return { '1_title': title, '2_date': start_date, '3_language': language, '4_weeks': duration, "5_rating": rating - }``` + } +``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с другим порядком столбцов, как это сделать? Как заменить столбец 2_date на days_before_start ? @@ -114,7 +118,6 @@ days_before_start ? ## Вместо заключения ## - В результате мы пришли к ситуации, когда логика обработки данных слабо зависит: 1. от источника данных; From 7b30dd5160664f4607ce859b10e9d507222100dc Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 20:04:23 +0700 Subject: [PATCH 11/14] Corrected code display #2 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 14083167..be25c01e 100644 --- a/README.md +++ b/README.md @@ -61,12 +61,12 @@ HTML разметкой вместо courses_url. Вуаля, мы решили появления на горизонте! Пойдем дальше. Код другой функции: - +```python def get_course_info(html): # ... parsing logic -```python + rating = soup.find_all('div', attrs={'class': 'ratings-text'}) if rating: # check if rating is not empty list rating = rating[0].contents[0].text @@ -90,12 +90,12 @@ def get_course_info(html): rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. Та же функция, часть вторая, последняя: - +```python def get_course_info(html): # ... more parsing logic is here # number prefix is usefull for simple sorting data before output to xlsx -```python + return { '1_title': title, '2_date': start_date, From dbe30e7b5583726a6e91aa82267a56390d0ce936 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 20:07:33 +0700 Subject: [PATCH 12/14] Corrected lists #3 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index be25c01e..0e651792 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ def get_courses_list(courses_url): сценариях вывод в консоль зависит от общей логики загрузки данных и многократных вызовов def get_courses_list. -## Что еще может потребоваться в скором будущем? ## +Что еще может потребоваться в скором будущем? 1. Отладить и покрыть тестами парсер HTML страницы. 2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком @@ -79,7 +79,7 @@ def get_course_info(html): return course_data ``` -## Что может произойти с кодом дальше? ## +Что может произойти с кодом дальше? Если рейтинга нет — надо искать его на другом сайте. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. From a12c1104c7d2748c4e687380c974cb801b1c7725 Mon Sep 17 00:00:00 2001 From: Ganozas Date: Sun, 7 Dec 2025 20:13:43 +0700 Subject: [PATCH 13/14] Added Inline code --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0e651792..8ede8a58 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,22 @@ def get_courses_list(courses_url): 2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. 3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. -Как все это сделать когда def get_courses_list сама завершает программу ?! От -вызова exit() надо отказаться. Можно выбросить исключение и таким образом +Как все это сделать когда `def get_courses_list` сама завершает программу ?! От +вызова `exit()` надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. -Вызов print тоже стоит вынести из тела функции наружу. В рассмотренных +Вызов `print` тоже стоит вынести из тела функции наружу. В рассмотренных сценариях вывод в консоль зависит от общей логики загрузки данных и -многократных вызовов def get_courses_list. +многократных вызовов `def get_courses_list`. Что еще может потребоваться в скором будущем? 1. Отладить и покрыть тестами парсер HTML страницы. 2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. -Ага, значит вызывать fetch_html() внутри def get_courses_list не такая уж -хорошая идея. Жить будет легче если передать в def get_courses_list строку с -HTML разметкой вместо courses_url. Вуаля, мы решили проблемы еще до их +Ага, значит вызывать `fetch_html()` внутри `def get_courses_list` не такая уж +хорошая идея. Жить будет легче если передать в `def get_courses_list` строку с +HTML разметкой вместо `courses_url`. Вуаля, мы решили проблемы еще до их появления на горизонте! Пойдем дальше. Код другой функции: @@ -86,7 +86,7 @@ def get_course_info(html): Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы удобнее было руками проверять. Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". -В Python для этих целей предусмотрено значение rating = None. А строку "No +В Python для этих целей предусмотрено значение `rating = None`. А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx. Та же функция, часть вторая, последняя: @@ -105,14 +105,14 @@ def get_course_info(html): } ``` Сразу возникают вопросы. А если нужна еще одна выгрузка в формате csv, с -другим порядком столбцов, как это сделать? Как заменить столбец 2_date на -days_before_start ? +другим порядком столбцов, как это сделать? Как заменить столбец `2_date` на +`days_before_start` ? Кроме того, наперед известно, что пользовательский интерфейс — будь то вывод в консоль или запись в файл — меняется очень часто. Было бы удобно собрать все, что относится к форматированию вывода в одном месте. Например, всю логику -выгрузки в xlsx поместить в def fill_xlsx(workbook, courses):, а вывод в -консоль собрать внутри if __name__=='__main__':. Удастся избежать вычитывания +выгрузки в xlsx поместить в `def fill_xlsx(workbook, courses):`, а вывод в +консоль собрать внутри `if __name__=='__main__':`. Удастся избежать вычитывания и повторной отладки всей программы от начала до конца, ведь изменения локальны и изолированы. From e0a022cab5998226e7e43ea0e696fa17f1ba767e Mon Sep 17 00:00:00 2001 From: Ganozas Date: Tue, 9 Dec 2025 20:25:21 +0700 Subject: [PATCH 14/14] Corrected lists #4 --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8ede8a58..8d7338e0 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,8 @@ def get_courses_list(courses_url): 1. В случае сетевой ошибки взять паузу в 10 секунд и повторить попытку, затем подождать еще 30 секунд и так далее. 2. В случае если адрес недоступен - постучаться по другому url в зеркало сайта. -3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного -кеша. +3. В случае ошибки сделать запись в лог и взять данные из ранее подготовленного кеша. + Как все это сделать когда `def get_courses_list` сама завершает программу ?! От вызова `exit()` надо отказаться. Можно выбросить исключение и таким образом сообщить о проблеме внешнему коду, пускай там разбираются. @@ -53,8 +53,8 @@ def get_courses_list(courses_url): Что еще может потребоваться в скором будущем? 1. Отладить и покрыть тестами парсер HTML страницы. -2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком -диске. +2. Ускорить работу скрипта, хранить ранее скачанные страницы в кеше на жестком диске. + Ага, значит вызывать `fetch_html()` внутри `def get_courses_list` не такая уж хорошая идея. Жить будет легче если передать в `def get_courses_list` строку с HTML разметкой вместо `courses_url`. Вуаля, мы решили проблемы еще до их @@ -81,10 +81,11 @@ def get_course_info(html): Что может произойти с кодом дальше? -Если рейтинга нет — надо искать его на другом сайте. -В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. -Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы +1. Если рейтинга нет — надо искать его на другом сайте. +2. В xlsx указывать не просто отсутствие рейтинга, а еще на каких сайтах искал. +3. Отчет о курсах без рейтинга выгружать в дополнительную вкладку xlsx, чтобы удобнее было руками проверять. + Для всего этого нужно уметь отличать от прочих ситуацию "рейтинг неизвестен". В Python для этих целей предусмотрено значение `rating = None`. А строку "No rating yet" можно переместить туда где данные подготавливаются к выводу в xlsx.