Skip to content

2. Домашнє завдання №2

Bohdan Mahometa edited this page Apr 11, 2021 · 26 revisions

Table of Contents

Функціональні вимоги до системи

1. Пошук інформації про книжку. Пріоритет - високий.

1.1 Пошук інформації про книжку за її назвою.
1.2 Пошук інформації про книжку за автором.
1.3 Комбінований пошук інформації про книжку (за назвою і автором).
2. Відео-огляди на книгу. Пріоритет - високий.
2.1 Пошук посилань відео-оглядів на знайдену книгу із платформи YouTube за допомогою однойменного API. Пріоритет - високий.
3. Відгуки про книгу на веб-сайті.
3.1 Отримання відгуків про книгу із сайту Goodreads за допомогою Beautiful Soup 4. Пріоритет - високий.
3.2 Відображення відгуків про книгу на веб-сайті. Пріоритет - високий.
3.3 Аналіз користі відгуків. Знаходження найкорисніших позитивних та негативних відгуків. Пріоритет - середній.
3.4 Відображення найкорисніших позитивних та негативних відгуків із платформи Goodreads. Пріоритет - середній.
4. Відображення іншої додаткової інформації про книгу. Пріоритет - середній.
4.1 Відображеня рейтингу книги на платформі Goodreads.
4.2 Відображення наявності українських перекладів знайденої книги.

Нефункціональні вимоги до системи

1. Вимоги до користувацького інтерфейсу.

1.1 Доступ до наданої функціональності відбувається через веб-сайт.
1.2 Інтерфейс повинен бути англійською мовою.
1.3 Інтерфейс повинен бути максимально простий та зрозумілий.
2. Вимоги до програмного забезпечення.
2.1 Логіка вебсайту повинна бути написана на мові програмування Python з дотримуванням стандарту PEP08.
2.2 Backend повинен бути реалізований з використанням фреймворку Flask.
2.3 Весь процес пошуку та виведення інформації про книгу повинен виконуватися не довше ніж 5 секунд.
3. Вимоги до безпеки.
3.1 Як розробникам, нам потрібно обмежити користувача в зловживанні сервісами, які ми надаємо.
3.2 Як розробники, ми не хочемо втратити API-ключі, тому нам потрібно контролювати частоту звертань сервера до API.
3.3 Як розробники, ми маємо забезпечити правильну відповідь системи на виняткові ситуації, що можуть статися під час користування веб-сайтом.

Опис даних, з якими працює програма

Опис даних отриманих з Goodreads API

Пошук книг за назвою, автором, ISBN

  • Запит:
    • Метод HTTP-запиту: GET
    • Endpoint: https://www.goodreads.com/search/index.xml
    • Параметри query string:
      • key: API ключ
      • q: фраза, за якою відбувається пошук
      • search[field]: вказує, в чому шукати фразу q (може бути 'title', 'author', або 'all'). Якщо не вказано, то шукає в усьому.
      • page: номер сторінки результату пошуку (на одній сторінці може бути до 20 знайдених книг).
  • Відповідь:
    • Формат відповіді - XML
    • Структура XML:
└───GoodreadsResponse
    ├───Request
    │   ├───authentication
    │   │   └───key - contains API key
    │   └───method
    └───search
        ├───query
        ├───results-start - порядковий номер першої книги на сторінці (integer)
        ├───results-end - порядковий номер останньої книги на сторінці (integer)
        ├───total-results - кількість книг на сторінці, тобто кількість дітей xml-елемента results, (integer)
        ├───source (string)
        ├───query-time-seconds - час, який сервер витратив на обробку запиту (real number)
        └───results
            ├───work
            │    ├───id (integer)
            │    ├───books_count (integer)
            │    ├───ratings_count (integer)
            │    ├───text_reviews_count (integer)
            │    ├───original_publication_year (integer)
            │    ├───original_publication_month (integer)
            │    ├───original_publication_day (integer)
            │    ├───average_rating (real number)
            │    └───best_book
            │        ├───id (integer)
            │        ├───title (string)
            │        ├───author
            │        │   ├───id (integer)
            │        │   └───name (string)
            │        ├───image_url (string)
            │        └───small_image_url (string)
            ├───work
            │   ...
            ├───work
            │   ...
            ...
            └───work
                ...
