askpablos-api 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- askpablos_api/__init__.py +28 -0
- askpablos_api/client.py +245 -0
- askpablos_api/config.py +20 -0
- askpablos_api/core.py +113 -0
- askpablos_api/exceptions.py +79 -0
- askpablos_api/utils.py +79 -0
- askpablos_api-0.1.0.dist-info/METADATA +255 -0
- askpablos_api-0.1.0.dist-info/RECORD +11 -0
- askpablos_api-0.1.0.dist-info/WHEEL +5 -0
- askpablos_api-0.1.0.dist-info/licenses/LICENSE +21 -0
- askpablos_api-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api
|
|
3
|
+
|
|
4
|
+
A Python client library for interacting with the AskPablos proxy API service.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .core import AskPablos
|
|
8
|
+
from .client import ProxyClient
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
AskPablosError,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
APIConnectionError,
|
|
13
|
+
ResponseError
|
|
14
|
+
)
|
|
15
|
+
from .utils import configure_logging
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
|
|
19
|
+
# Set up default exports
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AskPablos",
|
|
22
|
+
"ProxyClient",
|
|
23
|
+
"AskPablosError",
|
|
24
|
+
"AuthenticationError",
|
|
25
|
+
"APIConnectionError",
|
|
26
|
+
"ResponseError",
|
|
27
|
+
"configure_logging",
|
|
28
|
+
]
|
askpablos_api/client.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api.client
|
|
3
|
+
|
|
4
|
+
This module provides the main client class for interacting with the AskPablos proxy API.
|
|
5
|
+
|
|
6
|
+
The ProxyClient class handles low-level communication with the AskPablos API service,
|
|
7
|
+
including authentication, request signing, and HTTP communication. It serves as the
|
|
8
|
+
foundation for higher-level interfaces like the AskPablos class.
|
|
9
|
+
|
|
10
|
+
This module is designed for users who need direct control over API requests and
|
|
11
|
+
authentication handling, or for building custom interfaces on top of the base client.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import base64
|
|
16
|
+
import hmac
|
|
17
|
+
import hashlib
|
|
18
|
+
import requests
|
|
19
|
+
from typing import Optional, Dict, Any
|
|
20
|
+
|
|
21
|
+
from .exceptions import APIConnectionError, ResponseError, AuthenticationError
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ResponseData:
|
|
25
|
+
"""Response object that provides dot notation access to response data."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, status_code: int, headers: Dict[str, str], content: str,
|
|
28
|
+
url: str, elapsed: float, encoding: Optional[str],
|
|
29
|
+
json_data: Optional[Dict[str, Any]] = None):
|
|
30
|
+
self.status_code = status_code
|
|
31
|
+
self.headers = headers
|
|
32
|
+
self.content = content
|
|
33
|
+
self.url = url
|
|
34
|
+
self.elapsed = elapsed
|
|
35
|
+
self.encoding = encoding
|
|
36
|
+
self.json = json_data
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ProxyClient:
|
|
40
|
+
"""
|
|
41
|
+
ProxyClient securely sends requests through the AskPablos proxy service.
|
|
42
|
+
|
|
43
|
+
This is the low-level client that handles direct communication with the AskPablos
|
|
44
|
+
API. It manages authentication via HMAC-SHA256 signatures, constructs proper
|
|
45
|
+
request headers, and handles HTTP communication with the proxy service.
|
|
46
|
+
|
|
47
|
+
The ProxyClient is typically used internally by higher-level classes like AskPablos,
|
|
48
|
+
but can be used directly for fine-grained control over API interactions.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
api_key (str): The API key for authentication.
|
|
52
|
+
secret_key (str): The secret key for HMAC signing.
|
|
53
|
+
api_url (str): The base URL of the proxy API service.
|
|
54
|
+
|
|
55
|
+
Security Note:
|
|
56
|
+
This client uses HMAC-SHA256 signatures to ensure request integrity and
|
|
57
|
+
authenticity. The secret_key is never transmitted and is only used locally
|
|
58
|
+
to generate signatures.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, api_key: str, secret_key: str, api_url: str):
|
|
62
|
+
"""
|
|
63
|
+
Initialize the API client.
|
|
64
|
+
|
|
65
|
+
Sets up the client with authentication credentials and validates that
|
|
66
|
+
all required parameters are provided.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
api_key (str): Your API key from the AskPablos dashboard. This identifies
|
|
70
|
+
your account and is included in the X-API-Key header.
|
|
71
|
+
secret_key (str): Your shared secret for HMAC signing. This is used to
|
|
72
|
+
generate request signatures and must be kept secure.
|
|
73
|
+
api_url (str): The proxy API base URL. This should be the full URL to
|
|
74
|
+
the proxy endpoint.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
AuthenticationError: If any required credential is missing or empty.
|
|
78
|
+
"""
|
|
79
|
+
self.api_key = api_key
|
|
80
|
+
self.secret_key = secret_key
|
|
81
|
+
self.api_url = api_url
|
|
82
|
+
|
|
83
|
+
if not all([api_key, secret_key, api_url]):
|
|
84
|
+
raise AuthenticationError("API key, secret key, and API URL must all be provided")
|
|
85
|
+
|
|
86
|
+
def _generate_signature(self, payload: str) -> str:
|
|
87
|
+
"""
|
|
88
|
+
Generate a base64-encoded HMAC SHA256 signature.
|
|
89
|
+
|
|
90
|
+
Creates a cryptographic signature of the request payload using the client's
|
|
91
|
+
secret key. This signature is used by the API server to verify that the
|
|
92
|
+
request came from an authorized client and hasn't been tampered with.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
payload (str): JSON string of the request body that will be signed.
|
|
96
|
+
This should be the exact JSON that will be sent in the
|
|
97
|
+
HTTP request body.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
str: Base64 encoded HMAC-SHA256 signature of the payload.
|
|
101
|
+
|
|
102
|
+
Note:
|
|
103
|
+
The signature is generated using HMAC-SHA256 with the client's secret key.
|
|
104
|
+
The resulting binary signature is then base64-encoded for transmission
|
|
105
|
+
in HTTP headers.
|
|
106
|
+
"""
|
|
107
|
+
signature = hmac.new(
|
|
108
|
+
self.secret_key.encode(),
|
|
109
|
+
payload.encode(),
|
|
110
|
+
hashlib.sha256
|
|
111
|
+
).digest()
|
|
112
|
+
return base64.b64encode(signature).decode()
|
|
113
|
+
|
|
114
|
+
def _build_headers(self, payload: str) -> dict:
|
|
115
|
+
"""
|
|
116
|
+
Construct request headers with API key and signature.
|
|
117
|
+
|
|
118
|
+
Builds the complete set of HTTP headers needed for API authentication,
|
|
119
|
+
including the content type, API key, and request signature.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
payload (str): JSON string of the request body. Used to generate
|
|
123
|
+
the authentication signature.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
dict: HTTP headers dictionary containing:
|
|
127
|
+
- Content-Type: Set to "application/json"
|
|
128
|
+
- X-API-Key: Your API key for identification
|
|
129
|
+
- X-Signature: HMAC-SHA256 signature of the payload
|
|
130
|
+
"""
|
|
131
|
+
return {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
'X-API-Key': self.api_key,
|
|
134
|
+
'X-Signature': self._generate_signature(payload)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
def request(
|
|
138
|
+
self,
|
|
139
|
+
url: str,
|
|
140
|
+
method: str = "GET",
|
|
141
|
+
data: dict = None,
|
|
142
|
+
headers: dict = None,
|
|
143
|
+
params: dict = None,
|
|
144
|
+
options: dict = None,
|
|
145
|
+
timeout: int = 30
|
|
146
|
+
) -> ResponseData:
|
|
147
|
+
"""
|
|
148
|
+
Send a request through the AskPablos proxy.
|
|
149
|
+
|
|
150
|
+
This is the core method that handles communication with the AskPablos API.
|
|
151
|
+
It constructs the request payload, generates authentication headers,
|
|
152
|
+
and sends the HTTP request to the proxy service.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
url (str): The target URL to fetch through the proxy. Must be a valid
|
|
156
|
+
HTTP or HTTPS URL.
|
|
157
|
+
method (str, optional): HTTP method for the request. Supported methods
|
|
158
|
+
include GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.
|
|
159
|
+
Defaults to "GET".
|
|
160
|
+
data (dict, optional): Request payload for POST/PUT requests. Will be
|
|
161
|
+
included in the request body sent to the target URL.
|
|
162
|
+
headers (dict, optional): Custom headers to send to the target URL.
|
|
163
|
+
These are forwarded to the target server, not
|
|
164
|
+
used for API authentication.
|
|
165
|
+
params (dict, optional): Query parameters to append to the target URL.
|
|
166
|
+
options (dict, optional): Proxy-specific options controlling how the
|
|
167
|
+
request is processed. See options below.
|
|
168
|
+
timeout (int, optional): Request timeout in seconds. If the proxy
|
|
169
|
+
doesn't respond within this time, the request
|
|
170
|
+
will be cancelled. Defaults to 30.
|
|
171
|
+
|
|
172
|
+
Proxy Options:
|
|
173
|
+
The options dictionary can contain:
|
|
174
|
+
- browser (bool): Use browser automation for JavaScript rendering
|
|
175
|
+
- rotate_proxy (bool): Use proxy rotation for this request
|
|
176
|
+
- user_agent (str): Custom user agent string
|
|
177
|
+
- cookies (dict): Cookies to include with the request
|
|
178
|
+
- Any other proxy-specific options supported by the API
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
dict: The JSON response from the AskPablos API containing the results
|
|
182
|
+
of the proxied request. The exact structure depends on the API
|
|
183
|
+
version, but typically includes status_code, headers, content, etc.
|
|
184
|
+
|
|
185
|
+
Raises:
|
|
186
|
+
APIConnectionError: If the client cannot connect to the AskPablos API
|
|
187
|
+
due to network issues, DNS problems, or timeouts.
|
|
188
|
+
ResponseError: If the API returns an HTTP error status code (4xx or 5xx).
|
|
189
|
+
The exception will include the status code and error message.
|
|
190
|
+
"""
|
|
191
|
+
# Initialize options dictionary if not provided
|
|
192
|
+
if options is None:
|
|
193
|
+
options = {}
|
|
194
|
+
|
|
195
|
+
# Create the request data - matching your exact format
|
|
196
|
+
request_data = {
|
|
197
|
+
"url": url,
|
|
198
|
+
"method": method.upper(),
|
|
199
|
+
"browser": options.get("browser", False),
|
|
200
|
+
"rotateProxy": options.get("rotate_proxy", False)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if data:
|
|
204
|
+
request_data["data"] = data
|
|
205
|
+
|
|
206
|
+
if headers:
|
|
207
|
+
request_data["headers"] = headers
|
|
208
|
+
|
|
209
|
+
if params:
|
|
210
|
+
request_data["params"] = params
|
|
211
|
+
|
|
212
|
+
# Add any additional options
|
|
213
|
+
for key, value in options.items():
|
|
214
|
+
if key not in ["browser", "rotate_proxy"]:
|
|
215
|
+
request_data[key] = value
|
|
216
|
+
|
|
217
|
+
# Convert to JSON with same separators as your example
|
|
218
|
+
payload = json.dumps(request_data, separators=(',', ':'))
|
|
219
|
+
headers = self._build_headers(payload)
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
response = requests.post(self.api_url, data=payload, headers=headers, timeout=timeout)
|
|
223
|
+
|
|
224
|
+
if response.status_code >= 400:
|
|
225
|
+
error_msg = response.json().get('error', 'Unknown error') if response.text else 'No error details provided'
|
|
226
|
+
raise ResponseError(response.status_code, error_msg)
|
|
227
|
+
|
|
228
|
+
# Return complete response information
|
|
229
|
+
response_data = ResponseData(
|
|
230
|
+
url=response.url,
|
|
231
|
+
status_code=response.status_code,
|
|
232
|
+
headers=dict(response.headers),
|
|
233
|
+
content=json.loads(response.text).get('data'),
|
|
234
|
+
elapsed=response.elapsed.total_seconds(),
|
|
235
|
+
encoding=response.encoding
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
response_data.json = response.json()
|
|
240
|
+
except (ValueError, json.JSONDecodeError):
|
|
241
|
+
response_data.json = None
|
|
242
|
+
|
|
243
|
+
return response_data
|
|
244
|
+
except requests.RequestException as e:
|
|
245
|
+
raise APIConnectionError(f"Failed to connect to API: {str(e)}")
|
askpablos_api/config.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api.config
|
|
3
|
+
|
|
4
|
+
Configuration settings for the AskPablos API client.
|
|
5
|
+
|
|
6
|
+
This module contains default configuration values and settings that can be
|
|
7
|
+
customized if needed. The primary API URL is defined here so users don't
|
|
8
|
+
need to specify it when creating client instances.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Default API configuration
|
|
12
|
+
DEFAULT_API_URL = "http://10.10.10.178:7500/api/proxy/"
|
|
13
|
+
|
|
14
|
+
# Default request settings - matching your example
|
|
15
|
+
DEFAULT_TIMEOUT = 30
|
|
16
|
+
DEFAULT_ROTATE_PROXY = True
|
|
17
|
+
DEFAULT_BROWSER = False
|
|
18
|
+
|
|
19
|
+
# User agent for requests
|
|
20
|
+
DEFAULT_USER_AGENT = "AskPablos-Python-Client/0.1.0"
|
askpablos_api/core.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api.core
|
|
3
|
+
|
|
4
|
+
Core functionality for the AskPablos proxy API client.
|
|
5
|
+
|
|
6
|
+
This module provides the main AskPablos class, which serves as a simple interface
|
|
7
|
+
for making GET requests through the AskPablos proxy service. The client handles
|
|
8
|
+
authentication, error management, and request formatting automatically.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Dict, Optional
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
from .client import ProxyClient, ResponseData
|
|
15
|
+
from .utils import build_proxy_options
|
|
16
|
+
from .exceptions import AskPablosError
|
|
17
|
+
from .config import DEFAULT_API_URL
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger("askpablos_api")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AskPablos:
|
|
23
|
+
"""
|
|
24
|
+
Simple interface for making GET requests through the AskPablos proxy API.
|
|
25
|
+
|
|
26
|
+
This class provides a clean interface for sending GET requests through the
|
|
27
|
+
AskPablos proxy service. It handles authentication, request formatting,
|
|
28
|
+
and error management automatically.
|
|
29
|
+
|
|
30
|
+
The AskPablos class is designed to be simple and focused - it only supports
|
|
31
|
+
GET requests to keep the interface clean and easy to use.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
client (ProxyClient): The underlying client for making API requests.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, api_key: str, secret_key: str):
|
|
39
|
+
"""
|
|
40
|
+
Initialize the AskPablos API client.
|
|
41
|
+
|
|
42
|
+
Creates a new instance of the AskPablos client with the provided
|
|
43
|
+
authentication credentials. The client will use these credentials
|
|
44
|
+
for all subsequent API requests.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
api_key (str): Your unique API key from the AskPablos dashboard.
|
|
48
|
+
secret_key (str): Your private secret key used for HMAC signing.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
AuthenticationError: If any of the required credentials are missing
|
|
52
|
+
or invalid.
|
|
53
|
+
"""
|
|
54
|
+
self.client = ProxyClient(api_key, secret_key, DEFAULT_API_URL)
|
|
55
|
+
|
|
56
|
+
def get(
|
|
57
|
+
self,
|
|
58
|
+
url: str,
|
|
59
|
+
params: Optional[Dict[str, str]] = None,
|
|
60
|
+
headers: Optional[Dict[str, str]] = None,
|
|
61
|
+
browser: bool = False,
|
|
62
|
+
rotate_proxy: bool = False,
|
|
63
|
+
timeout: int = 30,
|
|
64
|
+
**options
|
|
65
|
+
) -> ResponseData:
|
|
66
|
+
"""
|
|
67
|
+
Send a GET request through the AskPablos proxy.
|
|
68
|
+
|
|
69
|
+
This is the main method for fetching web pages and API endpoints through
|
|
70
|
+
the AskPablos proxy service. It supports various options for customizing
|
|
71
|
+
the request behavior.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
url (str): The target URL to fetch. Must be a valid HTTP/HTTPS URL.
|
|
75
|
+
params (Dict[str, str], optional): URL query parameters to append.
|
|
76
|
+
Example: {"page": "1", "limit": "10"}
|
|
77
|
+
headers (Dict[str, str], optional): Custom headers for the request.
|
|
78
|
+
browser (bool, optional): Whether to use browser automation for
|
|
79
|
+
JavaScript rendering. Useful for SPAs and
|
|
80
|
+
dynamic content. Defaults to False.
|
|
81
|
+
rotate_proxy (bool, optional): Whether to use proxy rotation for this
|
|
82
|
+
request. Helps avoid rate limiting.
|
|
83
|
+
Defaults to True.
|
|
84
|
+
timeout (int, optional): Request timeout in seconds. Defaults to 30.
|
|
85
|
+
**options: Additional proxy options like user_agent, cookies, etc.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
ResponseData: The response object from the API containing:
|
|
89
|
+
- status_code (int): HTTP status code from the target server
|
|
90
|
+
- headers (Dict[str, str]): Response headers from target server
|
|
91
|
+
- content (str): Response body/content
|
|
92
|
+
- url (str): Final URL after any redirects
|
|
93
|
+
- elapsed (float): Time taken to complete the request in seconds
|
|
94
|
+
- encoding (Optional[str]): Response text encoding
|
|
95
|
+
- json (Optional[Dict[str, Any]]): Parsed JSON data if available
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
APIConnectionError: If the client cannot connect to the AskPablos API.
|
|
99
|
+
ResponseError: If the API returns an error status code.
|
|
100
|
+
AuthenticationError: If authentication fails.
|
|
101
|
+
"""
|
|
102
|
+
# Build proxy options
|
|
103
|
+
proxy_options = build_proxy_options(
|
|
104
|
+
browser=browser,
|
|
105
|
+
rotate_proxy=rotate_proxy,
|
|
106
|
+
**options
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
return self.client.request(url=url, headers=headers, params=params, options=proxy_options, timeout=timeout)
|
|
111
|
+
except AskPablosError as e:
|
|
112
|
+
logger.error(f"GET request failed: {str(e)}")
|
|
113
|
+
raise
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api.exceptions
|
|
3
|
+
|
|
4
|
+
This module defines exceptions specific to the AskPablos API client.
|
|
5
|
+
|
|
6
|
+
The exception hierarchy is designed to provide clear and specific error handling
|
|
7
|
+
for different types of failures that can occur when interacting with the AskPablos
|
|
8
|
+
API service.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AskPablosError(Exception):
|
|
13
|
+
"""
|
|
14
|
+
Base exception for all AskPablos API errors.
|
|
15
|
+
|
|
16
|
+
This is the root exception class for all errors that can occur within the
|
|
17
|
+
AskPablos API client. All other exceptions inherit from this base class.
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AuthenticationError(AskPablosError):
|
|
23
|
+
"""
|
|
24
|
+
Raised when there is an authentication problem with the API.
|
|
25
|
+
|
|
26
|
+
This exception is raised when:
|
|
27
|
+
- API key is missing or empty
|
|
28
|
+
- Secret key is missing or empty
|
|
29
|
+
- Authentication credentials are invalid
|
|
30
|
+
- HMAC signature verification fails
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class APIConnectionError(AskPablosError):
|
|
36
|
+
"""
|
|
37
|
+
Raised when the client fails to connect to the API.
|
|
38
|
+
|
|
39
|
+
This exception is raised when:
|
|
40
|
+
- Network connectivity issues prevent reaching the API
|
|
41
|
+
- DNS resolution fails for the API URL
|
|
42
|
+
- Connection timeouts occur
|
|
43
|
+
- The API server is unreachable or down
|
|
44
|
+
"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ResponseError(AskPablosError):
|
|
49
|
+
"""
|
|
50
|
+
Raised when there is an error in the API response.
|
|
51
|
+
|
|
52
|
+
This exception is raised when the AskPablos API returns an HTTP error
|
|
53
|
+
status code (4xx or 5xx). It includes both the status code and the
|
|
54
|
+
error message from the API response.
|
|
55
|
+
|
|
56
|
+
Common scenarios:
|
|
57
|
+
- 400 Bad Request: Invalid request parameters
|
|
58
|
+
- 401 Unauthorized: Authentication failed
|
|
59
|
+
- 403 Forbidden: Access denied
|
|
60
|
+
- 404 Not Found: Endpoint not found
|
|
61
|
+
- 429 Too Many Requests: Rate limit exceeded
|
|
62
|
+
- 500 Internal Server Error: Server error
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
status_code (int): The HTTP status code from the API response
|
|
66
|
+
message (str): The error message from the API response
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, status_code: int, message: str):
|
|
70
|
+
"""
|
|
71
|
+
Initialize a ResponseError with status code and message.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
status_code (int): The HTTP status code from the API response
|
|
75
|
+
message (str): The error message describing what went wrong
|
|
76
|
+
"""
|
|
77
|
+
self.status_code = status_code
|
|
78
|
+
self.message = message
|
|
79
|
+
super().__init__(f"API response error (status {status_code}): {message}")
|
askpablos_api/utils.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
askpablos_api.utils
|
|
3
|
+
|
|
4
|
+
Utility functions and helpers for the AskPablos API client.
|
|
5
|
+
|
|
6
|
+
This module provides utility functions that support the main API client
|
|
7
|
+
functionality, including logging configuration and option building.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Optional, Dict, Any
|
|
12
|
+
|
|
13
|
+
# Configure logging
|
|
14
|
+
logger = logging.getLogger("askpablos_api")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def configure_logging(level: int = logging.INFO,
|
|
18
|
+
format_string: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s") -> None:
|
|
19
|
+
"""
|
|
20
|
+
Configure the logger for the AskPablos API package.
|
|
21
|
+
|
|
22
|
+
Sets up logging for the AskPablos API client with customizable log level
|
|
23
|
+
and format. This is useful for debugging API requests and understanding
|
|
24
|
+
what's happening during client operations.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
level (int, optional): The logging level to use. Defaults to logging.INFO.
|
|
28
|
+
format_string (str, optional): The format string for log messages.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> from askpablos_api import configure_logging
|
|
32
|
+
>>> import logging
|
|
33
|
+
>>> configure_logging(level=logging.DEBUG)
|
|
34
|
+
"""
|
|
35
|
+
handler = logging.StreamHandler()
|
|
36
|
+
formatter = logging.Formatter(format_string)
|
|
37
|
+
handler.setFormatter(formatter)
|
|
38
|
+
|
|
39
|
+
logger.setLevel(level)
|
|
40
|
+
logger.addHandler(handler)
|
|
41
|
+
logger.propagate = False
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_proxy_options(browser: bool = False,
|
|
45
|
+
rotate_proxy: bool = True,
|
|
46
|
+
user_agent: Optional[str] = None,
|
|
47
|
+
cookies: Optional[Dict[str, str]] = None,
|
|
48
|
+
**kwargs) -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Build a dictionary of options for the proxy request.
|
|
51
|
+
|
|
52
|
+
Creates a standardized options dictionary for proxy requests with common
|
|
53
|
+
parameters and defaults.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
browser (bool, optional): Whether to use browser automation. Defaults to False.
|
|
57
|
+
rotate_proxy (bool, optional): Whether to use proxy rotation. Defaults to True.
|
|
58
|
+
user_agent (str, optional): Custom User-Agent string.
|
|
59
|
+
cookies (Dict[str, str], optional): Dictionary of cookies.
|
|
60
|
+
**kwargs: Additional options to include in the proxy request.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Dict[str, Any]: Dictionary of proxy options.
|
|
64
|
+
"""
|
|
65
|
+
options = {
|
|
66
|
+
"browser": browser,
|
|
67
|
+
"rotate_proxy": rotate_proxy
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if user_agent:
|
|
71
|
+
options["user_agent"] = user_agent
|
|
72
|
+
|
|
73
|
+
if cookies:
|
|
74
|
+
options["cookies"] = cookies
|
|
75
|
+
|
|
76
|
+
# Add any additional options
|
|
77
|
+
options.update(kwargs)
|
|
78
|
+
|
|
79
|
+
return options
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: askpablos-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Professional Python client for the AskPablos proxy API service
|
|
5
|
+
Author-email: Fawad Ali <fawadstar6@gmail.com>
|
|
6
|
+
Maintainer-email: Fawad Ali <fawadstar6@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/fawadss1/askpablos-api
|
|
9
|
+
Project-URL: Documentation, https://askpablos-api.readthedocs.io
|
|
10
|
+
Project-URL: Repository, https://github.com/fawadss1/askpablos-api
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/fawadss1/askpablos-api/issues
|
|
12
|
+
Keywords: proxy,api,web-scraping,browser,http-client,askpablos
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
25
|
+
Classifier: Topic :: Internet :: Proxy Servers
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Topic :: System :: Networking
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: requests>=2.25.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=21.0; extra == "dev"
|
|
36
|
+
Requires-Dist: flake8>=3.8; extra == "dev"
|
|
37
|
+
Requires-Dist: mypy>=0.900; extra == "dev"
|
|
38
|
+
Requires-Dist: pre-commit>=2.0; extra == "dev"
|
|
39
|
+
Provides-Extra: docs
|
|
40
|
+
Requires-Dist: sphinx>=4.0; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
|
|
42
|
+
Requires-Dist: myst-parser>=0.15; extra == "docs"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# AskPablos API Client
|
|
46
|
+
[](https://pypi.python.org/pypi/askpablos-api)
|
|
47
|
+
[](https://pypi.python.org/pypi/askpablos-api)
|
|
48
|
+
|
|
49
|
+
A simple Python client for making GET requests through the AskPablos proxy API service. This library provides a clean and easy-to-use interface for fetching web pages and APIs through the AskPablos proxy infrastructure.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- 🔐 **Secure Authentication**: HMAC-SHA256 signature-based authentication
|
|
54
|
+
- 🌐 **Proxy Support**: Route requests through rotating proxies
|
|
55
|
+
- 🤖 **Browser Integration**: Support for JavaScript-heavy websites
|
|
56
|
+
- 🛡️ **Error Handling**: Comprehensive exception handling
|
|
57
|
+
- 📊 **Logging**: Built-in logging support for debugging
|
|
58
|
+
- 🎯 **Simple Interface**: GET-only requests for clean API
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install askpablos-api
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from askpablos_api import AskPablos
|
|
70
|
+
|
|
71
|
+
# Initialize the client
|
|
72
|
+
client = AskPablos(
|
|
73
|
+
api_key="your_api_key",
|
|
74
|
+
secret_key="your_secret_key"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Make a simple GET request
|
|
78
|
+
response = client.get("https://httpbin.org/ip")
|
|
79
|
+
print(response)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Authentication
|
|
83
|
+
|
|
84
|
+
The AskPablos API uses HMAC-SHA256 signature-based authentication. You only need:
|
|
85
|
+
|
|
86
|
+
1. **API Key**: Your unique API identifier
|
|
87
|
+
2. **Secret Key**: Your private key for signing requests
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from askpablos_api import AskPablos
|
|
91
|
+
|
|
92
|
+
client = AskPablos(
|
|
93
|
+
api_key="your_api_key",
|
|
94
|
+
secret_key="your_secret_key"
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Usage Examples
|
|
99
|
+
|
|
100
|
+
### Basic GET Requests
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Simple GET request
|
|
104
|
+
response = client.get("https://example.com")
|
|
105
|
+
|
|
106
|
+
# GET with query parameters
|
|
107
|
+
response = client.get(
|
|
108
|
+
"https://api.example.com/users",
|
|
109
|
+
params={"page": 1, "limit": 10}
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# GET with custom headers
|
|
113
|
+
response = client.get(
|
|
114
|
+
"https://api.example.com/data",
|
|
115
|
+
headers={"Authorization": "Bearer token123"}
|
|
116
|
+
)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Advanced Options
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Use browser automation for JavaScript-heavy sites
|
|
123
|
+
response = client.get(
|
|
124
|
+
"https://spa-website.com",
|
|
125
|
+
browser=True
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Disable proxy rotation
|
|
129
|
+
response = client.get(
|
|
130
|
+
"https://example.com",
|
|
131
|
+
rotate_proxy=False
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Custom user agent and cookies
|
|
135
|
+
response = client.get(
|
|
136
|
+
"https://example.com",
|
|
137
|
+
user_agent="Mozilla/5.0 (Custom Bot)",
|
|
138
|
+
cookies={"session": "abc123"}
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Custom timeout
|
|
142
|
+
response = client.get(
|
|
143
|
+
"https://slow-website.com",
|
|
144
|
+
timeout=60
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Error Handling
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from askpablos_api import (
|
|
152
|
+
AskPablos,
|
|
153
|
+
AuthenticationError,
|
|
154
|
+
APIConnectionError,
|
|
155
|
+
ResponseError
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
client = AskPablos(api_key="", secret_key="")
|
|
160
|
+
except AuthenticationError as e:
|
|
161
|
+
print(f"Authentication failed: {e}")
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
response = client.get("https://example.com")
|
|
165
|
+
except APIConnectionError as e:
|
|
166
|
+
print(f"Connection failed: {e}")
|
|
167
|
+
except ResponseError as e:
|
|
168
|
+
print(f"API error {e.status_code}: {e.message}")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Logging
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from askpablos_api import configure_logging
|
|
175
|
+
import logging
|
|
176
|
+
|
|
177
|
+
# Enable debug logging
|
|
178
|
+
configure_logging(level=logging.DEBUG)
|
|
179
|
+
|
|
180
|
+
client = AskPablos(api_key="...", secret_key="...")
|
|
181
|
+
response = client.get("https://example.com") # This will be logged
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## API Reference
|
|
185
|
+
|
|
186
|
+
### AskPablos Class
|
|
187
|
+
|
|
188
|
+
The main interface for the API client.
|
|
189
|
+
|
|
190
|
+
#### Constructor
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
AskPablos(api_key: str, secret_key: str)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Parameters:**
|
|
197
|
+
- `api_key` (str): Your API key from the AskPablos dashboard
|
|
198
|
+
- `secret_key` (str): Your secret key for HMAC signing
|
|
199
|
+
|
|
200
|
+
#### Methods
|
|
201
|
+
|
|
202
|
+
##### get()
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
get(url, params=None, headers=None, browser=False, rotate_proxy=True, timeout=30, **options)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Send a GET request through the AskPablos proxy.
|
|
209
|
+
|
|
210
|
+
**Parameters:**
|
|
211
|
+
- `url` (str): Target URL to fetch
|
|
212
|
+
- `params` (dict, optional): URL query parameters
|
|
213
|
+
- `headers` (dict, optional): Custom headers
|
|
214
|
+
- `browser` (bool, optional): Use browser automation (default: False)
|
|
215
|
+
- `rotate_proxy` (bool, optional): Enable proxy rotation (default: True)
|
|
216
|
+
- `timeout` (int, optional): Request timeout in seconds (default: 30)
|
|
217
|
+
- `**options`: Additional options like user_agent, cookies, etc.
|
|
218
|
+
|
|
219
|
+
**Returns:** Dictionary containing the API response
|
|
220
|
+
|
|
221
|
+
### Exception Classes
|
|
222
|
+
|
|
223
|
+
- `AskPablosError` - Base exception class
|
|
224
|
+
- `AuthenticationError` - Authentication-related errors
|
|
225
|
+
- `APIConnectionError` - Connection and network errors
|
|
226
|
+
- `ResponseError` - API response errors
|
|
227
|
+
|
|
228
|
+
## Response Format
|
|
229
|
+
|
|
230
|
+
All successful requests return a dictionary with:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
{
|
|
234
|
+
"status_code": 200,
|
|
235
|
+
"headers": {"content-type": "text/html", ...},
|
|
236
|
+
"content": "Response body content",
|
|
237
|
+
"url": "Final URL after redirects",
|
|
238
|
+
"proxy_used": "proxy.example.com:8080",
|
|
239
|
+
"time_taken": 1.23
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Requirements
|
|
244
|
+
|
|
245
|
+
- Python 3.9+
|
|
246
|
+
- requests >= 2.25.0
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
This project is licensed under the MIT License.
|
|
251
|
+
|
|
252
|
+
## Support
|
|
253
|
+
|
|
254
|
+
For support and questions:
|
|
255
|
+
- Email: fawadstar6@gmail.com
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
askpablos_api/__init__.py,sha256=oFUjsefQY1uzVw_3gXySwfjif1uKozOjz6PU7vLezI0,562
|
|
2
|
+
askpablos_api/client.py,sha256=8WE_mlL5HXxYwddvbY7BS0_FxY7VP6o_NzqABQBsU5A,10296
|
|
3
|
+
askpablos_api/config.py,sha256=k4a9DWqqLqpi6Kj_SaMmugM0lMfdWLLqqakUWJXZ8wI,593
|
|
4
|
+
askpablos_api/core.py,sha256=-flWGRtCIkjoFthnf5k-4tMVJ1qJqo0yXVrgENvXN8M,4692
|
|
5
|
+
askpablos_api/exceptions.py,sha256=FbAlgo8yQ0L6aewT2atnfJVm2ty-8r9WHcx6uCMbrvw,2486
|
|
6
|
+
askpablos_api/utils.py,sha256=0TAzmWutodhSIaHUiCvLlAb6fozDuOhDWZJiQgg-5BI,2584
|
|
7
|
+
askpablos_api-0.1.0.dist-info/licenses/LICENSE,sha256=DhJQ4_j45c_DWghISLKmJshcLvX_Pr7QXaahe2iRMNo,1087
|
|
8
|
+
askpablos_api-0.1.0.dist-info/METADATA,sha256=n9Wm6aINVDWr53vifsNNUJIcl-S32jeTv1GaQoz69Xk,7047
|
|
9
|
+
askpablos_api-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
askpablos_api-0.1.0.dist-info/top_level.txt,sha256=XM61mjwJQtG8sSxvUHD_yMOQKguLv3Yh0hc5S2zHlYc,14
|
|
11
|
+
askpablos_api-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Fawad Ali
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
askpablos_api
|