From 5e848ef0460e7a72d97782066763c39705827b39 Mon Sep 17 00:00:00 2001 From: Karim shoair Date: Wed, 6 Nov 2024 21:03:19 +0200 Subject: [PATCH] Adding new class type `TextHandlers` --- scrapling/core/custom_types.py | 49 ++++++++++++++++++++++++++++++++-- scrapling/parser.py | 4 ++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/scrapling/core/custom_types.py b/scrapling/core/custom_types.py index bffc36f..f157879 100644 --- a/scrapling/core/custom_types.py +++ b/scrapling/core/custom_types.py @@ -3,7 +3,7 @@ from types import MappingProxyType from collections.abc import Mapping from scrapling.core.utils import _is_iterable, flatten -from scrapling.core._types import Dict, List, Union, Pattern +from scrapling.core._types import Dict, List, Union, Pattern, SupportsIndex from orjson import loads, dumps from w3lib.html import replace_entities as _replace_entities @@ -69,7 +69,7 @@ class TextHandler(str): return [TextHandler(_replace_entities(s)) for s in results] def re_first(self, regex: Union[str, Pattern[str]], default=None, replace_entities: bool = True, - clean_match: bool = False, case_sensitive: bool = False,): + clean_match: bool = False, case_sensitive: bool = False) -> Union[str, None]: """Apply the given regex to text and return the first match if found, otherwise return the default value. :param regex: Can be either a compiled regular expression or a string. @@ -83,6 +83,51 @@ class TextHandler(str): return result[0] if result else default +class TextHandlers(List[TextHandler]): + """ + The :class:`TextHandlers` class is a subclass of the builtin ``List`` class, which provides a few additional methods. + """ + __slots__ = () + + def __getitem__(self, pos: Union[SupportsIndex, slice]) -> Union[TextHandler, "TextHandlers[TextHandler]"]: + lst = super().__getitem__(pos) + if isinstance(pos, slice): + return self.__class__(lst) + else: + return lst + + def re(self, regex: Union[str, Pattern[str]], replace_entities: bool = True, clean_match: bool = False, + case_sensitive: bool = False) -> 'List[str]': + """Call the ``.re()`` method for each element in this list and return + their results flattened as TextHandlers. + + :param regex: Can be either a compiled regular expression or a string. + :param replace_entities: if enabled character entity references are replaced by their corresponding character + :param clean_match: if enabled, this will ignore all whitespaces and consecutive spaces while matching + :param case_sensitive: if enabled, function will set the regex to ignore letters case while compiling it + """ + results = [ + n.re(regex, replace_entities, clean_match, case_sensitive) for n in self + ] + return flatten(results) + + def re_first(self, regex: Union[str, Pattern[str]], default=None, replace_entities: bool = True, + clean_match: bool = False, case_sensitive: bool = False) -> Union[str, None]: + """Call the ``.re_first()`` method for each element in this list and return + the first result or the default value otherwise. + + :param regex: Can be either a compiled regular expression or a string. + :param default: The default value to be returned if there is no match + :param replace_entities: if enabled character entity references are replaced by their corresponding character + :param clean_match: if enabled, this will ignore all whitespaces and consecutive spaces while matching + :param case_sensitive: if enabled, function will set the regex to ignore letters case while compiling it + """ + for n in self: + for result in n.re(regex, replace_entities, clean_match, case_sensitive): + return result + return default + + class AttributesHandler(Mapping): """A read-only mapping to use instead of the standard dictionary for the speed boost but at the same time I use it to add more functionalities. diff --git a/scrapling/parser.py b/scrapling/parser.py index f3d95b3..583b1c5 100644 --- a/scrapling/parser.py +++ b/scrapling/parser.py @@ -3,7 +3,7 @@ from difflib import SequenceMatcher from scrapling.core.translator import HTMLTranslator from scrapling.core.mixins import SelectorsGeneration -from scrapling.core.custom_types import TextHandler, AttributesHandler +from scrapling.core.custom_types import TextHandler, TextHandlers, AttributesHandler from scrapling.core.storage_adaptors import SQLiteStorageSystem, StorageSystemMixin, _StorageTools from scrapling.core.utils import setup_basic_logging, logging, clean_spaces, flatten, html_forbidden from scrapling.core._types import Any, Dict, List, Tuple, Optional, Pattern, Union, Callable, Generator, SupportsIndex @@ -158,6 +158,8 @@ class Adaptor(SelectorsGeneration): results = [self.__get_correct_result(n) for n in result] if all(isinstance(res, self.__class__) for res in results): return Adaptors(results) + elif all(isinstance(res, TextHandler) for res in results): + return TextHandlers(results) return results return self.__get_correct_result(result)