В елементах work міститься інформація про знайдені книги.

Отримання огляду на книгу за її ISBN

  • Запит:
    • Метод HTTP-запиту: GET
    • Endpoint: https://www.goodreads.com/book/isbn/ISBN?format=json
    • Параметри query string:
      • key: API ключ
      • isbn: ISBN книги, огляди якої шукаємо.
      • rating: Показати лише відгуки з певним рейтингом (optional)
  • Відповідь:
    • Формат відповіді - json
    • Структура json-об'єкту:
      • Відповідь містить лише одну пару name/value
      • {'reviews_widget': html-розмітка}
      • Отримана html-розмітка містить html-елемент iframe, значення одного з атрибутів якого є посиланням на огляди. Відповідно, щоб отримати огляди, потрібно пропарсити сторінку за цим посиланням.

Можливості бібліотек, які використовуватимуться для роботи з даними

Огляд модуля xml.etree.ElementTree стандартної бібліотеки Python

Модуль xml.etree.ElementTree дозволяє представляти дані XML у вигляді дерева. Для цього він надає клас ElementTree (представляє все дерево) та клас Element (представляє одну вершину дерева, корінь чи будь-яку іншу).

Parsing XML

Деякі зручні способи перетворити XML-дані в об'єкти модуля xml.etree.ElementTree:

  • функція xml.etree.ElementTree.fromstring(text)
    • Перетворює стрічку text у об'єкт класу Element і повертає його.
    • Отриманий об'єкт класу Element представляє корінь дерева, тобто елемент, що найвищий в ієрархії.
    • За допомогою отриманого об'єкту можна доступитися до всіх інших елементів, їх атрибутів та вмісту.
  • функція ET.parse(source)
    • Читає XML-дані з файлу і повертає об'єкт класу ElementTree.
    • аргумент source - файл-об'єкт або шлях до файлу з XML-даними.
    • За допомогою отриманого об'єкту класу ElementTree можна доступитися до всіх XML-елементів, їх атрибутів та вмісту.

Доступ до XML-даних

  • Щоб отримати корінь (елемент, який найвищий в ієрархії), якщо маємо об'єкт класу ElementTree, можна використати метод getroot.
  • Об'єкт класу Element є iterable. Тобто можна пройтися по всіх дітях XML-елементу за допомогою циклу for.
  • Іншим способом доступитися до дітей XML-елементу є індексування. Наприклад, element[0] поверне першу дитину XML-елемента.
  • Метод екземляру iter класу Element повертає ітератор, щоб пройтися по всіх нащадках XML-елементу. Наприклад, list(element.iter()) поверне список всіх нащадків element.
  • Метод екземляру find класу Element дозволяє знайти дитину XML-елемента з вказаною назвою тега. Наприклад, element.find("some_name_of_tag") поверне об'єкт класу Element, що є дитиною element та має назву "some_name_of_tag"
  • Щоб знайти всіх дітей element за назвою тега, потрібно використати метод findall. Наприклад, element.findall('some_name_of_tag') повертає список всіх тегів, які є дітьми element і мають назву 'some_name_of_tag'.
  • Доступитися до атрибута XML-елементу можна використовуючи метод Element.get. Наприклад, element.get('type', 'no type') поверне значення атрибуту type, якщо він існує, інакше поверне стрічку 'no type'.
  • Щоб доступитися (змінити чи просто отримати) до тексту, що міститься у вмісті XML-елементу, потрібно скористатися атрибутом text об'єкта класу Element.
  • Щоб доступитися (змінити чи просто отримати) до тегу XML-елемента, потрібно скористатися атрибутом tag. Наприклад, element.tag поверне назву тега element, element.tag = "nice" змінить назву тега element на "nice".

Збереження XML-даних

  • Щоб записати XML-дані, що містяться в об'єкті ElementTree, можна скористатися методом write. Наприклад, tree.write('output.xml') запише XML-дані в файл з назвою 'output.xml'.
  • Якщо ж замість об'єкта класу ElementTree, в нас є об'єкт класу Element, то tree = ElementTree(element) створить дерево, коренем якого є element, а tree.write('output.xml') запише це дерево в файл.

Огляд модуля JSON

