swiftshadow 1.2.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
swiftshadow/__init__.py CHANGED
@@ -1,25 +1,30 @@
1
+ from typing import Literal
1
2
  from swiftshadow.providers import Providers
3
+ from swiftshadow.models import Proxy
4
+ from asyncio import run
2
5
 
3
6
 
4
- def QuickProxy(countries: list = [], protocol: str = "http"):
7
+ def QuickProxy(
8
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
9
+ ) -> Proxy | None:
5
10
  """
6
- This function is a faster alternative to `Proxy` class.
11
+ This function is a faster alternative to `ProxyInterface` class.
7
12
  No caching is done.
8
13
 
9
14
  Args:
10
- countries: ISO 3166-2 Two letter country codes to filter proxies.
11
- protocol: HTTP/HTTPS protocol to filter proxies.
15
+ countries: ISO 3166-2 Two letter country codes to filter proxies.
16
+ protocol: HTTP/HTTPS protocol to filter proxies.
12
17
 
13
18
  Returns:
14
- proxyObject (dict): A working proxy object.
19
+ proxyObject (Proxy): A working proxy object if found or else None.
15
20
  """
16
- for providerDict in Providers:
17
- if protocol not in providerDict["protocols"]:
21
+ for provider in Providers:
22
+ if protocol not in provider.protocols:
18
23
  continue
19
- if (len(countries) != 0) and (not providerDict["countryFilter"]):
24
+ if (len(countries) != 0) and (not provider.countryFilter):
20
25
  continue
21
26
  try:
22
- return providerDict["provider"](1, countries, protocol)[0]
23
- except:
27
+ return run(provider.providerFunction(countries, protocol))[0]
28
+ except Exception:
24
29
  continue
25
30
  return None
swiftshadow/cache.py CHANGED
@@ -1,16 +1,33 @@
1
1
  from datetime import datetime, timezone, timedelta
2
2
 
3
3
 
4
- def getExpiry(expiryIn):
4
+ def getExpiry(timeToLive: int) -> datetime:
5
+ """
6
+ Fetch a datetime object representing the cache expiry based on TTL.
7
+
8
+ Args:
9
+ timeToLive(int): Time in minutes the cache should expire.
10
+
11
+ Returns:
12
+ expiry(datetime): Datetime object representing the expiry time.
13
+ """
5
14
  now = datetime.now(timezone.utc)
6
- expiry = now + timedelta(minutes=expiryIn)
15
+ expiry = now + timedelta(minutes=timeToLive)
7
16
  return expiry
8
17
 
9
18
 
10
- def checkExpiry(expiryDateString):
19
+ def checkExpiry(expiryDate: datetime) -> bool:
20
+ """
21
+ Check if a expiry date object has expired.
22
+
23
+ Args:
24
+ expiryDate(datetime): Expiry date object.
25
+
26
+ Returns:
27
+ expiry(bool): Expire Status.
28
+ """
11
29
  now = datetime.now(timezone.utc)
12
- expiryDateObject = datetime.fromisoformat(expiryDateString)
13
- if (now - expiryDateObject).days < 0:
30
+ if (now - expiryDate).days < 0:
14
31
  return False
15
32
  else:
16
33
  return True
swiftshadow/classes.py CHANGED
@@ -1,145 +1,213 @@
1
- from requests import get
1
+ from datetime import datetime
2
2
  from random import choice
3
- from json import dump, load
4
- from swiftshadow.helpers import log
3
+ from typing import Literal
4
+ from pathlib import Path
5
+ from appdirs import user_cache_dir
6
+ from logging import FileHandler, getLogger, Formatter, StreamHandler, INFO, DEBUG
7
+ from sys import stdout
8
+ from pickle import load, dump
9
+ from swiftshadow.cache import checkExpiry, getExpiry
10
+ from swiftshadow.models import CacheData, Proxy as Proxy
11
+
12
+ from swiftshadow.exceptions import UnsupportedProxyProtocol
5
13
  from swiftshadow.providers import Providers
6
- import swiftshadow.cache as cache
7
- import logging
8
- import sys
9
-
10
- logger = logging.getLogger("swiftshadow")
11
- logger.setLevel(logging.INFO)
12
- logFormat = logging.Formatter("%(asctime)s - %(name)s [%(levelname)s]:%(message)s")
13
- streamhandler = logging.StreamHandler(stream=sys.stdout)
14
+ from asyncio import run
15
+
16
+ logger = getLogger("swiftshadow")
17
+ logger.setLevel(INFO)
18
+ logFormat = Formatter("%(asctime)s - %(name)s [%(levelname)s]:%(message)s")
19
+ streamhandler = StreamHandler(stream=stdout)
14
20
  streamhandler.setFormatter(logFormat)
15
21
  logger.addHandler(streamhandler)
16
22
 
17
23
 
18
- class Proxy:
24
+ class ProxyInterface:
25
+ """Manages proxy acquisition, caching, and rotation from various providers.
26
+
27
+ This class handles proxy retrieval either through fresh fetching from registered providers
28
+ or via cached data. It supports protocol filtering, country filtering, cache management,
29
+ and automatic/manual proxy rotation.
30
+
31
+ Attributes:
32
+ countries (list[str]): List of ISO country codes to filter proxies by (e.g., ["US", "CA"]).
33
+ protocol (Literal['https', 'http']): Proxy protocol to use. Defaults to 'http'.
34
+ maxproxies (int): Maximum number of proxies to collect from providers. Defaults to 10.
35
+ autorotate (bool): Whether to automatically rotate proxy on each get() call. Defaults to False.
36
+ cachePeriod (int): Number of minutes before cache is considered expired. Defaults to 10.
37
+ cacheFolderPath (Path): Filesystem path for cache storage. Uses system cache dir by default.
38
+ proxies (list[Proxy]): List of available proxy objects.
39
+ current (Proxy | None): Currently active proxy. None if no proxies available.
40
+ cacheExpiry (datetime | None): Timestamp when cache expires. None if no cache exists.
41
+
42
+ Example:
43
+ ```python
44
+ proxy_manager = ProxyInterface(
45
+ countries=["US"],
46
+ protocol="http",
47
+ autoRotate=True
48
+ )
49
+ print(proxy_manager.get())
50
+ ```
51
+
52
+ Raises:
53
+ UnsupportedProxyProtocol: If invalid protocol is specified during initialization.
54
+ ValueError: If no proxies match filters during update().
55
+ """
56
+
19
57
  def __init__(
20
58
  self,
21
- countries: list = [],
22
- protocol: str = "http",
59
+ countries: list[str] = [],
60
+ protocol: Literal["https", "http"] = "http",
23
61
  maxProxies: int = 10,
24
62
  autoRotate: bool = False,
25
63
  cachePeriod: int = 10,
26
- cacheFolder: str = "",
64
+ cacheFolderPath: Path | None = None,
27
65
  debug: bool = False,
28
66
  logToFile: bool = False,
29
67
  ):
