proxy-scraper-cli 1.8.1__tar.gz → 1.10.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.
Files changed (113) hide show
  1. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/PKG-INFO +110 -12
  2. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/README.md +108 -11
  3. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/PKG-INFO +110 -12
  4. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/SOURCES.txt +9 -0
  5. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/requires.txt +1 -0
  6. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/__init__.py +1 -1
  7. proxy_scraper_cli-1.10.0/proxyscraper/action.py +96 -0
  8. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/cli.py +20 -5
  9. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/completion.py +61 -3
  10. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/exporters.py +43 -0
  11. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/pages.py +7 -4
  12. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/parsing.py +13 -8
  13. proxy_scraper_cli-1.10.0/proxyscraper/proxypages.py +241 -0
  14. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/publish.py +15 -1
  15. proxy_scraper_cli-1.10.0/proxyscraper/report.py +266 -0
  16. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/core.py +23 -11
  17. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/site/index.html +163 -17
  18. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/sites.py +45 -6
  19. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/sources.py +3 -1
  20. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/pyproject.toml +1 -1
  21. proxy_scraper_cli-1.10.0/tests/test_action.py +69 -0
  22. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_completion.py +35 -0
  23. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_exporters.py +22 -0
  24. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_mine_sources.py +8 -0
  25. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_parsing.py +7 -5
  26. proxy_scraper_cli-1.10.0/tests/test_parsing_fuzz.py +83 -0
  27. proxy_scraper_cli-1.10.0/tests/test_proxy_pages.py +93 -0
  28. proxy_scraper_cli-1.10.0/tests/test_report.py +119 -0
  29. proxy_scraper_cli-1.10.0/tests/test_server_closed_writer.py +72 -0
  30. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_sites.py +31 -1
  31. proxy_scraper_cli-1.10.0/tests/test_sources_json.py +36 -0
  32. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_uptime.py +12 -0
  33. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/LICENSE +0 -0
  34. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/dependency_links.txt +0 -0
  35. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/entry_points.txt +0 -0
  36. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxy_scraper_cli.egg-info/top_level.txt +0 -0
  37. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/__main__.py +0 -0
  38. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/agent.py +0 -0
  39. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/api.py +0 -0
  40. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/app.py +0 -0
  41. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/asndb.py +0 -0
  42. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/blocklist.py +0 -0
  43. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/charts.py +0 -0
  44. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/checker.py +0 -0
  45. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/compat.py +0 -0
  46. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/fetchcache.py +0 -0
  47. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/geo.py +0 -0
  48. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/geodb.py +0 -0
  49. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/handshake.py +0 -0
  50. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/history.py +0 -0
  51. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/judges.py +0 -0
  52. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/mcp_entry.py +0 -0
  53. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/mcp_server.py +0 -0
  54. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/netio.py +0 -0
  55. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/options.py +0 -0
  56. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/output.py +0 -0
  57. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/paths.py +0 -0
  58. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/pipeline.py +0 -0
  59. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/preferences.py +0 -0
  60. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/__init__.py +0 -0
  61. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/http.py +0 -0
  62. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/pool.py +0 -0
  63. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/socks.py +0 -0
  64. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/status.py +0 -0
  65. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/server/upstream.py +0 -0
  66. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/site/apple-touch-icon.png +0 -0
  67. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/site/googleaac1161b7853c5b5.html +0 -0
  68. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/site/logo.png +0 -0
  69. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/site/og.png +0 -0
  70. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/sources.json +0 -0
  71. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/targets.py +0 -0
  72. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/__init__.py +0 -0
  73. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/dashboard.py +0 -0
  74. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/keys.py +0 -0
  75. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/report.py +0 -0
  76. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/serve.py +0 -0
  77. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/widgets.py +0 -0
  78. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/proxyscraper/ui/wizard.py +0 -0
  79. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/setup.cfg +0 -0
  80. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_agent.py +0 -0
  81. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_api.py +0 -0
  82. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_app.py +0 -0
  83. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_asndb.py +0 -0
  84. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_auth.py +0 -0
  85. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_blocklist.py +0 -0
  86. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_charts.py +0 -0
  87. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_checker.py +0 -0
  88. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_compat.py +0 -0
  89. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_discovery_growth.py +0 -0
  90. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_fetchcache.py +0 -0
  91. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_geodb.py +0 -0
  92. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_judges.py +0 -0
  93. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_learning.py +0 -0
  94. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_live_api.py +0 -0
  95. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_mcp_server.py +0 -0
  96. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_options.py +0 -0
  97. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_output_stdout.py +0 -0
  98. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_packaging.py +0 -0
  99. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_pages.py +0 -0
  100. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_paths.py +0 -0
  101. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_pipeline.py +0 -0
  102. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_proxy_sources.py +0 -0
  103. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_publish.py +0 -0
  104. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_robustness.py +0 -0
  105. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_serve_password.py +0 -0
  106. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_serve_refill.py +0 -0
  107. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_serve_v2.py +0 -0
  108. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_server.py +0 -0
  109. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_server_fixes.py +0 -0
  110. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_tamper.py +0 -0
  111. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_targets.py +0 -0
  112. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.0}/tests/test_ui.py +0 -0
  113. {proxy_scraper_cli-1.8.1 → proxy_scraper_cli-1.10.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.8.1
3
+ Version: 1.10.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
@@ -35,6 +35,7 @@ Provides-Extra: mcp
35
35
  Requires-Dist: mcp<3,>=2.2; python_version >= "3.10" and extra == "mcp"
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: pytest; extra == "dev"
38
+ Requires-Dist: hypothesis; extra == "dev"
38
39
  Requires-Dist: ruff==0.16.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
@@ -138,11 +139,12 @@ python3 proxy_scraper.py
138
139
 
139
140
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
140
141
 
141
- Tab completion for bash, zsh and fish:
142
+ Tab completion for bash, zsh, fish and PowerShell:
142
143
 
143
144
  ```bash
144
145
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
145
146
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
147
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
146
148
  ```
147
149
 
148
150
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -578,14 +580,92 @@ for p in proxies:
578
580
  continue # free proxies come and go – just take the next one
579
581
  ```
580
582
 
581
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
583
+ **httpx** (`pip install "httpx[socks]"`) – straight from the hourly list, no scan
584
+
585
+ ```python
586
+ import httpx
587
+ from proxyscraper import live_proxies
588
+
589
+ for p in live_proxies(types=["http", "socks5"], https=True, min_uptime=90, limit=10): # httpx can't do SOCKS4
590
+ try:
591
+ with httpx.Client(proxy=p.url, timeout=10) as client:
592
+ print(p.url, "→", client.get("https://api.ipify.org").text)
593
+ break
594
+ except httpx.HTTPError:
595
+ continue # next one
596
+ ```
597
+
598
+ **aiohttp** (`pip install aiohttp aiohttp-socks` – aiohttp alone can't do SOCKS)
599
+
600
+ ```python
601
+ import asyncio
602
+
603
+ import aiohttp
604
+ from aiohttp_socks import ProxyConnector, ProxyError
605
+ from proxyscraper import live_proxies_async
606
+
607
+
608
+ async def main():
609
+ for p in await live_proxies_async(https=True, min_uptime=90, limit=10):
610
+ try:
611
+ async with aiohttp.ClientSession(connector=ProxyConnector.from_url(p.url)) as session:
612
+ async with session.get("https://api.ipify.org", timeout=aiohttp.ClientTimeout(total=10)) as r:
613
+ print(p.url, "→", await r.text())
614
+ return
615
+ except (aiohttp.ClientError, ProxyError, asyncio.TimeoutError, OSError):
616
+ continue # free proxies come and go – just take the next one
617
+
618
+ asyncio.run(main())
619
+ ```
620
+
621
+ **Scrapy** – a downloader middleware that sends every request, retries included, through the next proxy. Scrapy only speaks HTTP proxies, `https=True` picks the ones that can tunnel `https://` pages
622
+
623
+ ```python
624
+ import itertools
625
+
626
+ import scrapy
627
+ from scrapy.crawler import CrawlerProcess
628
+ from proxyscraper import live_proxies
629
+
630
+ PROXIES = [p.url for p in live_proxies(types="http", https=True, min_uptime=50)]
631
+ if not PROXIES:
632
+ raise SystemExit("No proxy matches right now – loosen the filters (e.g. min_uptime)")
633
+ POOL = itertools.cycle(PROXIES)
634
+
635
+
636
+ class RotatingProxy:
637
+ def process_request(self, request, spider=None): # newer Scrapy leaves out spider
638
+ request.meta["proxy"] = next(POOL)
639
+
640
+
641
+ class IpSpider(scrapy.Spider):
642
+ name = "ip"
643
+ start_urls = [f"https://api.ipify.org/?n={i}" for i in range(3)]
644
+ custom_settings = {
645
+ "DOWNLOADER_MIDDLEWARES": {f"{__name__}.RotatingProxy": 350},
646
+ "RETRY_TIMES": 5, "DOWNLOAD_TIMEOUT": 15,
647
+ }
648
+
649
+ def parse(self, response):
650
+ print(response.meta["proxy"], "→", response.text)
651
+
652
+
653
+ if __name__ == "__main__":
654
+ process = CrawlerProcess()
655
+ process.crawl(IpSpider)
656
+ process.start()
657
+ ```
658
+
659
+ In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
660
+
661
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
582
662
 
583
663
  ```bash
584
- proxy-scraper --want 30 -y --export proxychains,clash
664
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
585
665
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
586
666
  ```
587
667
 
588
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
668
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
589
669
 
590
670
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
591
671
 
@@ -601,6 +681,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
601
681
  pip download requests # git, pip, npm & co. now go through the pool
602
682
  ```
603
683
 
684
+ **In a GitHub workflow** – the action picks working proxies for the next steps
685
+
686
+ ```yaml
687
+ - id: proxies
688
+ uses: maximilianfeix/proxy-scraper@v1
689
+ with:
690
+ types: socks5
691
+ https: true
692
+ min-uptime: 90 # on the list for 90 %+ of the week
693
+ works-on: google # optional: google, reddit, amazon
694
+ recheck: true # optional: check them again from the runner
695
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
696
+ ```
697
+
698
+ `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`.
699
+
604
700
  **Without installing anything** – straight from the live list
605
701
 
606
702
  ```bash
@@ -644,16 +740,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
644
740
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
645
741
  | `--discover` | search GitHub for new sources right now |
646
742
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
647
- | `--list-sources [N]` | show the source ranking |
743
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
648
744
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
649
745
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
650
746
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
651
747
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
652
748
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
653
749
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
654
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
750
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
655
751
  | `-V`, `--version` | print the version |
656
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
752
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
657
753
 
658
754
  Everything else: `proxy-scraper --help`
659
755
 
@@ -735,10 +831,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
735
831
 
736
832
  ## Roadmap
737
833
 
738
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
834
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
835
+
836
+ - [ ] [Measure throughput, not just latency](../../issues/140)
837
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
739
838
 
740
- - [ ] [Protocol detection on the same connection](../../issues/44)
741
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
839
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
742
840
 
743
841
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
744
842
 
@@ -783,7 +881,7 @@ proxyscraper/
783
881
  ├── geodb.py DB-IP country database (monthly, binary search)
784
882
  ├── targets.py target sites for --target
785
883
  ├── output.py result files
786
- ├── exporters.py proxychains, Clash and curl formats (--export)
884
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
787
885
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
788
886
  ├── api.py find_proxies() / check_proxies() for Python
789
887
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -98,11 +98,12 @@ python3 proxy_scraper.py
98
98
 
99
99
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
100
100
 
101
- Tab completion for bash, zsh and fish:
101
+ Tab completion for bash, zsh, fish and PowerShell:
102
102
 
103
103
  ```bash
104
104
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
105
105
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
106
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
106
107
  ```
107
108
 
108
109
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -538,14 +539,92 @@ for p in proxies:
538
539
  continue # free proxies come and go – just take the next one
539
540
  ```
540
541
 
541
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
542
+ **httpx** (`pip install "httpx[socks]"`) – straight from the hourly list, no scan
543
+
544
+ ```python
545
+ import httpx
546
+ from proxyscraper import live_proxies
547
+
548
+ for p in live_proxies(types=["http", "socks5"], https=True, min_uptime=90, limit=10): # httpx can't do SOCKS4
549
+ try:
550
+ with httpx.Client(proxy=p.url, timeout=10) as client:
551
+ print(p.url, "→", client.get("https://api.ipify.org").text)
552
+ break
553
+ except httpx.HTTPError:
554
+ continue # next one
555
+ ```
556
+
557
+ **aiohttp** (`pip install aiohttp aiohttp-socks` – aiohttp alone can't do SOCKS)
558
+
559
+ ```python
560
+ import asyncio
561
+
562
+ import aiohttp
563
+ from aiohttp_socks import ProxyConnector, ProxyError
564
+ from proxyscraper import live_proxies_async
565
+
566
+
567
+ async def main():
568
+ for p in await live_proxies_async(https=True, min_uptime=90, limit=10):
569
+ try:
570
+ async with aiohttp.ClientSession(connector=ProxyConnector.from_url(p.url)) as session:
571
+ async with session.get("https://api.ipify.org", timeout=aiohttp.ClientTimeout(total=10)) as r:
572
+ print(p.url, "→", await r.text())
573
+ return
574
+ except (aiohttp.ClientError, ProxyError, asyncio.TimeoutError, OSError):
575
+ continue # free proxies come and go – just take the next one
576
+
577
+ asyncio.run(main())
578
+ ```
579
+
580
+ **Scrapy** – a downloader middleware that sends every request, retries included, through the next proxy. Scrapy only speaks HTTP proxies, `https=True` picks the ones that can tunnel `https://` pages
581
+
582
+ ```python
583
+ import itertools
584
+
585
+ import scrapy
586
+ from scrapy.crawler import CrawlerProcess
587
+ from proxyscraper import live_proxies
588
+
589
+ PROXIES = [p.url for p in live_proxies(types="http", https=True, min_uptime=50)]
590
+ if not PROXIES:
591
+ raise SystemExit("No proxy matches right now – loosen the filters (e.g. min_uptime)")
592
+ POOL = itertools.cycle(PROXIES)
593
+
594
+
595
+ class RotatingProxy:
596
+ def process_request(self, request, spider=None): # newer Scrapy leaves out spider
597
+ request.meta["proxy"] = next(POOL)
598
+
599
+
600
+ class IpSpider(scrapy.Spider):
601
+ name = "ip"
602
+ start_urls = [f"https://api.ipify.org/?n={i}" for i in range(3)]
603
+ custom_settings = {
604
+ "DOWNLOADER_MIDDLEWARES": {f"{__name__}.RotatingProxy": 350},
605
+ "RETRY_TIMES": 5, "DOWNLOAD_TIMEOUT": 15,
606
+ }
607
+
608
+ def parse(self, response):
609
+ print(response.meta["proxy"], "→", response.text)
610
+
611
+
612
+ if __name__ == "__main__":
613
+ process = CrawlerProcess()
614
+ process.crawl(IpSpider)
615
+ process.start()
616
+ ```
617
+
618
+ In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
619
+
620
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
542
621
 
543
622
  ```bash
544
- proxy-scraper --want 30 -y --export proxychains,clash
623
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
545
624
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
546
625
  ```
547
626
 
548
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
627
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
549
628
 
550
629
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
551
630
 
@@ -561,6 +640,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
561
640
  pip download requests # git, pip, npm & co. now go through the pool
562
641
  ```
563
642
 
643
+ **In a GitHub workflow** – the action picks working proxies for the next steps
644
+
645
+ ```yaml
646
+ - id: proxies
647
+ uses: maximilianfeix/proxy-scraper@v1
648
+ with:
649
+ types: socks5
650
+ https: true
651
+ min-uptime: 90 # on the list for 90 %+ of the week
652
+ works-on: google # optional: google, reddit, amazon
653
+ recheck: true # optional: check them again from the runner
654
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
655
+ ```
656
+
657
+ `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`.
658
+
564
659
  **Without installing anything** – straight from the live list
565
660
 
566
661
  ```bash
@@ -604,16 +699,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
604
699
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
605
700
  | `--discover` | search GitHub for new sources right now |
606
701
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
607
- | `--list-sources [N]` | show the source ranking |
702
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
608
703
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
609
704
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
610
705
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
611
706
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
612
707
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
613
708
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
614
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
709
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
615
710
  | `-V`, `--version` | print the version |
616
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
711
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
617
712
 
618
713
  Everything else: `proxy-scraper --help`
619
714
 
@@ -695,10 +790,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
695
790
 
696
791
  ## Roadmap
697
792
 
698
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
793
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
794
+
795
+ - [ ] [Measure throughput, not just latency](../../issues/140)
796
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
699
797
 
700
- - [ ] [Protocol detection on the same connection](../../issues/44)
701
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
798
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
702
799
 
703
800
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
704
801
 
@@ -743,7 +840,7 @@ proxyscraper/
743
840
  ├── geodb.py DB-IP country database (monthly, binary search)
744
841
  ├── targets.py target sites for --target
745
842
  ├── output.py result files
746
- ├── exporters.py proxychains, Clash and curl formats (--export)
843
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
747
844
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
748
845
  ├── api.py find_proxies() / check_proxies() for Python
749
846
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxy-scraper-cli
3
- Version: 1.8.1
3
+ Version: 1.10.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
@@ -35,6 +35,7 @@ Provides-Extra: mcp
35
35
  Requires-Dist: mcp<3,>=2.2; python_version >= "3.10" and extra == "mcp"
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: pytest; extra == "dev"
38
+ Requires-Dist: hypothesis; extra == "dev"
38
39
  Requires-Dist: ruff==0.16.8; extra == "dev"
39
40
  Dynamic: license-file
40
41
 
@@ -138,11 +139,12 @@ python3 proxy_scraper.py
138
139
 
139
140
  Every [release](https://github.com/maximilianfeix/proxy-scraper/releases/latest) also ships a wheel you can install with `pip install <file>.whl`, and a multi-arch image (amd64/arm64) on `ghcr.io`. In the container the wizard never shows up, it runs straight away. For the proxy server use `--serve --serve-host 0.0.0.0` with `-p 127.0.0.1:8899:8899`, so the port is only open on your own machine.
140
141
 
141
- Tab completion for bash, zsh and fish:
142
+ Tab completion for bash, zsh, fish and PowerShell:
142
143
 
143
144
  ```bash
144
145
  eval "$(proxy-scraper --completion zsh)" # in ~/.zshrc (after compinit), same for bash in ~/.bashrc
145
146
  proxy-scraper --completion fish > ~/.config/fish/completions/proxy-scraper.fish
147
+ proxy-scraper --completion powershell | Out-String | Invoke-Expression # in $PROFILE
146
148
  ```
147
149
 
148
150
  Installed, the learned state lives in your user data folder (`~/Library/Application Support/proxy-scraper`, `%LOCALAPPDATA%\proxy-scraper` or `~/.local/share/proxy-scraper`; override with `PROXY_SCRAPER_HOME`) and results go to `./results`. Run from a clone, both stay inside the project.
@@ -578,14 +580,92 @@ for p in proxies:
578
580
  continue # free proxies come and go – just take the next one
579
581
  ```
580
582
 
581
- **proxychains, Clash / Mihomo** – ready-made configs with `--export`
583
+ **httpx** (`pip install "httpx[socks]"`) – straight from the hourly list, no scan
584
+
585
+ ```python
586
+ import httpx
587
+ from proxyscraper import live_proxies
588
+
589
+ for p in live_proxies(types=["http", "socks5"], https=True, min_uptime=90, limit=10): # httpx can't do SOCKS4
590
+ try:
591
+ with httpx.Client(proxy=p.url, timeout=10) as client:
592
+ print(p.url, "→", client.get("https://api.ipify.org").text)
593
+ break
594
+ except httpx.HTTPError:
595
+ continue # next one
596
+ ```
597
+
598
+ **aiohttp** (`pip install aiohttp aiohttp-socks` – aiohttp alone can't do SOCKS)
599
+
600
+ ```python
601
+ import asyncio
602
+
603
+ import aiohttp
604
+ from aiohttp_socks import ProxyConnector, ProxyError
605
+ from proxyscraper import live_proxies_async
606
+
607
+
608
+ async def main():
609
+ for p in await live_proxies_async(https=True, min_uptime=90, limit=10):
610
+ try:
611
+ async with aiohttp.ClientSession(connector=ProxyConnector.from_url(p.url)) as session:
612
+ async with session.get("https://api.ipify.org", timeout=aiohttp.ClientTimeout(total=10)) as r:
613
+ print(p.url, "→", await r.text())
614
+ return
615
+ except (aiohttp.ClientError, ProxyError, asyncio.TimeoutError, OSError):
616
+ continue # free proxies come and go – just take the next one
617
+
618
+ asyncio.run(main())
619
+ ```
620
+
621
+ **Scrapy** – a downloader middleware that sends every request, retries included, through the next proxy. Scrapy only speaks HTTP proxies, `https=True` picks the ones that can tunnel `https://` pages
622
+
623
+ ```python
624
+ import itertools
625
+
626
+ import scrapy
627
+ from scrapy.crawler import CrawlerProcess
628
+ from proxyscraper import live_proxies
629
+
630
+ PROXIES = [p.url for p in live_proxies(types="http", https=True, min_uptime=50)]
631
+ if not PROXIES:
632
+ raise SystemExit("No proxy matches right now – loosen the filters (e.g. min_uptime)")
633
+ POOL = itertools.cycle(PROXIES)
634
+
635
+
636
+ class RotatingProxy:
637
+ def process_request(self, request, spider=None): # newer Scrapy leaves out spider
638
+ request.meta["proxy"] = next(POOL)
639
+
640
+
641
+ class IpSpider(scrapy.Spider):
642
+ name = "ip"
643
+ start_urls = [f"https://api.ipify.org/?n={i}" for i in range(3)]
644
+ custom_settings = {
645
+ "DOWNLOADER_MIDDLEWARES": {f"{__name__}.RotatingProxy": 350},
646
+ "RETRY_TIMES": 5, "DOWNLOAD_TIMEOUT": 15,
647
+ }
648
+
649
+ def parse(self, response):
650
+ print(response.meta["proxy"], "→", response.text)
651
+
652
+
653
+ if __name__ == "__main__":
654
+ process = CrawlerProcess()
655
+ process.crawl(IpSpider)
656
+ process.start()
657
+ ```
658
+
659
+ In a Scrapy project, put `RotatingProxy` in `middlewares.py` and add it to `DOWNLOADER_MIDDLEWARES` in `settings.py`. For long crawls, reload the pool now and then – the list changes every hour.
660
+
661
+ **proxychains, Clash / Mihomo, sing-box** – ready-made configs with `--export`
582
662
 
583
663
  ```bash
584
- proxy-scraper --want 30 -y --export proxychains,clash
664
+ proxy-scraper --want 30 -y --export proxychains,clash,singbox
585
665
  proxychains4 -f results/latest/proxychains.conf curl https://api.ipify.org
586
666
  ```
587
667
 
588
- `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. Both configs leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
668
+ `clash.yaml` has all HTTP and SOCKS5 proxies plus a `url-test` group that always picks the fastest. `singbox.json` does the same for sing-box, SOCKS4 included, and opens a local proxy: `sing-box run -c results/latest/singbox.json`, then use `127.0.0.1:2080` as HTTP or SOCKS5 proxy. All three leave out HTTP proxies that can't tunnel (`CONNECT`), because these tools tunnel everything.
589
669
 
590
670
  **In a pipe** – `-o -` prints the hits to stdout, the interface moves to stderr
591
671
 
@@ -601,6 +681,22 @@ export HTTPS_PROXY=http://127.0.0.1:8899 HTTP_PROXY=http://127.0.0.1:8899
601
681
  pip download requests # git, pip, npm & co. now go through the pool
602
682
  ```
603
683
 
684
+ **In a GitHub workflow** – the action picks working proxies for the next steps
685
+
686
+ ```yaml
687
+ - id: proxies
688
+ uses: maximilianfeix/proxy-scraper@v1
689
+ with:
690
+ types: socks5
691
+ https: true
692
+ min-uptime: 90 # on the list for 90 %+ of the week
693
+ works-on: google # optional: google, reddit, amazon
694
+ recheck: true # optional: check them again from the runner
695
+ - run: curl -x "${{ steps.proxies.outputs.proxy }}" https://api.ipify.org
696
+ ```
697
+
698
+ `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`.
699
+
604
700
  **Without installing anything** – straight from the live list
605
701
 
606
702
  ```bash
@@ -644,16 +740,16 @@ curl.exe -x (Get-Content "$run\all.txt" -TotalCount 1) http://api.ipify.org
644
740
  | `-t`, `--timeout S` | timeout per proxy (default: 8 s) |
645
741
  | `--discover` | search GitHub for new sources right now |
646
742
  | `--no-cache` | download every list again (unchanged ones are normally skipped via ETag) |
647
- | `--list-sources [N]` | show the source ranking |
743
+ | `--list-sources [N]` | show the source ranking (add `--json` for scripts: url, status, hit rate, checks, last change) |
648
744
  | `--serve-host ADDR` | where the proxy server listens (default `127.0.0.1`; `0.0.0.0` for Docker, with a warning) |
649
745
  | `--serve-password SECRET` | clients must send this password in the proxy login; better set `PROXY_SCRAPER_SERVE_PASSWORD` |
650
746
  | `--rotate STRATEGY` · `--sticky SEC` | how the proxy server picks proxies, see [above](#proxy-server) |
651
747
  | `--serve-refill HOURS` | while serving, check fresh proxies every HOURS and add the hits to the pool |
652
748
  | `--serve [PORT]` | afterwards serve as a rotating proxy on `127.0.0.1:PORT` (default: 8899) |
653
749
  | `-o FILE` | also write all hits to this file; `-o -` prints them to stdout |
654
- | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `curl` or `all` |
750
+ | `--export FORMATS` | extra files for other tools: `proxychains`, `clash`, `singbox`, `curl` or `all` |
655
751
  | `-V`, `--version` | print the version |
656
- | `--completion SHELL` | print the tab completion script for bash, zsh or fish |
752
+ | `--completion SHELL` | print the tab completion script for bash, zsh, fish or PowerShell |
657
753
 
658
754
  Everything else: `proxy-scraper --help`
659
755
 
@@ -735,10 +831,12 @@ Only for things that don't matter. Public proxies are run by strangers who can r
735
831
 
736
832
  ## Roadmap
737
833
 
738
- What's next is tracked in the milestone [**v1.8**](../../milestone/8) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
834
+ What's next is in the [open issues](../../issues) – ideas and wishes are welcome as an [issue](../../issues/new/choose). During Hacktoberfest there are [beginner-friendly issues](../../issues?q=is%3Aopen+label%3Ahacktoberfest) with pointers on where to start.
835
+
836
+ - [ ] [Measure throughput, not just latency](../../issues/140)
837
+ - [ ] [Daily snapshots as a dataset](../../issues/143)
739
838
 
740
- - [ ] [Protocol detection on the same connection](../../issues/44)
741
- - [ ] [sing-box export](../../issues/113), [PowerShell completion](../../issues/114), [shareable filters on the website](../../issues/116)
839
+ Shipped in v1.8 and v1.9: uptime per proxy and `stable.txt`, which proxies get through to Google, Reddit and Amazon, `live_proxies()` in Python, a page per proxy with its week of checks, a weekly report, live charts in this README, shareable filters on the website, sources mined from other scrapers' lists, untyped lists tried as HTTP and SOCKS5, `--export singbox`, PowerShell completion and recipes for httpx, aiohttp and Scrapy.
742
840
 
743
841
  Shipped in [v1.7](../../milestone/7): an MCP server so AI agents get working proxies and can load pages through them, 28 new sources and a GitHub search that runs daily and keeps what it found, and a cleaner website.
744
842
 
@@ -783,7 +881,7 @@ proxyscraper/
783
881
  ├── geodb.py DB-IP country database (monthly, binary search)
784
882
  ├── targets.py target sites for --target
785
883
  ├── output.py result files
786
- ├── exporters.py proxychains, Clash and curl formats (--export)
884
+ ├── exporters.py proxychains, Clash, sing-box and curl formats (--export)
787
885
  ├── server/ rotating proxy server (--serve): pool · http · upstream · socks · status · core
788
886
  ├── api.py find_proxies() / check_proxies() for Python
789
887
  ├── agent.py MCP tools without the SDK: live list, filters, fetch through proxies
@@ -9,6 +9,7 @@ proxy_scraper_cli.egg-info/requires.txt
9
9
  proxy_scraper_cli.egg-info/top_level.txt
10
10
  proxyscraper/__init__.py
11
11
  proxyscraper/__main__.py
12
+ proxyscraper/action.py
12
13
  proxyscraper/agent.py
13
14
  proxyscraper/api.py
14
15
  proxyscraper/app.py
@@ -36,7 +37,9 @@ proxyscraper/parsing.py
36
37
  proxyscraper/paths.py
37
38
  proxyscraper/pipeline.py
38
39
  proxyscraper/preferences.py
40
+ proxyscraper/proxypages.py
39
41
  proxyscraper/publish.py
42
+ proxyscraper/report.py
40
43
  proxyscraper/sites.py
41
44
  proxyscraper/sources.json
42
45
  proxyscraper/sources.py
@@ -60,6 +63,7 @@ proxyscraper/ui/report.py
60
63
  proxyscraper/ui/serve.py
61
64
  proxyscraper/ui/widgets.py
62
65
  proxyscraper/ui/wizard.py
66
+ tests/test_action.py
63
67
  tests/test_agent.py
64
68
  tests/test_api.py
65
69
  tests/test_app.py
@@ -84,17 +88,22 @@ tests/test_output_stdout.py
84
88
  tests/test_packaging.py
85
89
  tests/test_pages.py
86
90
  tests/test_parsing.py
91
+ tests/test_parsing_fuzz.py
87
92
  tests/test_paths.py
88
93
  tests/test_pipeline.py
94
+ tests/test_proxy_pages.py
89
95
  tests/test_proxy_sources.py
90
96
  tests/test_publish.py
97
+ tests/test_report.py
91
98
  tests/test_robustness.py
92
99
  tests/test_serve_password.py
93
100
  tests/test_serve_refill.py
94
101
  tests/test_serve_v2.py
95
102
  tests/test_server.py
103
+ tests/test_server_closed_writer.py
96
104
  tests/test_server_fixes.py
97
105
  tests/test_sites.py
106
+ tests/test_sources_json.py
98
107
  tests/test_tamper.py
99
108
  tests/test_targets.py
100
109
  tests/test_ui.py
@@ -3,6 +3,7 @@ certifi
3
3
 
4
4
  [dev]
5
5
  pytest
6
+ hypothesis
6
7
  ruff==0.16.8
7
8
 
8
9
  [fast]
@@ -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.8.1"
4
+ __version__ = "1.10.0"
5
5
 
6
6
 
7
7
  def __getattr__(name):