Karim shoair commited on
Commit
c0f5705
·
1 Parent(s): bbd0c34

feat(spiders): Add follow function to the response

Browse files
scrapling/core/_types.py CHANGED
@@ -12,6 +12,7 @@ from typing import (
12
  Callable,
13
  Dict,
14
  Generator,
 
15
  Generic,
16
  Iterable,
17
  List,
 
12
  Callable,
13
  Dict,
14
  Generator,
15
+ AsyncGenerator,
16
  Generic,
17
  Iterable,
18
  List,
scrapling/engines/toolbelt/custom.py CHANGED
@@ -10,12 +10,19 @@ from scrapling.core._types import (
10
  Dict,
11
  cast,
12
  List,
13
- Optional,
14
  Tuple,
 
 
 
 
 
15
  )
16
  from scrapling.core.custom_types import MappingProxyType
17
  from scrapling.parser import Selector, SQLiteStorageSystem
18
 
 
 
 
19
 
20
  class Response(Selector):
21
  """This class is returned by all engines as a way to unify the response type between different libraries."""
@@ -50,6 +57,52 @@ class Response(Selector):
50
  # For easier debugging while working from a Python shell
51
  log.info(f"Fetched ({status}) <{method} {url}> (referer: {request_headers.get('referer')})")
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  class BaseFetcher:
55
  __slots__ = ()
 
10
  Dict,
11
  cast,
12
  List,
 
13
  Tuple,
14
+ Union,
15
+ Optional,
16
+ Callable,
17
+ TYPE_CHECKING,
18
+ AsyncGenerator,
19
  )
20
  from scrapling.core.custom_types import MappingProxyType
21
  from scrapling.parser import Selector, SQLiteStorageSystem
22
 
23
+ if TYPE_CHECKING:
24
+ from scrapling.spiders import Request
25
+
26
 
27
  class Response(Selector):
28
  """This class is returned by all engines as a way to unify the response type between different libraries."""
 
57
  # For easier debugging while working from a Python shell
58
  log.info(f"Fetched ({status}) <{method} {url}> (referer: {request_headers.get('referer')})")
59
 
60
+ self.meta: Dict[str, Any] = {}
61
+ self.request: Optional["Request"] = None # Will be set by crawler
62
+
63
+ def follow(
64
+ self,
65
+ url: str,
66
+ sid: str = "",
67
+ callback: Callable[["Response"], AsyncGenerator[Union[Dict[str, Any], "Request", None], None]] | None = None,
68
+ priority: int | None = None,
69
+ dont_filter: bool = False,
70
+ meta: dict[str, Any] | None = None,
71
+ **kwargs: Any,
72
+ ) -> Any:
73
+ """Create a Request to follow a URL.
74
+
75
+ This is a helper method for spiders to easily follow links found in pages.
76
+
77
+ **IMPORTANT**: The below arguments if left empty, the corresponding value from the previous request will be used. The only exception is `dont_filter`.
78
+
79
+ :param url: The URL to follow (can be relative, will be joined with current URL)
80
+ :param sid: The session id to use
81
+ :param callback: Spider callback method to use
82
+ :param priority: The priority number to use, the higher the number, the higher priority to be processed first.
83
+ :param dont_filter: If this request has been done before, disable the filter to allow it again.
84
+ :param meta: Additional meta data to included in the request
85
+ :param kwargs: Additional Request arguments
86
+ :return: Request object ready to be yielded
87
+ """
88
+ from scrapling.spiders import Request
89
+
90
+ if not self.request or not isinstance(self.request, Request):
91
+ raise TypeError("This response has no request set yet.")
92
+
93
+ return Request(
94
+ url=self.urljoin(url),
95
+ sid=sid or self.request.sid,
96
+ callback=callback or self.request.callback,
97
+ priority=priority if priority is not None else self.request.priority,
98
+ dont_filter=dont_filter,
99
+ meta={**(self.meta or {}), **(meta or {})},
100
+ **(kwargs if kwargs else self.request._session_kwargs),
101
+ )
102
+
103
+ def __str__(self) -> str:
104
+ return f"<{self.status} {self.url}>"
105
+
106
 
107
  class BaseFetcher:
108
  __slots__ = ()