68
+ """Initializes ProxyInterface with specified configuration.
69
+
70
+ Args:
71
+ countries: List of ISO country codes to filter proxies. Empty list = no filtering.
72
+ protocol: Proxy protocol to retrieve. Choose between 'http' or 'https'.
73
+ maxProxies: Maximum proxies to collect from all providers combined.
74
+ autoRotate: Enable automatic proxy rotation on every get() call.
75
+ cachePeriod: Cache validity duration in minutes.
76
+ cacheFolderPath: Custom path for cache storage. Uses system cache dir if None.
77
+ debug: Enable debug logging level when True.
78
+ logToFile: Write logs to swiftshadow.log in cache folder when True.
30
79
  """
31
- The one class for everything.
32
80
 
33
- Proxy class contains all necessary methods required to use swiftshadow.
81
+ self.countries: list[str] = [i.upper() for i in countries]
34
82
 
35
- Args:
36
- countries: ISO 3166-2 Two letter country codes to filter proxies.
37
- protocol: HTTP/HTTPS protocol to filter proxies.
38
- maxProxies: Maximum number of proxies to store and rotate from.
39
- autoRotate: Rotates proxy when `Proxy.proxy()` function is called.
40
- cachePeriod: Time to cache proxies in minutes.
41
- cacheFolder: Folder to store cache file.
42
- debug: Sets Log Level to Debug.
43
- logToFile: Whether to pipe log to a log file. If cacheFolder is set log file is saved there.
83
+ if protocol not in ["https", "http"]:
84
+ raise UnsupportedProxyProtocol(
85
+ f"Protocol {protocol} is not supported by swiftshadow, please choose between HTTP or HTTPS"
86
+ )
87
+ self.protocol: Literal["https", "http"] = protocol
88
+
89
+ self.maxproxies: int = maxProxies
90
+ self.autorotate: bool = autoRotate
91
+ self.cachePeriod: int = cachePeriod
44
92
 
45
- Returns:
46
- proxyClass (swiftshadow.Proxy): `swiftshadow.Proxy` class instance
47
-
48
- Examples:
49
- Simplest way to get a proxy
50
- >>> from swiftshadow.swiftshadow import Proxy
51
- >>> swift = Proxy()
52
- >>> print(swift.proxy())
53
- {'http':'192.0.0.1:8080'}
54
- Note:
55
- Proxies are sourced from **Proxyscrape** and **Scrapingant** websites which are freely available and validated locally.
56
- """
57
- self.countries = [i.upper() for i in countries]
58
- self.protocol = protocol
59
- self.maxProxies = maxProxies
60
- self.autoRotate = autoRotate
61
- self.cachePeriod = cachePeriod
62
- if cacheFolder == "":
63
- self.cacheFilePath = ".swiftshadow.json"
64
- else:
65
- self.cacheFilePath = f"{cacheFolder}/.swiftshadow.json"
66
93
  if debug:
67
- logger.setLevel(logging.DEBUG)
94
+ logger.setLevel(DEBUG)
95
+
96
+ if not cacheFolderPath:
97
+ cacheFolderPath = Path(user_cache_dir(appname="swiftshadow"))
98
+ cacheFolderPath.mkdir(parents=True, exist_ok=True)
99
+ logger.debug(f"System Cache folder set at {cacheFolderPath}")
100
+ self.cacheFolderPath: Path = cacheFolderPath
101
+
68
102
  if logToFile:
69
- if cacheFolder == "":
70
- logFilePath = "swiftshadow.log"
71
- else:
72
- logFilePath = f"{cacheFolder}/swiftshadow.log"
73
- fileHandler = logging.FileHandler(logFilePath)
74
- fileHandler.setFormatter(logFormat)
75
- logger.addHandler(fileHandler)
103
+ logFileHandler = FileHandler(
104
+ self.cacheFolderPath.joinpath("swiftshadow.log")
105
+ )
106
+ logFileHandler.setFormatter(logFormat)
107
+ logger.addHandler(logFileHandler)
108
+ self.proxies: list[Proxy] = []
109
+ self.current: Proxy | None = None
110
+ self.cacheExpiry: datetime | None = None
111
+
76
112
  self.update()
77
113
 
78
114
  def update(self):
115
+ """
116
+ Updates proxy list from providers or cache.
117
+
118
+ First attempts to load valid proxies from cache. If cache is expired/missing,
119
+ fetches fresh proxies from registered providers that match country and protocol filters.
120
+ Updates cache file with new proxies if fetched from providers.
121
+
122
+ Raises:
123
+ ValueError: If no proxies found after provider scraping.
124
+ """
79
125
  try:
80
- with open(self.cacheFilePath, "r") as file:
81
- data = load(file)
82
- self.expiry = data[0]
83
- expired = cache.checkExpiry(self.expiry)
84
- if not expired:
85
- logger.info(
86
- "Loaded proxies from cache",
87
- )
88
- self.proxies = data[1]
89
- self.expiry = data[0]
90
- self.current = self.proxies[0]
91
- return
92
- else:
93
- logger.info(
94
- "Cache expired. Updating cache.",
95
- )
126
+ with open(
127
+ self.cacheFolderPath.joinpath("swiftshadow.pickle"), "rb"
128
+ ) as cacheFile:
129
+ cache: CacheData = load(cacheFile)
130
+
131
+ if not checkExpiry(cache.expiryIn):
132
+ self.proxies = cache.proxies
133
+ logger.info("Loaded proxies from cache.")
134
+ logger.debug(
135
+ f"Cache with {len(cache.proxies)} proxies, expire in {cache.expiryIn}"
136
+ )
137
+ self.current = self.proxies[0]
138
+ self.cacheExpiry = cache.expiryIn
139
+ return
140
+ else:
141
+ logger.info("Cache Expired")
96
142
  except FileNotFoundError:
97
- logger.info("No cache found. Cache will be created after update")
143
+ logger.info("No cache found, will be created after update.")
98
144
 
99
- self.proxies = []
100
- for providerDict in Providers:
101
- if self.protocol not in providerDict["protocols"]:
145
+ for provider in Providers:
146
+ if self.protocol not in provider.protocols:
102
147
  continue
103
- if (len(self.countries) != 0) and (not providerDict["countryFilter"]):
148
+ if (len(self.countries) != 0) and (not provider.countryFilter):
104
149
  continue
105
- self.proxies.extend(
106
- providerDict["provider"](self.maxProxies, self.countries, self.protocol)
150
+ providerProxies: list[Proxy] = run(
151
+ provider.providerFunction(self.countries, self.protocol)
107
152
  )
108
- if len(self.proxies) >= self.maxProxies:
153
+ logger.debug(
154
+ f"{len(providerProxies)} proxies from {provider.providerFunction.__name__}"
155
+ )
156
+ self.proxies.extend(providerProxies)
157
+
158
+ if len(self.proxies) >= self.maxproxies:
109
159
  break
160
+
110
161
  if len(self.proxies) == 0:
111
- logger.warning(
112
- "No proxies found for current settings. To prevent runtime error updating the proxy list again.",
113
- )
114
- self.update()
115
- with open(self.cacheFilePath, "w") as file:
116
- self.expiry = cache.getExpiry(self.cachePeriod).isoformat()
117
- dump([self.expiry, self.proxies], file)
162
+ raise ValueError("No proxies where found for the current filter settings.")
163
+
164
+ with open(
165
+ self.cacheFolderPath.joinpath("swiftshadow.pickle"), "wb+"
166
+ ) as cacheFile:
167
+ cacheExpiry = getExpiry(self.cachePeriod)
168
+ self.cacheExpiry = cacheExpiry
169
+ cache = CacheData(cacheExpiry, self.proxies)
170
+ dump(cache, cacheFile)
118
171
  self.current = self.proxies[0]
119
172
 
120
- def rotate(self):
173
+ def rotate(self, validate_cache: bool = False):
121
174
  """
122
- Rotate the current proxy.
175
+ Rotates to a random proxy from available proxies.
123
176
 
124
- Sets the current proxy to a random one from available proxies and also validates cache.
177
+ Args:
178
+ validate_cache: Force cache validation before rotation when True.
125
179
 
126
180
  Note:
127
- Function only for manual rotation. If `autoRotate` is set to `True` then no need to call this function.
181
+ Only required for manual rotation when autoRotate=False. Automatic rotation
182
+ occurs during get() calls when autoRotate=True.
183
+
184
+ Raises:
185
+ ValueError: If validate_cache=True but no cache exists.
128
186
  """
129
- if cache.checkExpiry(self.expiry):
130
- self.update()
187
+ if self.cacheExpiry and not validate_cache:
188
+ if checkExpiry(self.cacheExpiry):
189
+ self.update()
190
+ else:
191
+ raise ValueError("No cache availabel but validate_cache is true.")
131
192
  self.current = choice(self.proxies)
132
193
 
133
- def proxy(self):
194
+ def get(self) -> Proxy:
134
195
  """
135
- Returns a proxy dict.
196
+ Retrieves current active proxy.
136
197
 
137
198
  Returns:
138
- proxyDict (dict):A proxy dict of format `{protocol:address}`
199
+ Proxy: Current proxy object with connection details.
200
+
201
+ Note:
202
+ Performs automatic rotation if autorotate=True before returning proxy.
203
+
204
+ Raises:
205
+ ValueError: If no proxies are available (current is None).
139
206
  """
140
- if cache.checkExpiry(self.expiry):
141
- self.update()
142
- if self.autoRotate == True:
143
- return choice(self.proxies)
144
- else:
207
+
208
+ if self.autorotate:
209
+ self.rotate()
210
+ if self.current:
145
211
  return self.current
212
+ else:
213
+ raise ValueError("No proxy available in current, current is None")
@@ -0,0 +1,4 @@
1
+ class UnsupportedProxyProtocol(Exception):
2
+ """Exception raised when a unsupported proxy protocol is intialised."""
3
+
4
+ pass
swiftshadow/helpers.py CHANGED
@@ -1,5 +1,8 @@
1
- from requests import get
2
1
  from datetime import datetime
2
+ from typing import Literal
3
+
4
+ from requests import get
5
+ from swiftshadow.models import Proxy
3
6
 
4
7
 
5
8
  def checkProxy(proxy):
@@ -11,12 +14,24 @@ def checkProxy(proxy):
11
14
  if resp.count(".") == 3:
12
15
  return True
13
16
  return False
14
- except Exception as e:
17
+ except Exception:
15
18
  return False
16
19
 
17
20
 
18
21
  def log(level, message):
19
22
  level = level.upper()
20
23
  print(
21
- f'{datetime.now().strftime("%d/%m/%Y %H:%M:%S")} - [swiftshadow] - {level} : {message}'
24
+ f"{datetime.now().strftime('%d/%m/%Y %H:%M:%S')} - [swiftshadow] - {level} : {message}"
22
25
  )
26
+
27
+
28
+ def plaintextToProxies(text: str, protocol: Literal["http", "https"]) -> list[Proxy]:
29
+ proxies: list[Proxy] = []
30
+ for line in text.splitlines():
31
+ try:
32
+ ip, port = line.split(":")
33
+ except ValueError:
34
+ continue
35
+ proxy = Proxy(ip=ip, port=int(port), protocol=protocol)
36
+ proxies.append(proxy)
37
+ return proxies
swiftshadow/models.py ADDED
@@ -0,0 +1,71 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from typing import Any, Callable, Coroutine, Literal
4
+
5
+
6
+ @dataclass
7
+ class Proxy:
8
+ """
9
+ Class representing a Proxy object.
10
+
11
+ Attributes:
12
+ ip: IP Address of the proxy.
13
+ port: Port associated with the proxy.
14
+ protocol: Protocol type of the proxy.
15
+
16
+ """
17
+
18
+ ip: str
19
+ protocol: Literal["http", "https"]
20
+ port: int
21
+
22
+ def as_requests_dict(self) -> dict[Literal["http", "https"], str]:
23
+ """
24
+ Return proxy in requests commpatible dict format.
25
+
26
+ Returns:
27
+ dict: Dict representation of Proxy class.
28
+ """
29
+ return {self.protocol: f"{self.ip}:{self.port}"}
30
+
31
+ def as_string(self) -> str:
32
+ """
33
+ Return proxy in a string of format
34
+ <protocol>://<ip>:<port>
35
+
36
+ Returns:
37
+ string: Proxy in string format.
38
+ """
39
+ return f"{self.protocol}://{self.ip}:{self.port}"
40
+
41
+
42
+ @dataclass
43
+ class CacheData:
44
+ """
45
+ Class repersenting data structure if the cache in cache file.
46
+
47
+ Attributes:
48
+ expiryIn: Expiry date object.
49
+ proxies: Proxies to head.
50
+ """
51
+
52
+ expiryIn: datetime
53
+ proxies: list[Proxy]
54
+
55
+
56
+ @dataclass
57
+ class Provider:
58
+ """
59
+ Class repersenting a Provider.
60
+
61
+ Attributes:
62
+ providerFunction: Callable function for this provider.
63
+ countryFilter: Whether the provider supports country based filters.
64
+ protocols: Protocols supported by the provider.
65
+ """
66
+
67
+ providerFunction: Callable[
68
+ [list[str], Literal["http", "https"]], Coroutine[Any, Any, list[Proxy]]
69
+ ]
70
+ countryFilter: bool
71
+ protocols: list[Literal["http", "https"]]
swiftshadow/providers.py CHANGED
@@ -1,57 +1,162 @@
1
+ from typing import Literal
2
+
1
3
  from requests import get
2
- from swiftshadow.helpers import checkProxy
3
-
4
- def Monosans(max, countries=[],protocol="http"):
5
- raw = get('https://raw.githubusercontent.com/monosans/proxy-list/main/proxies.json').json()
6
- results = []
7
- count = 0
8
- for proxy in raw:
9
- if count == max:
10
- return results
11
- if proxy['protocol'] == protocol:
12
- if len(countries) != 0 and proxy['geolocation']['country']['iso_code'] not in countries:
13
- continue
14
- proxy = [f'{proxy['host']}:{proxy['port']}',proxy['protocol']]
15
- if checkProxy(proxy):
16
- results.append(proxy)
17
- count += 1
18
- return results
19
-
20
- def Thespeedx(max,countries=[],protocol='http'):
21
- results = []
22
- count =0
23
- raw = get('https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt').text
24
- for line in raw.splitlines():
25
- if count == max:
26
- break
27
- proxy = [line,'http']
28
- if checkProxy(proxy):
29
- results.append(proxy)
30
- print(proxy,True)
31
- count +=1
32
- else:
4
+
5
+ from swiftshadow.helpers import plaintextToProxies
6
+ from swiftshadow.models import Proxy, Provider
7
+ from swiftshadow.types import MonosansProxyDict
8
+ from swiftshadow.validator import validate_proxies
9
+
10
+
11
+ async def Monosans(
12
+ countries: list[str] = [],
13
+ protocol: Literal["http", "https"] = "http",
14
+ ) -> list[Proxy]:
15
+ response = get(
16
+ "https://raw.githubusercontent.com/monosans/proxy-list/main/proxies.json"
17
+ )
18
+ proxy_dicts: list[MonosansProxyDict] = response.json()
19
+ proxies_to_validate: list[Proxy] = []
20
+ for proxy_dict in proxy_dicts:
21
+ if proxy_dict["protocol"] != protocol:
33
22
  continue
23
+ if (
24
+ len(countries) != 0
25
+ and proxy_dict["geolocation"]["country"]["iso_code"] not in countries
26
+ ):
27
+ continue
28
+ proxy = Proxy(
29
+ ip=proxy_dict["host"],
30
+ port=proxy_dict["port"],
31
+ protocol=proxy_dict["protocol"],
32
+ )
33
+ proxies_to_validate.append(proxy)
34
+ result = await validate_proxies(proxies_to_validate)
35
+ return result
36
+
37
+
38
+ async def Thespeedx(
39
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
40
+ ):
41
+ raw: str = get(
42
+ "https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt"
43
+ ).text
44
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol="http")
45
+ results = await validate_proxies(proxies)
34
46
  return results
35
-
36
- def ProxyScrape(max,countries=[],protocol='http'):
37
- baseUrl = 'https://api.proxyscrape.com/v3/free-proxy-list/get?request=displayproxies&protocol=http&proxy_format=ipport&format=json'
38
- results = []
39
- count = 0
47
+
48
+
49
+ async def ProxyScrape(
50
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
51
+ ):
52
+ baseUrl = "https://api.proxyscrape.com/v3/free-proxy-list/get?request=displayproxies&protocol=http&proxy_format=ipport&format=json"
53
+ proxies: list[Proxy] = []
40
54
  if len(countries) == 0:
41
- apiUrl = baseUrl + '&country=all'
55
+ apiUrl = baseUrl + "&country=all"
42
56
  else:
43
- apiUrl = baseUrl + '&country=' + ','.join([i.upper() for i in countries])
57
+ apiUrl = baseUrl + "&country=" + ",".join([i.upper() for i in countries])
44
58
  raw = get(apiUrl).json()
45
- for ipRaw in raw['proxies']:
46
- if count == max:
47
- break
48
- proxy = [ipRaw['proxy'],'http']
49
- if checkProxy(proxy):
50
- results.append(proxy)
51
- count += 1
52
- else:
53
- print(proxy,False)
59
+ for ipRaw in raw["proxies"]:
60
+ proxy = Proxy(protocol="http", ip=ipRaw["ip"], port=ipRaw["port"])
61
+ proxies.append(proxy)
62
+ results = await validate_proxies(proxies)
63
+ return results
64
+
65
+
66
+ async def GoodProxy(
67
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
68
+ ):
69
+ baseUrl = "https://raw.githubusercontent.com/yuceltoluyag/GoodProxy/refs/heads/main/GoodProxy.txt"
70
+ proxies: list[Proxy] = []
71
+ raw = get(baseUrl).text
72
+
73
+ for line in raw.splitlines():
74
+ if line == "":
54
75
  continue
55
- return results
76
+ line = line.split("|")[0].split(":")
77
+ proxy = Proxy(ip=line[0], port=int(line[1]), protocol="http")
78
+ proxies.append(proxy)
79
+ results = await validate_proxies(proxies)
80
+ return results
81
+
82
+
83
+ async def OpenProxyList(
84
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
85
+ ):
86
+ raw = get("https://api.openproxylist.xyz/http.txt").text
87
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol="http")
88
+ results = await validate_proxies(proxies)
89
+ return results
90
+
91
+
92
+ async def MuRongPIG(
93
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
94
+ ):
95
+ raw = get(
96
+ "https://raw.githubusercontent.com/MuRongPIG/Proxy-Master/refs/heads/main/http_checked.txt"
97
+ ).text
98
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol="http")
99
+ results = await validate_proxies(proxies)
100
+ return results
101
+
102
+
103
+ async def KangProxy(
104
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
105
+ ):
106
+ raw = get(
107
+ f"https://github.com/officialputuid/KangProxy/raw/refs/heads/KangProxy/{protocol}/{protocol}.txt"
108
+ ).text
109
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol=protocol)
110
+ results = await validate_proxies(proxies)
111
+ return results
112
+
113
+
114
+ async def Mmpx12(
115
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
116
+ ):
117
+ raw = get(
118
+ f"https://github.com/mmpx12/proxy-list/raw/refs/heads/master/{protocol}.txt"
119
+ ).text
120
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol=protocol)
121
+ results = await validate_proxies(proxies)
122
+ return results
123
+
124
+
125
+ async def Anonym0usWork1221(
126
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
127
+ ):
128
+ raw = get(
129
+ f"https://github.com/Anonym0usWork1221/Free-Proxies/raw/refs/heads/main/proxy_files/{protocol}_proxies.txt"
130
+ ).text
131
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol=protocol)
132
+ results = await validate_proxies(proxies)
133
+ return results
134
+
135
+
136
+ async def ProxySpace(
137
+ countries: list[str] = [], protocol: Literal["http", "https"] = "http"
138
+ ):
139
+ raw = get("https://proxyspace.pro/http.txt").text
140
+ proxies: list[Proxy] = plaintextToProxies(raw, protocol="http")
141
+ results = await validate_proxies(proxies)
142
+ return results
143
+
56
144
 
57
- Providers = [{'provider':Monosans,'countryFilter':True,'protocols':['http']},{'provider':Thespeedx,'countryFilter':False,'protocols':['http']},{'provider':ProxyScrape,'countryFilter':True,'protocols':['http']}]
145
+ Providers: list[Provider] = [
146
+ Provider(providerFunction=ProxyScrape, countryFilter=True, protocols=["http"]),
147
+ Provider(providerFunction=Monosans, countryFilter=True, protocols=["http"]),
148
+ Provider(providerFunction=MuRongPIG, countryFilter=False, protocols=["http"]),
149
+ Provider(providerFunction=Thespeedx, countryFilter=False, protocols=["http"]),
150
+ Provider(
151
+ providerFunction=Anonym0usWork1221,
152
+ countryFilter=False,
153
+ protocols=["http", "https"],
154
+ ),
155
+ Provider(providerFunction=Mmpx12, countryFilter=False, protocols=["http", "https"]),
156
+ Provider(providerFunction=GoodProxy, countryFilter=False, protocols=["http"]),
157
+ Provider(
158
+ providerFunction=KangProxy, countryFilter=False, protocols=["http", "https"]
159
+ ),
160
+ Provider(providerFunction=ProxySpace, countryFilter=False, protocols=["http"]),
161
+ Provider(providerFunction=OpenProxyList, countryFilter=False, protocols=["http"]),
162
+ ]
swiftshadow/types.py ADDED
@@ -0,0 +1,50 @@
1
+ from typing import TypedDict
2
+
3
+
4
+ class MonosansNames(TypedDict):
5
+ de: str
6
+ en: str
7
+ es: str
8
+ fr: str
9
+ ja: str
10
+ pt_BR: str
11
+ ru: str
12
+ zh_CN: str
13
+
14
+
15
+ class MonosansContinent(TypedDict):
16
+ code: str
17
+ geoname_id: int
18
+ names: MonosansNames
19
+
20
+
21
+ class MonosansCountry(TypedDict):
22
+ geoname_id: int
23
+ is_in_european_union: bool
24
+ iso_code: str
25
+ names: MonosansNames
26
+
27
+
28
+ class MonosansLocation(TypedDict):
29
+ accuracy_radius: int
30
+ latitude: float
31
+ longitude: float
32
+ time_zone: str
33
+
34
+
35
+ class MonosansGeolocation(TypedDict):
36
+ continent: MonosansContinent
37
+ country: MonosansCountry
38
+ location: MonosansLocation
39
+ registered_country: MonosansCountry
40
+
41
+
42
+ class MonosansProxyDict(TypedDict):
43
+ protocol: str
44
+ username: str | None
45
+ password: str | None
46
+ host: str
47
+ port: int
48
+ exit_ip: str
49
+ timeout: float
50
+ geolocation: MonosansGeolocation
@@ -0,0 +1,50 @@
1
+ import asyncio
2
+
3
+ import aiohttp
4
+
5
+ from swiftshadow.models import Proxy
6
+
7
+
8
+ async def check_proxy(async_session: aiohttp.ClientSession, proxy: Proxy) -> str:
9
+ """
10
+ Check one proxy abject.
11
+
12
+ Args:
13
+ async_session: aiohttp client session object
14
+ proxy: Proxy Object
15
+
16
+ Returns:
17
+ text: API response text
18
+ """
19
+ async with async_session.get(
20
+ url=f"{proxy.protocol}://checkip.amazonaws.com",
21
+ proxy=proxy.as_string(),
22
+ timeout=4,
23
+ ) as response:
24
+ text = await response.text()
25
+ return text
26
+
27
+
28
+ async def validate_proxies(proxies: list[Proxy]) -> list[Proxy]:
29
+ """
30
+ Validate all proxies asynchronously.
31
+
32
+ Args:
33
+ proxies: List of Proxy objects
34
+
35
+ Returns:
36
+ working_proxies: List of working Proxies
37
+ """
38
+ working_proxies: list[Proxy] = []
39
+ async with aiohttp.ClientSession() as async_session:
40
+ tasks = []
41
+ for proxy in proxies:
42
+ task = asyncio.create_task(coro=check_proxy(async_session, proxy))
43
+ tasks.append(task)
44
+ results = await asyncio.gather(*tasks, return_exceptions=True)
45
+ for proxy, result in zip(proxies, results):
46
+ if type(result) is not str:
47
+ continue
48
+ if result.count(".") == 3:
49
+ working_proxies.append(proxy)
50
+ return working_proxies
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: swiftshadow
3
+ Version: 2.0.0
4
+ Summary: Free IP Proxy rotator for python
5
+ Author-email: sachin-sankar <mail.sachinsankar@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: aiohttp>=3.11.11
9
+ Requires-Dist: appdirs>=1.4.4
10
+ Requires-Dist: requests>=2.32.3
11
+ Description-Content-Type: text/markdown
12
+
13
+ # Swiftshadow
14
+
15
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/swiftshadow)
16
+ ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/sachin-sankar/swiftshadow?include_prereleases&style=flat)
17
+
18
+ > [!WARNING]
19
+ > **Heads up!** If you're using **versions 1.2.1 or below**, please note that **version 2.0.0 and above** includes breaking changes. Before upgrading, read the [documentation](https://sachin-sankar.github.io/swiftshadow/) to understand the changes and ensure compatibility with your code. If you encounter issues, please review the docs before opening a GitHub issue.
20
+
21
+ ## About
22
+
23
+ **Swiftshadow** is a lightweight and efficient Python library designed to simplify IP proxy rotation for web scraping, data mining, and other automated tasks. It helps you bypass common challenges like IP bans, rate limits, and detection mechanisms, ensuring smooth and uninterrupted data collection.
24
+
25
+ ### Key Features
26
+ - **Speed**: Optimized for fast proxy retrieval and rotation.
27
+ - **Reliability**: Automatically switches to working proxies if one fails.
28
+ - **Customization**: Configure proxy filters, rotation frequency, and caching behavior.
29
+ - **Low Dependencies**: Only one third-party dependency (`requests`), making it easy to use and maintain.
30
+ - **Caching**: Built-in caching mechanism to reduce load times and improve performance.
31
+
32
+ Whether you're a seasoned developer or a beginner, **Swiftshadow** makes proxy management effortless.
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ Install the library using pip:
39
+
40
+ ```bash
41
+ pip install swiftshadow
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ### Get a Proxy in 2 Lines
49
+ Fetch a random proxy with just two lines of code:
50
+
51
+ ```python
52
+ from swiftshadow import QuickProxy
53
+
54
+ print(QuickProxy()) # Output: http://<ip>:<port>
55
+ ```
56
+
57
+ ### Advanced Usage
58
+ For more control, use the `ProxyInterface` class:
59
+
60
+ ```python
61
+ from swiftshadow.classes import ProxyInterface
62
+
63
+ # Fetch HTTPS proxies from the US
64
+ swift = ProxyInterface(countries=["US"], protocol="https")
65
+ print(swift.get().as_string()) # Output: https://<ip>:<port>
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Documentation
71
+
72
+ Explore the full documentation to learn more about **Swiftshadow**'s features and advanced usage:
73
+
74
+ 📚 [Documentation](https://sachin-sankar.github.io/swiftshadow/)
75
+
76
+ ---
77
+
78
+ ## Why Swiftshadow?
79
+
80
+ - **Lightweight**: Minimal dependencies and easy to integrate.
81
+ - **Flexible**: Supports filtering by country and protocol.
82
+ - **Scalable**: Ideal for both small scripts and large-scale scraping projects.
83
+ - **Open Source**: Free to use, modify, and contribute to under the MIT License.
84
+
85
+ ---
86
+
87
+ ## Contributing
88
+
89
+ Contributions are welcome! If you'd like to improve **Swiftshadow**, feel free to open an issue or submit a pull request on [GitHub](https://github.com/sachin-sankar/swiftshadow).
90
+
91
+ ---
92
+
93
+ ## License
94
+
95
+ **Swiftshadow** is licensed under the MIT License. See the [LICENSE](https://github.com/sachin-sankar/swiftshadow/blob/main/LICENSE) file for details.
@@ -0,0 +1,14 @@
1
+ swiftshadow/__init__.py,sha256=DCxCxaMrluQDVJLyK5K61NxSaadD1d-nyTrFKsjfNDI,921
2
+ swiftshadow/cache.py,sha256=eA_AWa8EsPdB6xD__ButvQdqETC4i89qEgxmHQV2XWU,800
3
+ swiftshadow/classes.py,sha256=4hr0h2aOb-CXYPrf_ESf05RSEdRVLmYtvx09npnUVhQ,8242
4
+ swiftshadow/exceptions.py,sha256=qu4eXyrkWD9qd4HCIR-8vRfVcqLlTupo4sD72alCdug,129
5
+ swiftshadow/helpers.py,sha256=hHJ_JjRx2UFC5Ircl75LeYKBNDYTY_xMy2iWCk-UPqo,959
6
+ swiftshadow/models.py,sha256=YyfZV98tPdLnF1O3WmTNUNoK4t0GuchfEftzjiM03ck,1678
7
+ swiftshadow/providers.py,sha256=myJ6t-WD20wrjc7qDhxCpX1-oi7ipQutp34XKM2tjeI,5660
8
+ swiftshadow/types.py,sha256=Alyw3n54OESX1vSR-0kTvpYTlJ8LKfy5J9WZbtglHpE,894
9
+ swiftshadow/validator.py,sha256=dBcb6Lev6ojsk9f45IjyEUHZXsCcylyn6RoZA1WPDNQ,1345
10
+ swiftshadow-2.0.0.dist-info/METADATA,sha256=MSM2WB68lsN__C1JiNwMH2sPMah2GrPUoCwyNbPoF9U,3261
11
+ swiftshadow-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ swiftshadow-2.0.0.dist-info/entry_points.txt,sha256=yMj0uEagcmXK2dmMmNXWebTpTT9j5K03oaRrd2wkyLA,49
13
+ swiftshadow-2.0.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ swiftshadow-2.0.0.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.2.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ swiftshadow = swiftshadow:main
swiftshadow/constants.py DELETED
@@ -1,250 +0,0 @@
1
- CountryCodes = {
2
- "Afghanistan": "AF",
3
- "Åland Islands": "AX",
4
- "Albania": "AL",
5
- "Algeria": "DZ",
6
- "American Samoa": "AS",
7
- "Andorra": "AD",
8
- "Angola": "AO",
9
- "Anguilla": "AI",
10
- "Antarctica": "AQ",
11
- "Antigua and Barbuda": "AG",
12
- "Argentina": "AR",
13
- "Armenia": "AM",
14
- "Aruba": "AW",
15
- "Australia": "AU",
16
- "Austria": "AT",
17
- "Azerbaijan": "AZ",
18
- "Bahamas": "BS",
19
- "Bahrain": "BH",
20
- "Bangladesh": "BD",
21
- "Barbados": "BB",
22
- "Belarus": "BY",
23
- "Belgium": "BE",
24
- "Belize": "BZ",
25
- "Benin": "BJ",
26
- "Bermuda": "BM",
27
- "Bhutan": "BT",
28
- "Bolivia (Plurinational State of)": "BO",
29
- '"Bonaire': ' Sint Eustatius and Saba"',
30
- "Bosnia and Herzegovina": "BA",
31
- "Botswana": "BW",
32
- "Bouvet Island": "BV",
33
- "Brazil": "BR",
34
- "British Indian Ocean Territory": "IO",
35
- "Brunei Darussalam": "BN",
36
- "Bulgaria": "BG",
37
- "Burkina Faso": "BF",
38
- "Burundi": "BI",
39
- "Cabo Verde": "CV",
40
- "Cambodia": "KH",
41
- "Cameroon": "CM",
42
- "Canada": "CA",
43
- "Cayman Islands": "KY",
44
- "Central African Republic": "CF",
45
- "Chad": "TD",
46
- "Chile": "CL",
47
- "China": "CN",
48
- "Christmas Island": "CX",
49
- "Cocos (Keeling) Islands": "CC",
50
- "Colombia": "CO",
51
- "Comoros": "KM",
52
- "Congo": "CG",
53
- '"Congo': ' Democratic Republic of the"',
54
- "Cook Islands": "CK",
55
- "Costa Rica": "CR",
56
- "Côte d'Ivoire": "CI",
57
- "Croatia": "HR",
58
- "Cuba": "CU",
59
- "Curaçao": "CW",
60
- "Cyprus": "CY",
61
- "Czechia": "CZ",
62
- "Denmark": "DK",
63
- "Djibouti": "DJ",
64
- "Dominica": "DM",
65
- "Dominican Republic": "DO",
66
- "Ecuador": "EC",
67
- "Egypt": "EG",
68
- "El Salvador": "SV",
69
- "Equatorial Guinea": "GQ",
70
- "Eritrea": "ER",
71
- "Estonia": "EE",
72
- "Eswatini": "SZ",
73
- "Ethiopia": "ET",
74
- "Falkland Islands (Malvinas)": "FK",
75
- "Faroe Islands": "FO",
76
- "Fiji": "FJ",
77
- "Finland": "FI",
78
- "France": "FR",
79
- "French Guiana": "GF",
80
- "French Polynesia": "PF",
81
- "French Southern Territories": "TF",
82
- "Gabon": "GA",
83
- "Gambia": "GM",
84
- "Georgia": "GE",
85
- "Germany": "DE",
86
- "Ghana": "GH",
87
- "Gibraltar": "GI",
88
- "Greece": "GR",
89
- "Greenland": "GL",
90
- "Grenada": "GD",
91
- "Guadeloupe": "GP",
92
- "Guam": "GU",
93
- "Guatemala": "GT",
94
- "Guernsey": "GG",
95
- "Guinea": "GN",
96
- "Guinea-Bissau": "GW",
97
- "Guyana": "GY",
98
- "Haiti": "HT",
99
- "Heard Island and McDonald Islands": "HM",
100
- "Holy See": "VA",
101
- "Honduras": "HN",
102
- "Hong Kong": "HK",
103
- "Hungary": "HU",
104
- "Iceland": "IS",
105
- "India": "IN",
106
- "Indonesia": "ID",
107
- "Iran (Islamic Republic of)": "IR",
108
- "Iraq": "IQ",
109
- "Ireland": "IE",
110
- "Isle of Man": "IM",
111
- "Israel": "IL",
112
- "Italy": "IT",
113
- "Jamaica": "JM",
114
- "Japan": "JP",
115
- "Jersey": "JE",
116
- "Jordan": "JO",
117
- "Kazakhstan": "KZ",
118
- "Kenya": "KE",
119
- "Kiribati": "KI",
120
- "Korea (Democratic People's Republic of)": "KP",
121
- '"Korea': ' Republic of"',
122
- "Kuwait": "KW",
123
- "Kyrgyzstan": "KG",
124
- "Lao People's Democratic Republic": "LA",
125
- "Latvia": "LV",
126
- "Lebanon": "LB",
127
- "Lesotho": "LS",
128
- "Liberia": "LR",
129
- "Libya": "LY",
130
- "Liechtenstein": "LI",
131
- "Lithuania": "LT",
132
- "Luxembourg": "LU",
133
- "Macao": "MO",
134
- "Madagascar": "MG",
135
- "Malawi": "MW",
136
- "Malaysia": "MY",
137
- "Maldives": "MV",
138
- "Mali": "ML",
139
- "Malta": "MT",
140
- "Marshall Islands": "MH",
141
- "Martinique": "MQ",
142
- "Mauritania": "MR",
143
- "Mauritius": "MU",
144
- "Mayotte": "YT",
145
- "Mexico": "MX",
146
- "Micronesia (Federated States of)": "FM",
147
- '"Moldova': ' Republic of"',
148
- "Monaco": "MC",
149
- "Mongolia": "MN",
150
- "Montenegro": "ME",
151
- "Montserrat": "MS",
152
- "Morocco": "MA",
153
- "Mozambique": "MZ",
154
- "Myanmar": "MM",
155
- "Namibia": "NA",
156
- "Nauru": "NR",
157
- "Nepal": "NP",
158
- "Netherlands": "NL",
159
- "New Caledonia": "NC",
160
- "New Zealand": "NZ",
161
- "Nicaragua": "NI",
162
- "Niger": "NE",
163
- "Nigeria": "NG",
164
- "Niue": "NU",
165
- "Norfolk Island": "NF",
166
- "North Macedonia": "MK",
167
- "Northern Mariana Islands": "MP",
168
- "Norway": "NO",
169
- "Oman": "OM",
170
- "Pakistan": "PK",
171
- "Palau": "PW",
172
- '"Palestine': ' State of"',
173
- "Panama": "PA",
174
- "Papua New Guinea": "PG",
175
- "Paraguay": "PY",
176
- "Peru": "PE",
177
- "Philippines": "PH",
178
- "Pitcairn": "PN",
179
- "Poland": "PL",
180
- "Portugal": "PT",
181
- "Puerto Rico": "PR",
182
- "Qatar": "QA",
183
- "Réunion": "RE",
184
- "Romania": "RO",
185
- "Russian Federation": "RU",
186
- "Rwanda": "RW",
187
- "Saint Barthélemy": "BL",
188
- '"Saint Helena': ' Ascension and Tristan da Cunha"',
189
- "Saint Kitts and Nevis": "KN",
190
- "Saint Lucia": "LC",
191
- "Saint Martin (French part)": "MF",
192
- "Saint Pierre and Miquelon": "PM",
193
- "Saint Vincent and the Grenadines": "VC",
194
- "Samoa": "WS",
195
- "San Marino": "SM",
196
- "Sao Tome and Principe": "ST",
197
- "Saudi Arabia": "SA",
198
- "Senegal": "SN",
199
- "Serbia": "RS",
200
- "Seychelles": "SC",
201
- "Sierra Leone": "SL",
202
- "Singapore": "SG",
203
- "Sint Maarten (Dutch part)": "SX",
204
- "Slovakia": "SK",
205
- "Slovenia": "SI",
206
- "Solomon Islands": "SB",
207
- "Somalia": "SO",
208
- "South Africa": "ZA",
209
- "South Georgia and the South Sandwich Islands": "GS",
210
- "South Sudan": "SS",
211
- "Spain": "ES",
212
- "Sri Lanka": "LK",
213
- "Sudan": "SD",
214
- "Suriname": "SR",
215
- "Svalbard and Jan Mayen": "SJ",
216
- "Sweden": "SE",
217
- "Switzerland": "CH",
218
- "Syrian Arab Republic": "SY",
219
- '"Taiwan': ' Province of China"',
220
- "Tajikistan": "TJ",
221
- '"Tanzania': ' United Republic of"',
222
- "Thailand": "TH",
223
- "Timor-Leste": "TL",
224
- "Togo": "TG",
225
- "Tokelau": "TK",
226
- "Tonga": "TO",
227
- "Trinidad and Tobago": "TT",
228
- "Tunisia": "TN",
229
- "Turkey": "TR",
230
- "Turkmenistan": "TM",
231
- "Turks and Caicos Islands": "TC",
232
- "Tuvalu": "TV",
233
- "Uganda": "UG",
234
- "Ukraine": "UA",
235
- "United Arab Emirates": "AE",
236
- "United Kingdom of Great Britain and Northern Ireland": "GB",
237
- "United States of America": "US",
238
- "United States Minor Outlying Islands": "UM",
239
- "Uruguay": "UY",
240
- "Uzbekistan": "UZ",
241
- "Vanuatu": "VU",
242
- "Venezuela (Bolivarian Republic of)": "VE",
243
- "Viet Nam": "VN",
244
- "Virgin Islands (British)": "VG",
245
- "Virgin Islands (U.S.)": "VI",
246
- "Wallis and Futuna": "WF",
247
- "Western Sahara": "EH",
248
- "Yemen": "YE",
249
- "Zambia": "ZM",
250
- }
@@ -1,54 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: swiftshadow
3
- Version: 1.2.0
4
- Summary: Free IP Proxy rotator for python
5
- Home-page: https://github.com/sachin-sankar/swiftshadow
6
- Author: Sachin Sankar
7
- Author-email: mail.sachinsankar@gmail.com
8
- Classifier: Development Status :: 5 - Production/Stable
9
- Description-Content-Type: text/markdown
10
- License-File: LICENSE
11
- Requires-Dist: requests
12
-
13
- # Swiftshadow
14
-
15
- ![PyPI - Downloads](https://img.shields.io/pypi/dm/swiftshadow) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/sachin-sankar/swiftshadow?include_prereleases&style=flat)
16
-
17
- > [!TIP]
18
- > I'm refactoring the library for better speed and maintainability. Future updates might have breaking changes, but I'll keep you posted!
19
-
20
- ## About
21
-
22
- Swiftshadow is a powerful Python library designed to simplify the process of rotating IP proxies for web scraping, data mining, and other automated tasks. With its advanced features, Swiftshadow can help you overcome many of the challenges associated with web scraping, including blocked IP addresses and other forms of detection.
23
-
24
- One of the key benefits of Swiftshadow is its speed. The library is designed to operate quickly and efficiently, which means you can scrape data at a faster rate than with other tools. Additionally, Swiftshadow includes a built-in caching mechanism that helps to reduce the load time and improve performance.
25
-
26
- Another important feature of Swiftshadow is its reliability. The library is designed to be robust and resilient, which means that even if one of your proxies fails or is blocked, the system will automatically switch to another proxy to ensure continuity of your scraping process. This feature helps to ensure that your scraping efforts are not interrupted by technical issues or other problems.
27
-
28
- Swiftshadow is also highly customizable. You can easily configure the library to suit your specific needs, including specifying the number of proxies to use, the frequency of rotation, and other parameters. This level of flexibility makes Swiftshadow a versatile tool that can be adapted to a wide range of use cases.
29
-
30
- Finally, Swiftshadow has a low dependency on third-party libraries (only one), which makes it easier to use and maintain. Whether you are a seasoned developer or a novice, you can quickly get started with Swiftshadow and start scraping data with ease.
31
-
32
- ## Installation
33
-
34
- To get started install the library using pip.
35
-
36
- ``` py
37
- pip install swiftshadow
38
- ```
39
-
40
- ## One class rules all
41
-
42
- Everything in swiftshadow is under one class for ease of use and minimal code.
43
-
44
- Get a proxy using just 2 lines of code!
45
-
46
- ``` py
47
- from swiftshadow.swiftshadow import QuickProxy
48
-
49
- print(QuickProxy())
50
- ```
51
-
52
- That was easy.
53
-
54
- Head to [Documentation](https://sachin-sankar.github.io/swiftshadow/) to get started.
@@ -1,11 +0,0 @@
1
- swiftshadow/__init__.py,sha256=UGscPyIFECuTgLjcMxp9_QNyfxd6b-msRAtubenBAL0,764
2
- swiftshadow/cache.py,sha256=tNj07m0Ii_r0wLu9NKTYxOyS8uYeC7BbgFdMnuLqK7Y,411
3
- swiftshadow/classes.py,sha256=YlXuV5QobEwvIMQDt2sjT173Q-EQbGk6bz5nfyn0Cds,5249
4
- swiftshadow/constants.py,sha256=FRhoYg8WNaLy8RLDD4BsANlXlY2tctBCdy7sCnkaVY0,6303
5
- swiftshadow/helpers.py,sha256=RGC-Tq9vWqiddu_KEKYWc_URO-ISm3PXuMBqB_cl7XQ,533
6
- swiftshadow/providers.py,sha256=qUYUxUuSifwuWWs3KxW2al_T5ee9Lr7MRaTddIR9lyw,2060
7
- swiftshadow-1.2.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
8
- swiftshadow-1.2.0.dist-info/METADATA,sha256=RRq0DPKJlgD1gboURK0gtCJ_Fq04P08hinNvz988BjY,2793
9
- swiftshadow-1.2.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
10
- swiftshadow-1.2.0.dist-info/top_level.txt,sha256=GClzVDF5vWSzqHOwgG2ycQAAJXRxd-QOdP1h3YrHOuM,12
11
- swiftshadow-1.2.0.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- swiftshadow