thordata-sdk 0.3.0__py3-none-any.whl → 0.3.1__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.
- thordata/__init__.py +1 -1
- thordata/client.py +136 -2
- {thordata_sdk-0.3.0.dist-info → thordata_sdk-0.3.1.dist-info}/METADATA +4 -1
- thordata_sdk-0.3.1.dist-info/RECORD +10 -0
- thordata_sdk-0.3.0.dist-info/RECORD +0 -10
- {thordata_sdk-0.3.0.dist-info → thordata_sdk-0.3.1.dist-info}/WHEEL +0 -0
- {thordata_sdk-0.3.0.dist-info → thordata_sdk-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {thordata_sdk-0.3.0.dist-info → thordata_sdk-0.3.1.dist-info}/top_level.txt +0 -0
thordata/__init__.py
CHANGED
thordata/client.py
CHANGED
|
@@ -2,7 +2,7 @@ import requests
|
|
|
2
2
|
import logging
|
|
3
3
|
import json
|
|
4
4
|
import base64
|
|
5
|
-
from typing import Dict, Any, Union, Optional
|
|
5
|
+
from typing import Dict, Any, Union, Optional, List
|
|
6
6
|
|
|
7
7
|
from .enums import Engine
|
|
8
8
|
from .parameters import normalize_serp_params
|
|
@@ -53,6 +53,7 @@ class ThordataClient:
|
|
|
53
53
|
self.base_url = "https://scraperapi.thordata.com"
|
|
54
54
|
self.universal_url = "https://universalapi.thordata.com"
|
|
55
55
|
self.api_url = "https://api.thordata.com/api/web-scraper-api"
|
|
56
|
+
self.locations_url = "https://api.thordata.com/api/locations"
|
|
56
57
|
|
|
57
58
|
self.SERP_API_URL = f"{self.base_url}/request"
|
|
58
59
|
self.UNIVERSAL_API_URL = f"{self.universal_url}/request"
|
|
@@ -349,4 +350,137 @@ class ThordataClient:
|
|
|
349
350
|
raise Exception(f"API returned error: {data}")
|
|
350
351
|
except Exception as e:
|
|
351
352
|
logger.error(f"Get Result Failed: {e}")
|
|
352
|
-
raise
|
|
353
|
+
raise
|
|
354
|
+
|
|
355
|
+
def _get_locations(self, endpoint: str, params: Dict[str, str]) -> List[Dict[str, Any]]:
|
|
356
|
+
"""
|
|
357
|
+
Internal helper to call the public locations API.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
endpoint: One of 'countries', 'states', 'cities', 'asn'.
|
|
361
|
+
params: Query parameters (must include token, key, proxy_type, etc.)
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
List of location records from the 'data' field.
|
|
365
|
+
|
|
366
|
+
Raises:
|
|
367
|
+
RuntimeError: If token/key are missing or API returns an error code.
|
|
368
|
+
"""
|
|
369
|
+
if not self.public_token or not self.public_key:
|
|
370
|
+
raise RuntimeError(
|
|
371
|
+
"Public API token/key are required for locations endpoints. "
|
|
372
|
+
"Please provide 'public_token' and 'public_key' when "
|
|
373
|
+
"initializing ThordataClient."
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
url = f"{self.locations_url}/{endpoint}"
|
|
377
|
+
logger.info("Locations API request: %s", url)
|
|
378
|
+
|
|
379
|
+
# Use a direct requests.get here; no need to go through the proxy gateway.
|
|
380
|
+
response = requests.get(
|
|
381
|
+
url,
|
|
382
|
+
params=params,
|
|
383
|
+
timeout=30,
|
|
384
|
+
)
|
|
385
|
+
response.raise_for_status()
|
|
386
|
+
|
|
387
|
+
data = response.json()
|
|
388
|
+
if isinstance(data, dict):
|
|
389
|
+
code = data.get("code")
|
|
390
|
+
if code is not None and code != 200:
|
|
391
|
+
msg = data.get("msg", "")
|
|
392
|
+
raise RuntimeError(
|
|
393
|
+
f"Locations API error ({endpoint}): code={code}, msg={msg}"
|
|
394
|
+
)
|
|
395
|
+
return data.get("data") or []
|
|
396
|
+
# Fallback: if backend ever returns a list directly
|
|
397
|
+
if isinstance(data, list):
|
|
398
|
+
return data
|
|
399
|
+
return []
|
|
400
|
+
|
|
401
|
+
def list_countries(self, proxy_type: int = 1) -> List[Dict[str, Any]]:
|
|
402
|
+
"""
|
|
403
|
+
List supported countries for Thordata residential or unlimited proxies.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
proxy_type (int): 1 for residential proxies, 2 for unlimited proxies.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
List[Dict[str, Any]]: Each record contains 'country_code' and 'country_name'.
|
|
410
|
+
"""
|
|
411
|
+
params = {
|
|
412
|
+
"token": self.public_token,
|
|
413
|
+
"key": self.public_key,
|
|
414
|
+
"proxy_type": str(proxy_type),
|
|
415
|
+
}
|
|
416
|
+
return self._get_locations("countries", params)
|
|
417
|
+
|
|
418
|
+
def list_states(self, country_code: str, proxy_type: int = 1) -> List[Dict[str, Any]]:
|
|
419
|
+
"""
|
|
420
|
+
List supported states for a given country.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
country_code (str): Country code (e.g., 'US').
|
|
424
|
+
proxy_type (int): 1 for residential proxies, 2 for unlimited proxies.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
List[Dict[str, Any]]: Each record contains 'state_code' and 'state_name'.
|
|
428
|
+
"""
|
|
429
|
+
params = {
|
|
430
|
+
"token": self.public_token,
|
|
431
|
+
"key": self.public_key,
|
|
432
|
+
"proxy_type": str(proxy_type),
|
|
433
|
+
"country_code": country_code,
|
|
434
|
+
}
|
|
435
|
+
return self._get_locations("states", params)
|
|
436
|
+
|
|
437
|
+
def list_cities(
|
|
438
|
+
self,
|
|
439
|
+
country_code: str,
|
|
440
|
+
state_code: Optional[str] = None,
|
|
441
|
+
proxy_type: int = 1,
|
|
442
|
+
) -> List[Dict[str, Any]]:
|
|
443
|
+
"""
|
|
444
|
+
List supported cities for a given country (and optional state).
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
country_code (str): Country code (e.g., 'US').
|
|
448
|
+
state_code (Optional[str]): State code (e.g., 'alabama'), if applicable.
|
|
449
|
+
proxy_type (int): 1 for residential proxies, 2 for unlimited proxies.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
List[Dict[str, Any]]: Each record contains 'city_code' and 'city_name'.
|
|
453
|
+
"""
|
|
454
|
+
params: Dict[str, str] = {
|
|
455
|
+
"token": self.public_token,
|
|
456
|
+
"key": self.public_key,
|
|
457
|
+
"proxy_type": str(proxy_type),
|
|
458
|
+
"country_code": country_code,
|
|
459
|
+
}
|
|
460
|
+
if state_code:
|
|
461
|
+
params["state_code"] = state_code
|
|
462
|
+
|
|
463
|
+
return self._get_locations("cities", params)
|
|
464
|
+
|
|
465
|
+
def list_asn(
|
|
466
|
+
self,
|
|
467
|
+
country_code: str,
|
|
468
|
+
proxy_type: int = 1,
|
|
469
|
+
) -> List[Dict[str, Any]]:
|
|
470
|
+
"""
|
|
471
|
+
List supported ASNs for a given country.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
country_code (str): Country code (e.g., 'US').
|
|
475
|
+
proxy_type (int): 1 for residential proxies, 2 for unlimited proxies.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
List[Dict[str, Any]]: Each record contains 'asn_code' and 'asn_name'.
|
|
479
|
+
"""
|
|
480
|
+
params = {
|
|
481
|
+
"token": self.public_token,
|
|
482
|
+
"key": self.public_key,
|
|
483
|
+
"proxy_type": str(proxy_type),
|
|
484
|
+
"country_code": country_code,
|
|
485
|
+
}
|
|
486
|
+
return self._get_locations("asn", params)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thordata-sdk
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: The Official Python SDK for Thordata - AI Data Infrastructure & Proxy Network.
|
|
5
5
|
Author-email: Thordata Developer Team <support@thordata.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -36,6 +36,9 @@ Dynamic: license-file
|
|
|
36
36
|
</h4>
|
|
37
37
|
|
|
38
38
|
<p align="center">
|
|
39
|
+
<a href="https://github.com/Thordata/thordata-python-sdk/actions/workflows/ci.yml">
|
|
40
|
+
<img src="https://github.com/Thordata/thordata-python-sdk/actions/workflows/ci.yml/badge.svg" alt="CI">
|
|
41
|
+
</a>
|
|
39
42
|
<a href="https://pypi.org/project/thordata-sdk/">
|
|
40
43
|
<img src="https://img.shields.io/pypi/v/thordata-sdk?color=blue" alt="PyPI version">
|
|
41
44
|
</a>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
thordata/__init__.py,sha256=iv2luaDxUmcWTqScu08gGJfocUZr6pSwtzJs2akZ1Gg,365
|
|
2
|
+
thordata/async_client.py,sha256=cpBtRIzr8oH6GuZs8gTh505tGYYV1aRFBUzbtmFOfEg,9717
|
|
3
|
+
thordata/client.py,sha256=WVIvIZTACEpw9NaTbGtIkMGUlfliFL7kNGdCoTxxsUI,17193
|
|
4
|
+
thordata/enums.py,sha256=PGUCQX3jw5a9mX8_JfhuyoR1WriWjWQpAgibVP_bpdM,679
|
|
5
|
+
thordata/parameters.py,sha256=1lNx_BSS8ztBKEj_MXZMaIQQ9_W3EAlS-VFiBqSWb9E,1841
|
|
6
|
+
thordata_sdk-0.3.1.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
7
|
+
thordata_sdk-0.3.1.dist-info/METADATA,sha256=NMq7Be240zn2q3MlUUg2Dmo4NFoQtDMgkRAGzjg_yjc,5901
|
|
8
|
+
thordata_sdk-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
thordata_sdk-0.3.1.dist-info/top_level.txt,sha256=Z8R_07m0lXCCSb1hapL9_nxMtyO3rf_9wOvq4n9u2Hg,9
|
|
10
|
+
thordata_sdk-0.3.1.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
thordata/__init__.py,sha256=HVb6cHBsYRFoA1Sf_y_WSZ88vGV3DsT67rCdbZSuUYE,365
|
|
2
|
-
thordata/async_client.py,sha256=cpBtRIzr8oH6GuZs8gTh505tGYYV1aRFBUzbtmFOfEg,9717
|
|
3
|
-
thordata/client.py,sha256=w_EXs6CLM2qFtFPNU-x_Li66LEH1j7pQb2ca2MDKqyA,12432
|
|
4
|
-
thordata/enums.py,sha256=PGUCQX3jw5a9mX8_JfhuyoR1WriWjWQpAgibVP_bpdM,679
|
|
5
|
-
thordata/parameters.py,sha256=1lNx_BSS8ztBKEj_MXZMaIQQ9_W3EAlS-VFiBqSWb9E,1841
|
|
6
|
-
thordata_sdk-0.3.0.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
7
|
-
thordata_sdk-0.3.0.dist-info/METADATA,sha256=Yj6W3vSLkkUhSXTj6AK4AaMfdlJvGOVaK6cFI2MNqV8,5697
|
|
8
|
-
thordata_sdk-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
thordata_sdk-0.3.0.dist-info/top_level.txt,sha256=Z8R_07m0lXCCSb1hapL9_nxMtyO3rf_9wOvq4n9u2Hg,9
|
|
10
|
-
thordata_sdk-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|