proxy-scraper-cli 1.12.0__tar.gz → 1.14.0__tar.gz
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.
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/PKG-INFO +9 -1
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/README.md +8 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/PKG-INFO +9 -1
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/SOURCES.txt +1 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/__init__.py +1 -1
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/cli.py +52 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/publish.py +3 -1
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/index.html +61 -1
- proxy_scraper_cli-1.14.0/tests/test_pick.py +44 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_sites.py +2 -1
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/LICENSE +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/dependency_links.txt +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/entry_points.txt +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/requires.txt +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/top_level.txt +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/__main__.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/action.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/agent.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/api.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/app.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/asndb.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/blocklist.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/charts.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/checker.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/compat.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/completion.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/exporters.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/fetchcache.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/geo.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/geodb.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/handshake.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/history.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/judges.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/mcp_entry.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/mcp_server.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/netio.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/options.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/output.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/pages.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/parsing.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/paths.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/pipeline.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/preferences.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/proxypages.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/report.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/runsteps.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/__init__.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/core.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/http.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/pool.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/socks.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/status.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/server/upstream.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/apple-touch-icon.png +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/googleaac1161b7853c5b5.html +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/logo.png +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/og.png +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/sites.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/sources.json +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/sources.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/speed.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/targets.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/__init__.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/dashboard.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/keys.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/report.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/serve.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/widgets.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/ui/wizard.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/pyproject.toml +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/setup.cfg +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_action.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_agent.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_api.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_app.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_asndb.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_auth.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_blocklist.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_charts.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_checker.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_compat.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_completion.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_discovery_growth.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_exporters.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_fetchcache.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_geodb.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_judges.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_learning.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_live_api.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_mcp_server.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_mine_sources.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_options.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_output_stdout.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_packaging.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_pages.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_parsing.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_parsing_fuzz.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_paths.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_pipeline.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_proxy_pages.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_proxy_sources.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_publish.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_report.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_robustness.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_serve_password.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_serve_refill.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_serve_v2.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_server.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_server_closed_writer.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_server_fixes.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_sources_json.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_speed.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_tamper.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_targets.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_ui.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_uptime.py +0 -0
- {proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/tests/test_wizard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxy-scraper-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.0
|
|
4
4
|
Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
|
|
5
5
|
Author: Maximilian Feix
|
|
6
6
|
License-Expression: MIT
|
|
@@ -699,6 +699,13 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
699
699
|
|
|
700
700
|
`proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
|
|
701
701
|
|
|
702
|
+
**In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
706
|
+
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
707
|
+
```
|
|
708
|
+
|
|
702
709
|
**Without installing anything** – straight from the live list
|
|
703
710
|
|
|
704
711
|
```bash
|
|
@@ -743,6 +750,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
743
750
|
| `--discover` | search GitHub for new sources right now |
|
|
744
751
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
745
752
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
753
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
|
|
746
754
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
747
755
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
748
756
|
| `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
|
|
@@ -658,6 +658,13 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
658
658
|
|
|
659
659
|
`proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
|
|
660
660
|
|
|
661
|
+
**In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
|
|
662
|
+
|
|
663
|
+
```bash
|
|
664
|
+
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
665
|
+
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
666
|
+
```
|
|
667
|
+
|
|
661
668
|
**Without installing anything** – straight from the live list
|
|
662
669
|
|
|
663
670
|
```bash
|
|
@@ -702,6 +709,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
702
709
|
| `--discover` | search GitHub for new sources right now |
|
|
703
710
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
704
711
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
712
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
|
|
705
713
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
706
714
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
707
715
|
| `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proxy-scraper-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.0
|
|
4
4
|
Summary: Finds free HTTP, SOCKS4 and SOCKS5 proxies that actually work: 700+ sources, real checks, honeypot filtering and a rotating local proxy server.
|
|
5
5
|
Author: Maximilian Feix
|
|
6
6
|
License-Expression: MIT
|
|
@@ -699,6 +699,13 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
699
699
|
|
|
700
700
|
`proxy` is the fastest one, `file` a text file with all of them (up to `limit`, default 20), `count` how many. Without a match the step fails, unless `fail-if-empty: false`.
|
|
701
701
|
|
|
702
|
+
**In the shell, without a scan** – `--pick` takes proxies from the hourly list in about half a second
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
706
|
+
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
707
|
+
```
|
|
708
|
+
|
|
702
709
|
**Without installing anything** – straight from the live list
|
|
703
710
|
|
|
704
711
|
```bash
|
|
@@ -743,6 +750,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
743
750
|
| `--discover` | search GitHub for new sources right now |
|
|
744
751
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
745
752
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
753
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT` and `--works-on google,reddit,…` |
|
|
746
754
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
747
755
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
748
756
|
| `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""proxy-scraper: collects public HTTP/SOCKS4/SOCKS5 proxies from hundreds of sources, checks them
|
|
2
2
|
in parallel with its own protocol handshakes and learns which sources deliver good proxies."""
|
|
3
3
|
|
|
4
|
-
__version__ = "1.
|
|
4
|
+
__version__ = "1.14.0"
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def __getattr__(name):
|
|
@@ -34,6 +34,7 @@ from .output import has_latest_results
|
|
|
34
34
|
from .parsing import PROXY_TYPES
|
|
35
35
|
from .preferences import load_last_argv, save_last_argv
|
|
36
36
|
from .server.pool import STRATEGIES
|
|
37
|
+
from .sites import SITES
|
|
37
38
|
from .targets import parse_target
|
|
38
39
|
from .ui import ACCENT, BAD, MUTED, banner, note, render_source_ranking, widgets
|
|
39
40
|
from .ui.keys import is_interactive
|
|
@@ -62,6 +63,36 @@ def list_sources(limit: int, as_json: bool = False) -> int:
|
|
|
62
63
|
return 0
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
def pick(args) -> int:
|
|
67
|
+
"""--pick: proxies from the hourly list, nothing but their URLs on stdout (for $(…) in a shell)."""
|
|
68
|
+
from .api import live_proxies
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
found = live_proxies(types=args.types, countries=args.country or "", https=args.https_only,
|
|
72
|
+
anonymity=args.anonymity or "", max_latency=args.max_latency,
|
|
73
|
+
no_datacenter=args.no_datacenter, no_blocklisted=args.no_blocklisted,
|
|
74
|
+
min_uptime=args.min_uptime, works_on=args.works_on, limit=args.pick)
|
|
75
|
+
except (ConnectionError, ValueError) as e:
|
|
76
|
+
print(f"proxy-scraper: {e}", file=sys.stderr)
|
|
77
|
+
return 1
|
|
78
|
+
if not found:
|
|
79
|
+
print("proxy-scraper: no proxy in the live list matches these filters right now – loosen them, "
|
|
80
|
+
"or run a scan of your own", file=sys.stderr)
|
|
81
|
+
return 1
|
|
82
|
+
sys.stdout.write("".join(f"{p.url}\n" for p in found))
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def site_list(text: str) -> List[str]:
|
|
87
|
+
"""argparse type for --works-on: google,reddit -> ["google", "reddit"]"""
|
|
88
|
+
names = [n.strip().lower() for n in text.split(",") if n.strip()]
|
|
89
|
+
known = [s.name for s in SITES]
|
|
90
|
+
for n in names:
|
|
91
|
+
if n not in known:
|
|
92
|
+
raise argparse.ArgumentTypeError(f"unknown site {n!r}, use {', '.join(known)}")
|
|
93
|
+
return names
|
|
94
|
+
|
|
95
|
+
|
|
65
96
|
def _number(kind, minimum, strict=False, maximum=None):
|
|
66
97
|
"""argparse type for numbers with limits – with an understandable error message."""
|
|
67
98
|
def parse(text: str):
|
|
@@ -96,6 +127,7 @@ def export_list(text: str) -> List[str]:
|
|
|
96
127
|
positive_int = _number(int, 1)
|
|
97
128
|
port_number = _number(int, 1, maximum=65535)
|
|
98
129
|
non_negative_int = _number(int, 0)
|
|
130
|
+
percent = _number(int, 0, maximum=100)
|
|
99
131
|
positive_float = _number(float, 0, strict=True)
|
|
100
132
|
non_negative_float = _number(float, 0)
|
|
101
133
|
|
|
@@ -166,6 +198,16 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
|
166
198
|
f.add_argument("--target", action="append", type=target_url, metavar="URL",
|
|
167
199
|
help="only proxies that reach this site (repeatable), e.g. --target google.com")
|
|
168
200
|
|
|
201
|
+
live = p.add_argument_group("Live list (no scan)")
|
|
202
|
+
live.add_argument("--pick", nargs="?", const=1, type=positive_int, metavar="N",
|
|
203
|
+
help="print N proxies (default 1) from the hourly checked list that match the filters above, "
|
|
204
|
+
"one URL per line, and exit – e.g. curl -x \"$(proxy-scraper --pick --https-only)\" …")
|
|
205
|
+
live.add_argument("--min-uptime", type=percent, default=0, metavar="PERCENT",
|
|
206
|
+
help="with --pick: only proxies on the list in at least this share of the week's checks")
|
|
207
|
+
live.add_argument("--works-on", type=site_list, default=[], metavar="SITES",
|
|
208
|
+
help=f"with --pick: only proxies that got through to these sites in the last check "
|
|
209
|
+
f"({', '.join(s.name for s in SITES)})")
|
|
210
|
+
|
|
169
211
|
v = p.add_argument_group("Proxy server")
|
|
170
212
|
v.add_argument("--serve", nargs="?", const=DEFAULT_SERVE_PORT, default=0, type=port_number, metavar="PORT",
|
|
171
213
|
help=f"serve the hits as a rotating proxy on 127.0.0.1:PORT after the run "
|
|
@@ -209,6 +251,14 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
|
209
251
|
args = p.parse_args(argv)
|
|
210
252
|
if args.json and args.list_sources is None:
|
|
211
253
|
p.error("--json only works together with --list-sources")
|
|
254
|
+
if (args.min_uptime or args.works_on) and args.pick is None:
|
|
255
|
+
p.error("--min-uptime and --works-on only work together with --pick")
|
|
256
|
+
if args.pick is not None: # --pick exits right away: options for a scan or the server would be silently ignored
|
|
257
|
+
scan_only = [flag for dest, flag in (("target", "--target"), ("serve", "--serve"), ("recheck", "--recheck"),
|
|
258
|
+
("want", "--want"), ("output", "-o/--output"), ("export", "--export"))
|
|
259
|
+
if getattr(args, dest) != p.get_default(dest)]
|
|
260
|
+
if scan_only:
|
|
261
|
+
p.error(f"--pick takes proxies from the live list and exits – {', '.join(scan_only)} belong to a scan")
|
|
212
262
|
return args
|
|
213
263
|
|
|
214
264
|
|
|
@@ -260,6 +310,8 @@ def run(argv: Optional[List[str]] = None) -> int:
|
|
|
260
310
|
return mcp_main()
|
|
261
311
|
if args.list_sources is not None:
|
|
262
312
|
return list_sources(args.list_sources, args.json)
|
|
313
|
+
if args.pick is not None:
|
|
314
|
+
return pick(args)
|
|
263
315
|
opts = RunOptions.from_args(args)
|
|
264
316
|
if opts.output == STDOUT:
|
|
265
317
|
widgets.console = Console(highlight=False, stderr=True) # stdout only carries the hits
|
|
@@ -70,7 +70,9 @@ def stats_for(rows: List[dict], now: datetime) -> dict:
|
|
|
70
70
|
if any(r.get("blocklisted") is not None for r in rows) else None,
|
|
71
71
|
"stable": sum(1 for r in rows if r.get("streak", 0) >= STABLE_RUNS),
|
|
72
72
|
# proxies that got through to Google, Reddit, ... in the site check after the run (sites.py)
|
|
73
|
-
|
|
73
|
+
# None when a site wasn't checked at all (the step failed, its DNS lookup didn't work) – not a measured 0
|
|
74
|
+
"sites": {s.name: sum(1 for r in rows if (r.get("sites") or {}).get(s.name))
|
|
75
|
+
if any(s.name in (r.get("sites") or {}) for r in rows) else None for s in SITES},
|
|
74
76
|
"run_hours": RUN_HOURS,
|
|
75
77
|
"countries": dict(Counter(r["country"] for r in rows if r.get("country")).most_common(15)),
|
|
76
78
|
"median_latency": round(statistics.median(r["latency"] for r in rows)) if rows else 0,
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
<meta property="og:description" content="Every proxy on this list passed a real handshake, a honeypot check and a content check. Re-checked every hour.">
|
|
23
23
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='18' fill='%23D4F77A'/%3E%3Cpath d='M13 31.5 26.5 44.5 51 18' fill='none' stroke='%23121113' stroke-width='5.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Ccircle cx='13' cy='31.5' r='4.6' fill='%23121113'/%3E%3Ccircle cx='51' cy='18' r='4.6' fill='%23121113'/%3E%3Ccircle cx='26.5' cy='44.5' r='7.6' fill='%23D4F77A' stroke='%23121113' stroke-width='5'/%3E%3C/svg%3E">
|
|
24
24
|
<script type="application/ld+json">
|
|
25
|
+
{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "Are free proxies safe to use?", "acceptedAnswer": {"@type": "Answer", "text": "They're run by strangers, so treat them as untrusted. Anything that isn't encrypted can be read by whoever runs the proxy – never send passwords or personal data through one. Every proxy on this list has passed a content check (it didn't change a known page), and the HTTPS ones tunnel TLS that's verified end to end, so an https:// site can't be read or altered. That makes them fine for scraping public pages or seeing a site from another country, not for your bank."}}, {"@type": "Question", "name": "Why do most free proxies not work?", "acceptedAnswer": {"@type": "Answer", "text": "They don't live long. Of the proxies that drop off this list again, most are gone by the next hourly check, and only a few last a whole day – the weekly report tracks it. Many lists never re-check, so a large share of what they hold is long dead, and some of what still answers are honeypots or proxies that inject ads."}}, {"@type": "Question", "name": "How often is the list updated?", "acceptedAnswer": {"@type": "Answer", "text": "Every hour. GitHub Actions collects proxies from 700+ public lists, checks them and publishes whatever passed. This page loads the new list by itself when a run lands."}}, {"@type": "Question", "name": "Can I use it in my own projects?", "acceptedAnswer": {"@type": "Answer", "text": "Yes. The code is open source under the MIT license, and the lists are free to use, including in commercial projects. The proxies themselves belong to other people, so stick to the terms of the sites you visit through them."}}, {"@type": "Question", "name": "How do I use it from Python or an AI agent?", "acceptedAnswer": {"@type": "Answer", "text": "From Python: pip install proxy-scraper-cli, then live_proxies() returns the checked list with filters for country, protocol, uptime and the sites a proxy gets through to. AI agents like Claude Code or Cursor can use the MCP server, which gives them working proxies and can load pages through them."}}, {"@type": "Question", "name": "A proxy from the list doesn't work for me – why?", "acceptedAnswer": {"@type": "Answer", "text": "It worked from GitHub's servers in the last run, but free proxies come and go, and some block certain networks. proxy-scraper --recheck live checks the list again from your own connection in about 30 seconds and keeps what works from there."}}]}
|
|
26
|
+
</script>
|
|
27
|
+
<script type="application/ld+json">
|
|
25
28
|
{"@context": "https://schema.org", "@type": "Dataset",
|
|
26
29
|
"name": "Free proxy list, checked every hour",
|
|
27
30
|
"description": "HTTP, SOCKS4 and SOCKS5 proxies from 700+ public lists that passed a real handshake, a honeypot check on two sites and a content check. With country, provider, latency, anonymity and HTTPS support.",
|
|
@@ -343,6 +346,25 @@
|
|
|
343
346
|
.status .next:not(:empty)::before { content: " · "; }
|
|
344
347
|
.bookmarklet .btn { cursor: grab; margin: 6px 0 4px; }
|
|
345
348
|
.link-btn { font: inherit; color: var(--text); background: none; border: 0; padding: 0; text-decoration: underline; cursor: pointer; }
|
|
349
|
+
/* Why this list: one question per row, what a typical list says against what was measured here */
|
|
350
|
+
.ledger { border-top: 1px solid var(--line); }
|
|
351
|
+
.ledger-row { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr) minmax(0, 1.6fr); gap: 8px 32px; padding: 20px 0;
|
|
352
|
+
border-bottom: 1px solid var(--line); align-items: baseline; }
|
|
353
|
+
.ledger-head { padding: 14px 0; color: var(--muted); font-size: 14px; }
|
|
354
|
+
.ledger-row .q { font-size: 18px; font-weight: 500; letter-spacing: -.01em; }
|
|
355
|
+
.ledger-row .them { color: var(--muted); }
|
|
356
|
+
.ledger-row .us b { color: var(--signal-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
357
|
+
/* FAQ: opens on click, nothing moves on its own */
|
|
358
|
+
.faq-list { border-top: 1px solid var(--line); }
|
|
359
|
+
.faq-item { border-bottom: 1px solid var(--line); }
|
|
360
|
+
.faq-item summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 24px; padding: 22px 0; }
|
|
361
|
+
.faq-item summary::-webkit-details-marker { display: none; }
|
|
362
|
+
.faq-item summary h3 { margin: 0; font-size: clamp(18px, 2vw, 21px); font-weight: 500; letter-spacing: -.01em; }
|
|
363
|
+
.faq-item summary::after { content: ""; flex: none; width: 12px; height: 12px; border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
|
|
364
|
+
transform: translateY(-3px) rotate(45deg); transition: transform .3s var(--ease); }
|
|
365
|
+
.faq-item[open] summary::after { transform: translateY(3px) rotate(225deg); }
|
|
366
|
+
.faq-item summary:focus-visible { outline: 2px solid var(--signal-ink); outline-offset: 4px; border-radius: 6px; }
|
|
367
|
+
.faq-item p { margin: 0 0 24px; max-width: 70ch; color: var(--muted); font-size: 17px; }
|
|
346
368
|
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 22px; padding: clamp(22px, 3vw, 34px); }
|
|
347
369
|
|
|
348
370
|
/* For AI agents: a real Claude Code exchange, revealed line by line once it scrolls in */
|
|
@@ -440,6 +462,9 @@
|
|
|
440
462
|
.ctas { justify-content: flex-start; }
|
|
441
463
|
.figures { grid-template-columns: minmax(0, 1fr); }
|
|
442
464
|
.head-row, .agent, .closing { grid-template-columns: minmax(0, 1fr); }
|
|
465
|
+
.ledger-row { grid-template-columns: minmax(0, 1fr); gap: 4px; }
|
|
466
|
+
.ledger-head { display: none; }
|
|
467
|
+
.ledger-row .them::before { content: "Typical list: "; }
|
|
443
468
|
.tools li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
|
|
444
469
|
.transcript p { grid-template-columns: minmax(0, 1fr); gap: 4px; }
|
|
445
470
|
.head-row .intro-text { justify-self: start; }
|
|
@@ -507,7 +532,7 @@
|
|
|
507
532
|
<div class="wrap">
|
|
508
533
|
<a class="brand" href="#top" aria-label="proxy-scraper, back to top"><svg class="mark" aria-hidden="true"><use href="#mark"/></svg>proxy-scraper</a>
|
|
509
534
|
<nav class="nav-links" aria-label="Sections">
|
|
510
|
-
<a href="#list">The list</a><a href="#agents">AI agents</a><a href="#checks">How it's checked</a><a href="#runs">Last runs</a><a href="#use">Use it</a>
|
|
535
|
+
<a href="#list">The list</a><a href="#agents">AI agents</a><a href="#checks">How it's checked</a><a href="#why">Why this one</a><a href="#runs">Last runs</a><a href="#use">Use it</a>
|
|
511
536
|
</nav>
|
|
512
537
|
<div class="nav-actions">
|
|
513
538
|
<button class="toggle" id="theme" type="button" aria-label="Switch to light theme">
|
|
@@ -670,6 +695,23 @@
|
|
|
670
695
|
</div>
|
|
671
696
|
</div>
|
|
672
697
|
</section>
|
|
698
|
+
<section class="block wrap" id="why" aria-labelledby="why-title">
|
|
699
|
+
<div class="head-row">
|
|
700
|
+
<h2 class="h2" id="why-title">Why this list, not another one</h2>
|
|
701
|
+
<p class="intro-text">Most free proxy lists are a dump of addresses that answered at some point. This is what you'd want to know before you use one – and what this list measured in the last run.</p>
|
|
702
|
+
</div>
|
|
703
|
+
<div class="ledger" role="table" aria-label="What a typical free proxy list tells you, and what this one measures">
|
|
704
|
+
<div class="ledger-row ledger-head" role="row"><span role="columnheader">What you'd want to know</span><span role="columnheader">A typical free list</span><span role="columnheader">Here, in the last run</span></div>
|
|
705
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Is it a proxy at all?</span><span class="them" role="cell">That the port answered once</span><span class="us" role="cell">A real HTTP/SOCKS handshake and a page fetched through it: <b data-live="total">–</b> passed in the last run</span></div>
|
|
706
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Is it a honeypot?</span><span class="them" role="cell">Often not checked</span><span class="us" role="cell">The exit IP is checked on two different sites and has to match both times</span></div>
|
|
707
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Does it change the pages you load?</span><span class="them" role="cell">Not checked</span><span class="us" role="cell">A known page has to arrive byte for byte. Proxies that inject scripts or ads are dropped</span></div>
|
|
708
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Does HTTPS work through it?</span><span class="them" role="cell">Sometimes a flag in the list</span><span class="us" role="cell">A verified TLS handshake through the tunnel: <b data-live="https">–</b> of them</span></div>
|
|
709
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Will it still be there tomorrow?</span><span class="them" role="cell">Unknown</span><span class="us" role="cell">Every proxy's week of hourly checks. <b data-live="stable">–</b> were on the list for 90 %+ of the week</span></div>
|
|
710
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Does Google let it through?</span><span class="them" role="cell">Unknown</span><span class="us" role="cell">Tried on Google, Reddit, Amazon, Instagram and TikTok: <b data-live="google">–</b> got through to Google</span></div>
|
|
711
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">Is it fast enough for real pages?</span><span class="them" role="cell">A latency number, if any</span><span class="us" role="cell">100 KB downloaded through every HTTPS-capable one: half of them manage <b data-live="speed">–</b> or more</span></div>
|
|
712
|
+
<div class="ledger-row" role="row"><span class="q" role="rowheader">How fresh is it?</span><span class="them" role="cell">Scraped once, rarely checked again</span><span class="us" role="cell">Checked again every hour, the last run <b data-live="ago">–</b></span></div>
|
|
713
|
+
</div>
|
|
714
|
+
</section>
|
|
673
715
|
|
|
674
716
|
<section class="block wrap" id="runs" aria-labelledby="runs-title">
|
|
675
717
|
<div class="head-row">
|
|
@@ -748,6 +790,20 @@ proxy-scraper --want 50 --https-only</pre><button class="icon-btn" type="button"
|
|
|
748
790
|
}</pre><button class="icon-btn" type="button" data-copy-pre aria-label="Copy the example"><svg aria-hidden="true"><use href="#i-copy"/></svg></button></div>
|
|
749
791
|
</div>
|
|
750
792
|
</section>
|
|
793
|
+
<section class="block wrap faq" id="faq" aria-labelledby="faq-title">
|
|
794
|
+
<div class="head-row">
|
|
795
|
+
<h2 class="h2" id="faq-title">Questions people ask</h2>
|
|
796
|
+
<p class="intro-text">Short answers. The <a href="https://github.com/maximilianfeix/proxy-scraper#faq">README</a> has more.</p>
|
|
797
|
+
</div>
|
|
798
|
+
<div class="faq-list">
|
|
799
|
+
<details class="faq-item"><summary><h3>Are free proxies safe to use?</h3></summary><p>They're run by strangers, so treat them as untrusted. Anything that isn't encrypted can be read by whoever runs the proxy – never send passwords or personal data through one. Every proxy on this list has passed a content check (it didn't change a known page), and the HTTPS ones tunnel TLS that's verified end to end, so an https:// site can't be read or altered. That makes them fine for scraping public pages or seeing a site from another country, not for your bank.</p></details>
|
|
800
|
+
<details class="faq-item"><summary><h3>Why do most free proxies not work?</h3></summary><p>They don't live long. Of the proxies that drop off this list again, most are gone by the next hourly check, and only a few last a whole day – the weekly report tracks it. Many lists never re-check, so a large share of what they hold is long dead, and some of what still answers are honeypots or proxies that inject ads.</p></details>
|
|
801
|
+
<details class="faq-item"><summary><h3>How often is the list updated?</h3></summary><p>Every hour. GitHub Actions collects proxies from 700+ public lists, checks them and publishes whatever passed. This page loads the new list by itself when a run lands.</p></details>
|
|
802
|
+
<details class="faq-item"><summary><h3>Can I use it in my own projects?</h3></summary><p>Yes. The code is open source under the MIT license, and the lists are free to use, including in commercial projects. The proxies themselves belong to other people, so stick to the terms of the sites you visit through them.</p></details>
|
|
803
|
+
<details class="faq-item"><summary><h3>How do I use it from Python or an AI agent?</h3></summary><p>From Python: pip install proxy-scraper-cli, then live_proxies() returns the checked list with filters for country, protocol, uptime and the sites a proxy gets through to. AI agents like Claude Code or Cursor can use the MCP server, which gives them working proxies and can load pages through them.</p></details>
|
|
804
|
+
<details class="faq-item"><summary><h3>A proxy from the list doesn't work for me – why?</h3></summary><p>It worked from GitHub's servers in the last run, but free proxies come and go, and some block certain networks. proxy-scraper --recheck live checks the list again from your own connection in about 30 seconds and keeps what works from there.</p></details>
|
|
805
|
+
</div>
|
|
806
|
+
</section>
|
|
751
807
|
<section class="block wrap closing" aria-labelledby="star-title">
|
|
752
808
|
<div>
|
|
753
809
|
<h2 class="h2" id="star-title">Saved you an afternoon?</h2>
|
|
@@ -1311,6 +1367,10 @@ proxy-scraper --want 50 --https-only</pre><button class="icon-btn" type="button"
|
|
|
1311
1367
|
sieve(rows, st.total);
|
|
1312
1368
|
started = true;
|
|
1313
1369
|
$("#final-n").textContent = fmt(st.total);
|
|
1370
|
+
const live = { total: fmt(st.total), https: fmt(st.https), stable: fmt(rows.filter(r => (r.uptime_7d || 0) >= 90).length),
|
|
1371
|
+
google: st.sites && st.sites.google != null ? fmt(st.sites.google) : "–",
|
|
1372
|
+
speed: st.median_speed_kbps ? `${fmt(st.median_speed_kbps)} KB/s` : "–", ago: ago(st.updated) };
|
|
1373
|
+
$$("[data-live]").forEach((el) => { el.textContent = live[el.dataset.live] ?? "–"; });
|
|
1314
1374
|
const dc = st.datacenter != null ? ` ${pct(st.datacenter, st.total)} % exit from a datacenter` : "";
|
|
1315
1375
|
const bl = st.blocklisted != null ? `${dc ? "," : ""} ${pct(st.blocklisted, st.total)} % from an IP on a spam blocklist` : "";
|
|
1316
1376
|
$("#step5").textContent = `HTTPS with verified TLS, anonymity, country, provider and spam blocklists. This time ${fmt(st.https)} tunnel HTTPS and ${fmt(st.elite)} are elite.${dc}${bl}${dc || bl ? "." : ""}`;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""proxy-scraper --pick: proxies from the hourly list on stdout, with the usual filters, no scan."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from proxyscraper import api, cli
|
|
6
|
+
from tests.test_live_api import ROWS, STATS, fetch_from
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture(autouse=True)
|
|
10
|
+
def offline(monkeypatch):
|
|
11
|
+
rows = [dict(ROWS[0], sites={"google": True}), ROWS[1], ROWS[2]]
|
|
12
|
+
monkeypatch.setattr(api, "_live_fetch", fetch_from({"proxies.json": rows, "stats.json": STATS}))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_one_proxy_by_default_and_nothing_else_on_stdout(capsys):
|
|
16
|
+
assert cli.run(["--pick"]) == 0
|
|
17
|
+
assert capsys.readouterr().out == "http://2.2.2.2:80\n" # the fastest
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_the_usual_filters_apply(capsys):
|
|
21
|
+
assert cli.run(["--pick", "5", "--types", "socks4", "socks5", "--country", "DE", "--https-only"]) == 0
|
|
22
|
+
assert capsys.readouterr().out.splitlines() == ["socks4://3.3.3.3:4145", "socks5://1.1.1.1:1080"]
|
|
23
|
+
assert cli.run(["--pick", "--min-uptime", "90", "--works-on", "google"]) == 0
|
|
24
|
+
assert capsys.readouterr().out == "socks5://1.1.1.1:1080\n"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_nothing_matching_is_exit_1_with_a_hint_on_stderr(capsys):
|
|
28
|
+
assert cli.run(["--pick", "--country", "JP"]) == 1
|
|
29
|
+
out = capsys.readouterr()
|
|
30
|
+
assert out.out == "" and "no proxy" in out.err.lower()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_pick_only_filters_need_pick(capsys):
|
|
34
|
+
with pytest.raises(SystemExit):
|
|
35
|
+
cli.run(["--min-uptime", "90"])
|
|
36
|
+
assert "--pick" in capsys.readouterr().err
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("extra", [["--target", "discord.com"], ["--serve"], ["--recheck"], ["--want", "5"],
|
|
40
|
+
["-o", "x.txt"], ["--export", "clash"]])
|
|
41
|
+
def test_options_for_a_scan_are_an_error_not_ignored(extra, capsys):
|
|
42
|
+
with pytest.raises(SystemExit):
|
|
43
|
+
cli.run(["--pick", *extra])
|
|
44
|
+
assert "--pick" in capsys.readouterr().err
|
|
@@ -119,7 +119,8 @@ def test_publish_writes_a_list_per_site_and_counts(tmp_path):
|
|
|
119
119
|
assert (out / "works-with" / "reddit.txt").read_text() == ""
|
|
120
120
|
assert (out / "works-with" / "amazon.txt").read_text() == ""
|
|
121
121
|
stats = json.loads((out / "stats.json").read_text())
|
|
122
|
-
|
|
122
|
+
# amazon, instagram and tiktok weren't checked at all in this run: that's "not measured", not zero
|
|
123
|
+
assert stats["sites"] == {"google": 1, "reddit": 0, "amazon": None, "instagram": None, "tiktok": None}
|
|
123
124
|
assert (out / "works-with" / "tiktok.txt").read_text() == ""
|
|
124
125
|
import csv
|
|
125
126
|
with (out / "proxies.csv").open(newline="", encoding="utf-8") as fh:
|
|
File without changes
|
|
File without changes
|
{proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/requires.txt
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxy_scraper_cli.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/apple-touch-icon.png
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.12.0 → proxy_scraper_cli-1.14.0}/proxyscraper/site/googleaac1161b7853c5b5.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|