Модуль JSON дозволяє кодувати і декодувати дані в зручному форматі.

  • json.dump (obj, fp, skipkeys = False, ensure_ascii = True, check_circular = True, allow_nan = True, cls = None, indent = None, separators = None, default = None, sort_keys = False, ** kw) - серіалізуює obj, записуючи його в файл-об'єкт fp.
    • Якщо skipkeys = True, то ключі словника не базового типу (str, unicode, int, long, float, bool, None) будуть проігноровані, замість того, щоб викликати виключення TypeError.
    • Якщо ensure_ascii = True, всі не-ASCII символи будуть записані в файл послідовностями \uXXXX, і результатом буде рядок, що містить тільки ASCII символи. Якщо ensure_ascii = False, рядки запишуться як є.
    • Якщо check_circular = False, то перевірка циклічних посилань буде пропущена, а такі посилання будуть викликати OverflowError.
    • Якщо allow_nan = False, при спробі серіалізувати значення з коми, що виходить за допустимі межі, буде викликатися ValueError (nan, inf, -inf) в суворій відповідності зі специфікацією еуJSON, замість того, щоб використовувати еквіваленти з JavaScript (NaN, Infinity, -Infinity ).
    • Якщо indent є невід'ємним числом, то масиви і об'єкти в JSON будуть виводитися з цим рівнем відступу. Якщо рівень відступу 0, негативний або "", то замість цього просто використовуватимуться нові рядки. Значення за замовчуванням None відображає найбільш компактне уявлення. Якщо indent - рядок, то вона і буде використовуватися в якості відступу.
    • Якщо sort_keys = True, то ключі виведеного словника будуть відсортовані.
  • json.dumps (obj, skipkeys = False, ensure_ascii = True, check_circular = True, allow_nan = True, cls = None, indent = None, separators = None, default = None, sort_keys = False, ** kw) - серіалізує obj в рядок.
Аргументи мають те ж значення, що і для dump().
  • json.load (fp, cls = None, object_hook = None, parse_float = None, parse_int = None, parse_constant = None, object_pairs_hook = None, ** kw) – десеріалізує JSON з fp.
    • object_hook - опціональна функція, яка викликається для кожного JSON-об'єкта (тобто словника), який передається їй як аргумент. Повернуте значення цієї функції буде використовуватися замість словника.
    • object_pairs_hook - опціональна функція, яка застосовується до результату декодування об'єкта з певною послідовністю пар ключ / значення. Буде використаний результат, що повертається функцією, замість справжнього словника. Якщо заданий так само object_hook, то пріоритет віддається object_pairs_hook.
    • parse_float, якщо визначений, буде викликаний для кожного значення JSON з плаваючою точкою. За замовчуванням, це еквівалентно float(num_str).
    • parse_int, якщо визначений, буде викликаний для рядка JSON з числовим значенням. За замовчуванням еквівалентно int (num_str).
    • parse_constant, якщо визначений, буде викликаний для наступних рядків: "-Infinity", "Infinity", "NaN". Може бути використано для порушення винятків при виявленні помилкових чисел JSON. Якщо не вдасться десеріалізувати JSON, буде порушено виняток ValueError.
  • json.loads (s, encoding = None, cls = None, object_hook = None, parse_float = None, parse_int = None, parse_constant = None, object_pairs_hook = None,** kw) - десеріалізує s (екземпляр str, що містить документ JSON) в об'єкт Python.
Інші аргументи аналогічні аргументам в load().

Структури даних стандартної бібліотеки та рекомандації по їх підбору

  • List - структура даних, яка містить впорядкований набір елементів.
  • Set - невпорядкована множина з відмнних хешованих елементів.
  • Dict - розділяє хешовані значення за довільними змінювальними елементами.
  • Default dict - словник, ініціалізований зі значеннями за замовчуванням, коли кожен ключ зустрічається один раз.
  • Deque - узагальнення стеків та черг.
  • Heapq - бінарне дерево, для якого кожна батьківська комірка має значення менше або рівне за значення своїх нащадків.
  • Counter - підклас словника для підрахунку хешованих об'єктів. Це невпорядкована колекція, де елементи зберігаються як ключі словника і їхні значення зберігаються як значення словника.
  • Bisect - модуль, що встановлює алгоритм для вставки елементів до списку поки утримує список впорядкованим.