|
13 | 13 | from .exceptions import RequestQuotaExceededError, TimeoutExceededError |
14 | 14 | from .handler_utils import ( |
15 | 15 | API_URL, |
| 16 | + RESPROXY_API_URL, |
16 | 17 | BATCH_MAX_SIZE, |
17 | 18 | CACHE_MAXSIZE, |
18 | 19 | CACHE_TTL, |
@@ -145,6 +146,57 @@ def getDetails(self, ip_address=None, timeout=None): |
145 | 146 |
|
146 | 147 | return Details(details) |
147 | 148 |
|
| 149 | + def getResproxy(self, ip_address, timeout=None): |
| 150 | + """ |
| 151 | + Get residential proxy information for specified IP address. |
| 152 | +
|
| 153 | + Returns a Details object containing: |
| 154 | + - ip: The IP address |
| 155 | + - last_seen: The last recorded date when the proxy was active (YYYY-MM-DD) |
| 156 | + - percent_days_seen: Percentage of days active in the last 7-day period |
| 157 | + - service: Name of the residential proxy service |
| 158 | +
|
| 159 | + If `timeout` is not `None`, it will override the client-level timeout |
| 160 | + just for this operation. |
| 161 | + """ |
| 162 | + if isinstance(ip_address, IPv4Address) or isinstance(ip_address, IPv6Address): |
| 163 | + ip_address = ip_address.exploded |
| 164 | + |
| 165 | + # check cache first. |
| 166 | + cache_key_str = f"resproxy:{ip_address}" |
| 167 | + try: |
| 168 | + cached_data = self.cache[cache_key(cache_key_str)] |
| 169 | + return Details(cached_data) |
| 170 | + except KeyError: |
| 171 | + pass |
| 172 | + |
| 173 | + # prepare req http opts |
| 174 | + req_opts = {**self.request_options} |
| 175 | + if timeout is not None: |
| 176 | + req_opts["timeout"] = timeout |
| 177 | + |
| 178 | + # do http req |
| 179 | + url = f"{RESPROXY_API_URL}/{ip_address}" |
| 180 | + headers = handler_utils.get_headers(self.access_token, self.headers) |
| 181 | + response = requests.get(url, headers=headers, **req_opts) |
| 182 | + if response.status_code == 429: |
| 183 | + raise RequestQuotaExceededError() |
| 184 | + if response.status_code >= 400: |
| 185 | + error_code = response.status_code |
| 186 | + content_type = response.headers.get("Content-Type") |
| 187 | + if content_type == "application/json": |
| 188 | + error_response = response.json() |
| 189 | + else: |
| 190 | + error_response = {"error": response.text} |
| 191 | + raise APIError(error_code, error_response) |
| 192 | + details = response.json() |
| 193 | + |
| 194 | + # cache result |
| 195 | + self.cache[cache_key(cache_key_str)] = details |
| 196 | + |
| 197 | + return Details(details) |
| 198 | + |
| 199 | + |
148 | 200 | def getBatchDetails( |
149 | 201 | self, |
150 | 202 | ip_addresses, |
|
0 commit comments