proxy-scraper-cli 1.17.0__tar.gz → 1.18.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.17.0 → proxy_scraper_cli-1.18.0}/PKG-INFO +7 -2
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/README.md +6 -1
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/PKG-INFO +7 -2
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/SOURCES.txt +1 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/__init__.py +1 -1
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/action.py +4 -2
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/agent.py +2 -1
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/api.py +9 -5
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/checker.py +1 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/cli.py +6 -3
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/pool.py +37 -5
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_action.py +13 -4
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_pick.py +7 -0
- proxy_scraper_cli-1.18.0/tests/test_speed_ranking.py +58 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/LICENSE +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/dependency_links.txt +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/entry_points.txt +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/requires.txt +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/top_level.txt +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/__main__.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/app.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/asndb.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/blocklist.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/charts.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/compat.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/completion.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/exporters.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/fetchcache.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/geo.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/geodb.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/handshake.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/history.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/judges.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/mcp_entry.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/mcp_server.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/netio.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/options.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/output.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/pages.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/parsing.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/paths.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/pipeline.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/preferences.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/proxypages.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/publish.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/report.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/runsteps.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/__init__.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/core.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/http.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/socks.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/status.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/server/upstream.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/apple-touch-icon.png +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/googleaac1161b7853c5b5.html +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/index.html +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/logo.png +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/og.png +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/sites.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/sources.json +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/sources.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/speed.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/targets.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/__init__.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/dashboard.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/keys.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/report.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/serve.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/widgets.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/ui/wizard.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/pyproject.toml +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/setup.cfg +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_agent.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_api.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_app.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_asndb.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_auth.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_blocklist.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_charts.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_checker.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_compat.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_completion.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_discovery_growth.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_exporters.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_fetchcache.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_geodb.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_judges.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_learning.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_live_api.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_mcp_server.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_mine_sources.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_options.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_output_stdout.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_packaging.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_pages.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_parsing.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_parsing_fuzz.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_paths.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_pipeline.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_proxy_pages.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_proxy_sources.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_publish.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_report.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_robustness.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_rotator.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_serve_password.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_serve_refill.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_serve_v2.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_server.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_server_closed_writer.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_server_fixes.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_sites.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_sources_json.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_speed.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_tamper.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_targets.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_ui.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/tests/test_uptime.py +0 -0
- {proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.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.18.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
|
|
@@ -704,6 +704,7 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
704
704
|
types: socks5
|
|
705
705
|
https: true
|
|
706
706
|
min-uptime: 90 # on the list for 90 %+ of the week
|
|
707
|
+
min-speed: 100 # optional: downloaded 100+ KB/s in the last check
|
|
707
708
|
works-on: google # optional: google, reddit, amazon
|
|
708
709
|
recheck: true # optional: check them again from the runner
|
|
709
710
|
- run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
|
|
@@ -716,8 +717,12 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
716
717
|
```bash
|
|
717
718
|
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
718
719
|
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
720
|
+
proxy-scraper --pick 5 --min-speed 200 # only ones that downloaded 200+ KB/s
|
|
719
721
|
```
|
|
720
722
|
|
|
723
|
+
"Fastest first" means the first answer plus the download speed measured in the last check – in a test with real
|
|
724
|
+
pages, the 25 fastest by download loaded four times as many pages as the 25 quickest to answer a tiny request.
|
|
725
|
+
|
|
721
726
|
**Without installing anything** – straight from the live list
|
|
722
727
|
|
|
723
728
|
```bash
|
|
@@ -762,7 +767,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
762
767
|
| `--discover` | search GitHub for new sources right now |
|
|
763
768
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
764
769
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
765
|
-
| `--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,…` |
|
|
770
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT`, `--min-speed KBPS` and `--works-on google,reddit,…` |
|
|
766
771
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
767
772
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
768
773
|
| `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
|
|
@@ -663,6 +663,7 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
663
663
|
types: socks5
|
|
664
664
|
https: true
|
|
665
665
|
min-uptime: 90 # on the list for 90 %+ of the week
|
|
666
|
+
min-speed: 100 # optional: downloaded 100+ KB/s in the last check
|
|
666
667
|
works-on: google # optional: google, reddit, amazon
|
|
667
668
|
recheck: true # optional: check them again from the runner
|
|
668
669
|
- run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
|
|
@@ -675,8 +676,12 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
675
676
|
```bash
|
|
676
677
|
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
677
678
|
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
679
|
+
proxy-scraper --pick 5 --min-speed 200 # only ones that downloaded 200+ KB/s
|
|
678
680
|
```
|
|
679
681
|
|
|
682
|
+
"Fastest first" means the first answer plus the download speed measured in the last check – in a test with real
|
|
683
|
+
pages, the 25 fastest by download loaded four times as many pages as the 25 quickest to answer a tiny request.
|
|
684
|
+
|
|
680
685
|
**Without installing anything** – straight from the live list
|
|
681
686
|
|
|
682
687
|
```bash
|
|
@@ -721,7 +726,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
721
726
|
| `--discover` | search GitHub for new sources right now |
|
|
722
727
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
723
728
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
724
|
-
| `--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,…` |
|
|
729
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT`, `--min-speed KBPS` and `--works-on google,reddit,…` |
|
|
725
730
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
726
731
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
727
732
|
| `--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.18.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
|
|
@@ -704,6 +704,7 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
704
704
|
types: socks5
|
|
705
705
|
https: true
|
|
706
706
|
min-uptime: 90 # on the list for 90 %+ of the week
|
|
707
|
+
min-speed: 100 # optional: downloaded 100+ KB/s in the last check
|
|
707
708
|
works-on: google # optional: google, reddit, amazon
|
|
708
709
|
recheck: true # optional: check them again from the runner
|
|
709
710
|
- run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
|
|
@@ -716,8 +717,12 @@ pip download requests # git, pip, npm & co. now go through the pool
|
|
|
716
717
|
```bash
|
|
717
718
|
curl -x "$(proxy-scraper --pick --https-only --min-uptime 90)" https://api.ipify.org
|
|
718
719
|
proxy-scraper --pick 10 --country DE --works-on google > de.txt
|
|
720
|
+
proxy-scraper --pick 5 --min-speed 200 # only ones that downloaded 200+ KB/s
|
|
719
721
|
```
|
|
720
722
|
|
|
723
|
+
"Fastest first" means the first answer plus the download speed measured in the last check – in a test with real
|
|
724
|
+
pages, the 25 fastest by download loaded four times as many pages as the 25 quickest to answer a tiny request.
|
|
725
|
+
|
|
721
726
|
**Without installing anything** – straight from the live list
|
|
722
727
|
|
|
723
728
|
```bash
|
|
@@ -762,7 +767,7 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
|
|
|
762
767
|
| `--discover` | search GitHub for new sources right now |
|
|
763
768
|
| `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
|
|
764
769
|
| `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
|
|
765
|
-
| `--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,…` |
|
|
770
|
+
| `--pick [N]` | print N proxies (default 1) from the hourly checked list and exit – no scan, same filters, plus `--min-uptime PERCENT`, `--min-speed KBPS` and `--works-on google,reddit,…` |
|
|
766
771
|
| `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
|
|
767
772
|
| `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
|
|
768
773
|
| `--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.18.0"
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def __getattr__(name):
|
|
@@ -63,7 +63,7 @@ def main() -> int:
|
|
|
63
63
|
filters = {"types": _list("PS_TYPES") or ["http", "socks4", "socks5"],
|
|
64
64
|
"countries": os.environ.get("PS_COUNTRIES", "").strip(),
|
|
65
65
|
"https": _flag("PS_HTTPS"), "min_uptime": _number("PS_MIN_UPTIME", 0),
|
|
66
|
-
"works_on": _list("PS_WORKS_ON")}
|
|
66
|
+
"min_speed": _number("PS_MIN_SPEED", 0), "works_on": _list("PS_WORKS_ON")}
|
|
67
67
|
# a recheck starts from all of them – the limit applies to what still works from here
|
|
68
68
|
found = live_proxies(**filters, limit=0 if recheck else limit)
|
|
69
69
|
except (InputError, ValueError) as e:
|
|
@@ -75,7 +75,9 @@ def main() -> int:
|
|
|
75
75
|
urls = [p.url for p in found]
|
|
76
76
|
if recheck and urls:
|
|
77
77
|
print(f"Checking {len(urls)} proxies again from this runner …")
|
|
78
|
-
|
|
78
|
+
# in the live list's order: it's ranked by page load, the recheck only knows the first answer
|
|
79
|
+
working = {r.url for r in check_proxies(urls, https=filters["https"])}
|
|
80
|
+
urls = [u for u in urls if u in working]
|
|
79
81
|
if limit:
|
|
80
82
|
urls = urls[:limit]
|
|
81
83
|
# a file of its own per step: a job may use the action twice (socks5 in one step, http in the next)
|
|
@@ -429,7 +429,8 @@ def _as_result(r: dict) -> Optional[CheckResult]:
|
|
|
429
429
|
return CheckResult(make_key(r["ptype"], r["proxy"]), r["ptype"], r["proxy"], int(r.get("latency") or 0),
|
|
430
430
|
str(r.get("exit_ip") or ""), https=r.get("https"), anonymity=r.get("anonymity") or "",
|
|
431
431
|
country=r.get("country") or "", asn=int(r.get("asn") or 0), org=r.get("org") or "",
|
|
432
|
-
hosting=r.get("hosting"), blocklisted=r.get("blocklisted")
|
|
432
|
+
hosting=r.get("hosting"), blocklisted=r.get("blocklisted"),
|
|
433
|
+
speed_kbps=r["speed_kbps"] if type(r.get("speed_kbps")) is int else None)
|
|
433
434
|
except (KeyError, TypeError, ValueError):
|
|
434
435
|
return None
|
|
435
436
|
|
|
@@ -204,8 +204,9 @@ async def live_proxies_async(*, types: Iterable[str] = PROXY_TYPES, countries: I
|
|
|
204
204
|
anonymity: str = "", max_latency: int = 0, no_datacenter: bool = False,
|
|
205
205
|
no_blocklisted: bool = False, min_uptime: int = 0, works_on: Iterable[str] = (),
|
|
206
206
|
min_speed: int = 0, limit: int = 0) -> List[LiveProxy]:
|
|
207
|
-
"""The proxies from the hourly list that pass the filters, fastest first
|
|
208
|
-
worked from GitHub's servers in the last run (at most an hour
|
|
207
|
+
"""The proxies from the hourly list that pass the filters, fastest first – by first answer plus measured
|
|
208
|
+
download speed. Nothing is checked here: they worked from GitHub's servers in the last run (at most an hour
|
|
209
|
+
ago). find_proxies checks from your network.
|
|
209
210
|
|
|
210
211
|
Same filters as find_proxies, plus
|
|
211
212
|
min_uptime only proxies listed in at least this share (percent) of the week's runs, e.g. 90
|
|
@@ -215,6 +216,7 @@ async def live_proxies_async(*, types: Iterable[str] = PROXY_TYPES, countries: I
|
|
|
215
216
|
limit at most this many (0 = all)
|
|
216
217
|
"""
|
|
217
218
|
from .agent import AgentError, LiveSource
|
|
219
|
+
from .server.pool import page_ms, typical_speed
|
|
218
220
|
from .sites import SITE
|
|
219
221
|
|
|
220
222
|
types = _types(types)
|
|
@@ -237,9 +239,11 @@ async def live_proxies_async(*, types: Iterable[str] = PROXY_TYPES, countries: I
|
|
|
237
239
|
data = await LiveSource(fetch=_live_fetch).get()
|
|
238
240
|
except AgentError as e:
|
|
239
241
|
raise ConnectionError(str(e)) from None
|
|
240
|
-
found =
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
found = [_live_proxy(r, data.run_hours) for r in data.rows
|
|
243
|
+
if r.get("ptype") in types and isinstance(r.get("proxy"), str)]
|
|
244
|
+
# fastest first – by how quickly a page comes through when the list has download speeds (#186)
|
|
245
|
+
typical = typical_speed(found)
|
|
246
|
+
found.sort(key=lambda p: page_ms(p, typical))
|
|
243
247
|
found = [p for p in found if opts.filters.accepts(p) and (not min_uptime or (p.uptime_7d or 0) >= min_uptime)
|
|
244
248
|
and all(p.sites.get(s) for s in works_on) and (not min_speed or (p.speed_kbps or 0) >= min_speed)]
|
|
245
249
|
return found[:limit] if limit else found
|
|
@@ -93,6 +93,7 @@ class CheckResult:
|
|
|
93
93
|
org: str = ""
|
|
94
94
|
hosting: Optional[bool] = None # exit probably in a datacenter? None = unknown
|
|
95
95
|
blocklisted: Optional[bool] = None # exit IP on the SpamCop blocklist? None = unknown
|
|
96
|
+
speed_kbps: Optional[int] = None # download speed from the live list (KiB/s), None = not measured
|
|
96
97
|
|
|
97
98
|
@property
|
|
98
99
|
def url(self) -> str:
|
|
@@ -71,7 +71,8 @@ def pick(args) -> int:
|
|
|
71
71
|
found = live_proxies(types=args.types, countries=args.country or "", https=args.https_only,
|
|
72
72
|
anonymity=args.anonymity or "", max_latency=args.max_latency,
|
|
73
73
|
no_datacenter=args.no_datacenter, no_blocklisted=args.no_blocklisted,
|
|
74
|
-
min_uptime=args.min_uptime, works_on=args.works_on,
|
|
74
|
+
min_uptime=args.min_uptime, works_on=args.works_on, min_speed=args.min_speed,
|
|
75
|
+
limit=args.pick)
|
|
75
76
|
except (ConnectionError, ValueError) as e:
|
|
76
77
|
print(f"proxy-scraper: {e}", file=sys.stderr)
|
|
77
78
|
return 1
|
|
@@ -204,6 +205,8 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
|
204
205
|
"one URL per line, and exit – e.g. curl -x \"$(proxy-scraper --pick --https-only)\" …")
|
|
205
206
|
live.add_argument("--min-uptime", type=percent, default=0, metavar="PERCENT",
|
|
206
207
|
help="with --pick: only proxies on the list in at least this share of the week's checks")
|
|
208
|
+
live.add_argument("--min-speed", type=positive_int, default=0, metavar="KBPS",
|
|
209
|
+
help="with --pick: only proxies that downloaded at least this many KB/s in the last check")
|
|
207
210
|
live.add_argument("--works-on", type=site_list, default=[], metavar="SITES",
|
|
208
211
|
help=f"with --pick: only proxies that got through to these sites in the last check "
|
|
209
212
|
f"({', '.join(s.name for s in SITES)})")
|
|
@@ -251,8 +254,8 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
|
|
251
254
|
args = p.parse_args(argv)
|
|
252
255
|
if args.json and args.list_sources is None:
|
|
253
256
|
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")
|
|
257
|
+
if (args.min_uptime or args.works_on or args.min_speed) and args.pick is None:
|
|
258
|
+
p.error("--min-uptime, --min-speed and --works-on only work together with --pick")
|
|
256
259
|
if args.pick is not None: # --pick exits right away: options for a scan or the server would be silently ignored
|
|
257
260
|
scan_only = [flag for dest, flag in (("target", "--target"), ("serve", "--serve"), ("recheck", "--recheck"),
|
|
258
261
|
("want", "--want"), ("output", "-o/--output"), ("export", "--export"))
|
|
@@ -4,12 +4,16 @@ Selection per connection in three steps:
|
|
|
4
4
|
1. filters from the request (Selection): country, type – via the user name, e.g. "country-de-type-socks5"
|
|
5
5
|
2. sticky: the same session (or with --sticky the same target site) keeps the same proxy for a while
|
|
6
6
|
3. strategy for everything else: weighted (default), random, round-robin, fastest
|
|
7
|
+
|
|
8
|
+
"Fast" means how quickly a page comes through, not just the first answer: when the proxies come from the live list,
|
|
9
|
+
which measures a download per proxy every hour, that download counts too (#186).
|
|
7
10
|
"""
|
|
8
11
|
|
|
9
12
|
from __future__ import annotations
|
|
10
13
|
|
|
11
14
|
import random
|
|
12
15
|
import re
|
|
16
|
+
import statistics
|
|
13
17
|
import time
|
|
14
18
|
from dataclasses import dataclass
|
|
15
19
|
from typing import Callable, Dict, List, Optional, Set, Tuple
|
|
@@ -20,6 +24,25 @@ DISABLE_AFTER = 3 # this many failures in a row -> out of the rotation
|
|
|
20
24
|
STRATEGIES = ("weighted", "random", "round-robin", "fastest")
|
|
21
25
|
SESSION_SECONDS = 600 # how long a session (session-…) keeps its proxy when --sticky isn't set
|
|
22
26
|
_TOKEN_RE = re.compile(r"(country|type|session)[-_]([A-Za-z0-9]+)")
|
|
27
|
+
PAGE_KB = 100 # roughly one page – the download the speed step measures
|
|
28
|
+
UNMEASURED_KBPS = 20 # tried in the speed step, no download got through: counted as slow (it passed the checks)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def page_ms(result, typical_kbps: Optional[float]) -> float:
|
|
32
|
+
"""Roughly how long a page takes through this proxy: the first answer plus the download.
|
|
33
|
+
|
|
34
|
+
typical_kbps is the median measured speed of the set, None when it has no speeds at all (an own scan) – then
|
|
35
|
+
latency decides alone. The speed step only tries HTTPS-capable proxies: one of those without a speed didn't
|
|
36
|
+
get the download through, the others were never tried and count as typical."""
|
|
37
|
+
if not typical_kbps:
|
|
38
|
+
return result.latency
|
|
39
|
+
kbps = result.speed_kbps or (UNMEASURED_KBPS if result.https else typical_kbps)
|
|
40
|
+
return result.latency + PAGE_KB * 1000 / kbps
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def typical_speed(results) -> Optional[float]:
|
|
44
|
+
speeds = [r.speed_kbps for r in results if r.speed_kbps]
|
|
45
|
+
return statistics.median(speeds) if speeds else None
|
|
23
46
|
|
|
24
47
|
|
|
25
48
|
@dataclass(frozen=True)
|
|
@@ -57,12 +80,17 @@ class PoolEntry:
|
|
|
57
80
|
fail_streak: int = 0
|
|
58
81
|
active: int = 0
|
|
59
82
|
disabled: bool = False
|
|
83
|
+
typical_kbps: Optional[float] = None # set by the pool: median measured speed of its proxies
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def cost(self) -> float:
|
|
87
|
+
return page_ms(self.result, self.typical_kbps)
|
|
60
88
|
|
|
61
89
|
@property
|
|
62
90
|
def weight(self) -> float:
|
|
63
91
|
# prefer fast and proven ones, but give everyone a chance
|
|
64
92
|
reliability = (self.ok + 1) / (self.ok + self.fail + 2)
|
|
65
|
-
return reliability / (self.
|
|
93
|
+
return reliability / (self.cost + 300)
|
|
66
94
|
|
|
67
95
|
|
|
68
96
|
class ProxyPool:
|
|
@@ -71,7 +99,8 @@ class ProxyPool:
|
|
|
71
99
|
clock: Callable[[], float] = time.monotonic, strict_tls: bool = False):
|
|
72
100
|
if strategy not in STRATEGIES:
|
|
73
101
|
raise ValueError(f"unknown strategy {strategy!r} (possible: {', '.join(STRATEGIES)})")
|
|
74
|
-
|
|
102
|
+
typical = typical_speed(results)
|
|
103
|
+
self.entries = sorted((PoolEntry(r, typical_kbps=typical) for r in results), key=lambda e: e.cost)
|
|
75
104
|
self.rng = rng or random.Random()
|
|
76
105
|
self.strategy = strategy
|
|
77
106
|
self.sticky_seconds = sticky_seconds
|
|
@@ -135,8 +164,8 @@ class ProxyPool:
|
|
|
135
164
|
# the fastest free one; if all are busy, the least busy one (then the faster one)
|
|
136
165
|
idle = [e for e in candidates if not e.active]
|
|
137
166
|
if idle:
|
|
138
|
-
return min(idle, key=lambda e: (e.
|
|
139
|
-
return min(candidates, key=lambda e: (e.active, e.
|
|
167
|
+
return min(idle, key=lambda e: (e.cost, -e.weight))
|
|
168
|
+
return min(candidates, key=lambda e: (e.active, e.cost))
|
|
140
169
|
if self.strategy == "round-robin":
|
|
141
170
|
ordered = sorted(candidates, key=lambda e: e.result.key)
|
|
142
171
|
entry = ordered[self._next % len(ordered)]
|
|
@@ -186,7 +215,10 @@ class ProxyPool:
|
|
|
186
215
|
for r in fresh.values():
|
|
187
216
|
kept.append(PoolEntry(r))
|
|
188
217
|
added += 1
|
|
189
|
-
|
|
218
|
+
typical = typical_speed([e.result for e in kept])
|
|
219
|
+
for entry in kept:
|
|
220
|
+
entry.typical_kbps = typical
|
|
221
|
+
self.entries = sorted(kept, key=lambda e: e.cost)
|
|
190
222
|
return added
|
|
191
223
|
|
|
192
224
|
def revive(self, entry: PoolEntry) -> None:
|
|
@@ -11,7 +11,7 @@ def fake(n, **extra):
|
|
|
11
11
|
for i in range(n)]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def run(monkeypatch, tmp_path, env, found=3, rechecked=None):
|
|
14
|
+
def run(monkeypatch, tmp_path, env, found=3, rechecked=None, recheck=None):
|
|
15
15
|
calls = {}
|
|
16
16
|
|
|
17
17
|
def live(**kwargs):
|
|
@@ -20,7 +20,7 @@ def run(monkeypatch, tmp_path, env, found=3, rechecked=None):
|
|
|
20
20
|
|
|
21
21
|
def check(urls, **kwargs):
|
|
22
22
|
calls["check"] = list(urls)
|
|
23
|
-
return fake(found)[:rechecked]
|
|
23
|
+
return recheck(found) if recheck else fake(found)[:rechecked]
|
|
24
24
|
monkeypatch.setattr(action, "live_proxies", live)
|
|
25
25
|
monkeypatch.setattr(action, "check_proxies", check)
|
|
26
26
|
out = tmp_path / "out.txt"
|
|
@@ -36,10 +36,10 @@ def run(monkeypatch, tmp_path, env, found=3, rechecked=None):
|
|
|
36
36
|
def test_filters_reach_live_proxies_and_outputs_are_set(monkeypatch, tmp_path):
|
|
37
37
|
code, out, calls = run(monkeypatch, tmp_path, {
|
|
38
38
|
"PS_TYPES": "socks5, http", "PS_COUNTRIES": "de,nl", "PS_HTTPS": "true", "PS_MIN_UPTIME": "90",
|
|
39
|
-
"PS_WORKS_ON": "google", "PS_LIMIT": "2"})
|
|
39
|
+
"PS_MIN_SPEED": "100", "PS_WORKS_ON": "google", "PS_LIMIT": "2"})
|
|
40
40
|
assert code == 0
|
|
41
41
|
assert calls["live"] == {"types": ["socks5", "http"], "countries": "de,nl", "https": True, "min_uptime": 90,
|
|
42
|
-
"works_on": ["google"], "limit": 2}
|
|
42
|
+
"min_speed": 100, "works_on": ["google"], "limit": 2}
|
|
43
43
|
assert out["proxy"] == "socks5://1.1.1.0:1080" and out["count"] == "2" # limit
|
|
44
44
|
file = out["file"]
|
|
45
45
|
assert file.startswith(str(tmp_path)) and Path(file).read_text().splitlines()[0] == "socks5://1.1.1.0:1080"
|
|
@@ -67,3 +67,12 @@ def test_nothing_found_fails_the_step_unless_allowed(monkeypatch, tmp_path, caps
|
|
|
67
67
|
def test_bad_inputs_are_explained(monkeypatch, tmp_path, capsys):
|
|
68
68
|
code, _, _ = run(monkeypatch, tmp_path, {"PS_LIMIT": "lots"})
|
|
69
69
|
assert code == 1 and "limit" in capsys.readouterr().out
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_a_recheck_keeps_the_live_lists_order(monkeypatch, tmp_path):
|
|
74
|
+
# the live list comes ranked by page load; the recheck's latencies say nothing about downloads
|
|
75
|
+
def slower_first(n): # from the runner, the list's first proxy happens to answer slowest
|
|
76
|
+
return [CheckResult(r.key, r.ptype, r.proxy, 500 - r.latency, r.exit_ip, True) for r in fake(n)]
|
|
77
|
+
_, out, _ = run(monkeypatch, tmp_path, {"PS_RECHECK": "true"}, recheck=slower_first)
|
|
78
|
+
assert out["proxy"] == "socks5://1.1.1.0:1080"
|
|
@@ -42,3 +42,10 @@ def test_options_for_a_scan_are_an_error_not_ignored(extra, capsys):
|
|
|
42
42
|
with pytest.raises(SystemExit):
|
|
43
43
|
cli.run(["--pick", *extra])
|
|
44
44
|
assert "--pick" in capsys.readouterr().err
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_min_speed_keeps_only_proxies_that_downloaded_that_fast(monkeypatch, capsys):
|
|
48
|
+
rows = [dict(ROWS[0], speed_kbps=300), dict(ROWS[2], speed_kbps=40), ROWS[1]]
|
|
49
|
+
monkeypatch.setattr(api, "_live_fetch", fetch_from({"proxies.json": rows, "stats.json": STATS}))
|
|
50
|
+
assert cli.run(["--pick", "5", "--min-speed", "100"]) == 0
|
|
51
|
+
assert capsys.readouterr().out == "socks5://1.1.1.1:1080\n"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Download speed in the choice of proxy (#186): a quick answer to one tiny request says little about loading a
|
|
2
|
+
page – in a test on the live list, the 25 proxies with the best latency loaded 8 of 50 pages, the 25 that downloaded
|
|
3
|
+
fastest in the last run 33 of 50."""
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from proxyscraper import api, live_proxies
|
|
8
|
+
from proxyscraper.agent import _as_result
|
|
9
|
+
from proxyscraper.checker import CheckResult
|
|
10
|
+
from proxyscraper.server import ProxyPool
|
|
11
|
+
from proxyscraper.server.pool import page_ms
|
|
12
|
+
|
|
13
|
+
from .test_live_api import ROWS, STATS, fetch_from
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def result(n, latency, speed=None, https=True):
|
|
17
|
+
proxy = f"10.0.0.{n}:80"
|
|
18
|
+
return CheckResult(f"http {proxy}", "http", proxy, latency, "9.9.9.9", https=https, speed_kbps=speed)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_a_proxy_that_downloads_fast_beats_one_that_only_answers_fast():
|
|
22
|
+
quick_ping, fast_download = result(1, 100), result(2, 400, speed=500)
|
|
23
|
+
assert page_ms(fast_download, typical_kbps=100) < page_ms(quick_ping, typical_kbps=100)
|
|
24
|
+
pool = ProxyPool([quick_ping, fast_download], strategy="fastest")
|
|
25
|
+
assert pool.pick(set()).result is fast_download
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_the_weighted_choice_leans_to_the_fast_downloader():
|
|
29
|
+
pool = ProxyPool([result(1, 100), result(2, 400, speed=500)], rng=random.Random(1))
|
|
30
|
+
picks = [pool.pick(set()).result.proxy for _ in range(1000)]
|
|
31
|
+
assert picks.count("10.0.0.2:80") > 700
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_without_any_speed_data_latency_decides_as_before():
|
|
35
|
+
pool = ProxyPool([result(1, 400), result(2, 100)], strategy="fastest")
|
|
36
|
+
assert pool.pick(set()).result.latency == 100
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_the_live_list_brings_its_speed_into_the_pool():
|
|
40
|
+
row = {"ptype": "http", "proxy": "1.2.3.4:80", "latency": 100, "https": True, "speed_kbps": 250}
|
|
41
|
+
assert _as_result(row).speed_kbps == 250
|
|
42
|
+
assert _as_result({**row, "speed_kbps": "fast"}).speed_kbps is None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_live_proxies_put_the_measured_fast_ones_first(monkeypatch):
|
|
46
|
+
rows = [dict(r) for r in ROWS]
|
|
47
|
+
rows[0]["speed_kbps"] = 400 # socks5, latency 300 – the slowest to answer, the only one that downloaded
|
|
48
|
+
monkeypatch.setattr(api, "_live_fetch", fetch_from({"proxies.json": rows, "stats.json": STATS}))
|
|
49
|
+
# the socks4 one answers faster but got no download through in the speed step
|
|
50
|
+
assert [p.url for p in live_proxies(https=True)] == ["socks5://1.1.1.1:1080", "socks4://3.3.3.3:4145"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_proxies_the_speed_step_never_tried_count_as_typical_not_slow():
|
|
54
|
+
# it only measures HTTPS-capable ones – a plain-HTTP-only proxy wasn't slow, it just wasn't measured
|
|
55
|
+
plain_only, measured = result(1, 50, https=False), result(2, 900, speed=100)
|
|
56
|
+
pool = ProxyPool([plain_only, measured], strategy="fastest")
|
|
57
|
+
assert pool.pick(set()).result is plain_only
|
|
58
|
+
assert pool.pick(set(), tls=True).result is measured
|
|
File without changes
|
|
File without changes
|
{proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxy_scraper_cli.egg-info/requires.txt
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.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
|
{proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.0}/proxyscraper/site/apple-touch-icon.png
RENAMED
|
File without changes
|
{proxy_scraper_cli-1.17.0 → proxy_scraper_cli-1.18.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
|
|
File without changes
|
|
File without changes
|