Sora-reader / backend

https://backend.sora-reader.app
GNU General Public License v3.0
4 stars 4 forks source link

Refactor parsers #114

Closed dhvcc closed 3 years ago

dhvcc commented 3 years ago

На данный момнт код немного запутан,так как мы используем 3 библиотеки для парсинга Есть предложение создать более структурированную архитектуру и разбить все на классы (миксины) с наследованием

Предлогается следующая архитектура:

  1. Есть 3 библиотеки (типы парсеров):
    1. lxml (requests+lxml)
    2. scrapy
    3. selenium (уже вместо pyppeteer)
  2. Итоговый парсер каталога это класс-наследник BaseParser
  3. Дополнение методов происхоит через наследование миксинов

TODO: Что делать с сохранением

Структура файлов

apps/common
├── admin.py
├── commands.py
├── models.py
├── parser
│   ├── base.py
│   ├── __init__.py
│   ├── lxml
│   │   ├── base.py
│   │   └── __init__.py
│   ├── scrapy
│   │   ├── base.py
│   │   └── __init__.py
│   └── selenium
│       ├── base.py
│       ├── __init__.py
│       └── utils.py
├── serializers.py
└── utils.py

Примерная диаграмма

не обязательно соответствующая стандартам

Parser Heirarchy


Набросок кода (уже не совсем валиден)

import requests

from typing import List
from abc import ABC, abstractmethod

class BaseParseMixin(ABC):
    @abstractmethod
    def __get_xpath(self) -> dict:
        ...

    @abstractmethod
    def __get_html(self, url: str):
        ...

    @abstractmethod
    def __check_response(self, *args, **kwargs) -> bool:
        ...

class BaseLXMLMixin(BaseParseMixin, ABC):
    REQUEST_HEADERS = {}

    def __get_response(self, url: str) -> requests.Response:
        return requests.get(url, headers=self.__class__.REQUEST_HEADERS)

    def __check_response(self, response: requests.Response):
        return response.status_code == 200

class SourceListMixin(BaseLXMLMixin):
    def __get_xpath(self):
        return {
            "title": "//title",
            "desccription": "//desc",
            "rss": None,
        }

    def parse_list(self) -> List[dict]:
        response = self.__get_response("https://source.com/mangaList")
        if not self.__check_response(response):
            raise Exception("Bad response")

        html = some_lxml_parsing_function(response.text)
        xpath = self.__get_xpath()
        output = []

        ...

        return output

class BaseParser:
    MANAGEMENT_NAME: str

    def parse_list(self, *args, **kwargs) -> List[dict]:
        ...

    def parse_details(self, *args, **kwargs) -> dict:
        ...

    def parse_chatpers(self, *args, **kwargs) -> List[dict]:
        ...

    def parse_images(self, *args, **kwargs) -> List[str]:
        ...

class SourceParser(BaseParser, SourceListMixin, ...):
    MANAGEMENT_NAME = "source"

    ...
dhvcc commented 3 years ago

@NikDark @MariaAfanasyeva :eyes: