77
88from httpx import URL
99from more_itertools import flatten
10+ from pydantic import AnyHttpUrl , TypeAdapter
1011from typing_extensions import Protocol
1112
1213from crawlee ._utils .crypto import crypto_random_object_id
@@ -117,6 +118,7 @@ def __init__(
117118 self ._configuration = configuration or Configuration ()
118119 self ._next_custom_url_index = 0
119120 self ._used_proxy_urls = dict [str , URL ]()
121+ self ._url_validator = TypeAdapter (AnyHttpUrl )
120122
121123 # Validation
122124 if sum (map (bool , (proxy_urls , new_url_function , list (flatten (tiered_proxy_urls or []))))) != 1 :
@@ -125,9 +127,15 @@ def __init__(
125127 'must be specified (and non-empty).'
126128 )
127129
128- self ._proxy_urls = [URL (url ) for url in proxy_urls ] if proxy_urls else []
130+ self ._proxy_urls = (
131+ [URL (url ) for url in proxy_urls if self ._url_validator .validate_python (url )] if proxy_urls else []
132+ )
129133 self ._proxy_tier_tracker = (
130- ProxyTierTracker ([[URL (url ) for url in tier ] for tier in tiered_proxy_urls ]) if tiered_proxy_urls else None
134+ ProxyTierTracker (
135+ [[URL (url ) for url in tier if self ._url_validator .validate_python (url )] for tier in tiered_proxy_urls ]
136+ )
137+ if tiered_proxy_urls
138+ else None
131139 )
132140 self ._new_url_function = new_url_function
133141
@@ -163,15 +171,29 @@ async def new_proxy_info(
163171
164172 return info
165173
174+ async def new_url (
175+ self , session_id : str | None = None , request : Request | None = None , proxy_tier : int | None = None
176+ ) -> str | None :
177+ """Return a new proxy url.
178+
179+ If called repeatedly with the same request, it is assumed that the request is being retried.
180+ If a previously used session ID is received, it will return the same proxy url.
181+ """
182+ proxy_info = await self .new_proxy_info (session_id , request , proxy_tier )
183+ return proxy_info .url if proxy_info else None
184+
166185 async def _pick_url (
167186 self , session_id : str | None , request : Request | None , proxy_tier : int | None
168187 ) -> tuple [URL | None , int | None ]:
169188 if self ._new_url_function :
170- result = self ._new_url_function (session_id , request )
171- if inspect .isawaitable (result ):
172- result = await result
173-
174- return URL (cast (str , result )) if result is not None else None , None
189+ try :
190+ result = self ._new_url_function (session_id , request )
191+ if inspect .isawaitable (result ):
192+ result = await result
193+
194+ return URL (cast (str , result )) if result is not None else None , None
195+ except Exception as e :
196+ raise ValueError ('The provided "new_url_function" did not return a valid URL' ) from e
175197
176198 if self ._proxy_tier_tracker :
177199 if request is not None and proxy_tier is None :
0 commit comments