Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 31 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,10 @@ PORT = 5000 # 监听端口
DB_CONN = 'redis://:pwd@127.0.0.1:8888/0'


# 配置 ProxyFetcher

PROXY_FETCHER = [
"freeProxy01", # 这里是启用的代理抓取方法名,所有fetch方法位于fetcher/proxyFetcher.py
"freeProxy02",
# ....
]
# 配置代理源(可选)
# 默认自动扫描 fetcher/sources/ 目录下所有 enabled=True 的代理源
# 如需禁用某些代理源,在黑名单中添加其 name 即可
# PROXY_FETCHER_EXCLUDE = ["freevpnnode"]
```

#### 启动项目:
Expand Down Expand Up @@ -167,59 +164,48 @@ def getHtml():

  添加一个新的代理源方法如下:

* 1、首先在[ProxyFetcher](https://github.com/jhao104/proxy_pool/blob/1a3666283806a22ef287fba1a8efab7b94e94bac/fetcher/proxyFetcher.py#L21)类中添加自定义的获取代理的静态方法,
该方法需要以生成器(yield)形式返回`host:ip`格式的代理,例如:
* 1、在 `fetcher/sources/` 目录下新建 `.py` 文件,继承 `BaseFetcher` 基类,声明 `name`/`url`/`enabled` 属性,实现 `fetch()` 方法以生成器(yield)形式返回`host:port`格式的代理,例如:

```python
from fetcher.baseFetcher import BaseFetcher
from util.webRequest import WebRequest

class ProxyFetcher(object):
# ....
class MyProxyFetcher(BaseFetcher):
"""我的代理源"""

# 自定义代理源获取方法
@staticmethod
def freeProxyCustom1(): # 命名不和已有重复即可
name = "myproxy"
url = "https://www.example.com/"
enabled = True

# 通过某网站或者某接口或某数据库获取代理
# 假设你已经拿到了一个代理列表
proxies = ["x.x.x.x:3128", "x.x.x.x:80"]
for proxy in proxies:
yield proxy
# 确保每个proxy都是 host:ip正确的格式返回
def fetch(self):
r = WebRequest().get("https://www.example.com/api/proxies")
for item in r.json:
yield item["ip"] + ":" + item["port"]
```

* 2、添加好方法后,修改[setting.py](https://github.com/jhao104/proxy_pool/blob/1a3666283806a22ef287fba1a8efab7b94e94bac/setting.py#L47)文件中的`PROXY_FETCHER`项:

  在`PROXY_FETCHER`下添加自定义方法的名字:

```python
PROXY_FETCHER = [
"freeProxy01",
"freeProxy02",
# ....
"freeProxyCustom1" # # 确保名字和你添加方法名字一致
]
```
* 2、添加好后,`schedule` 进程下次抓取时会自动扫描 `fetcher/sources/` 目录并启用新代理源,无需修改配置。

  可用 `python proxyPool.py fetcher` 命令查看当前启用的代理源列表。

  `schedule` 进程会每隔一段时间抓取一次代理,下次抓取时会自动识别调用你定义的方法
  如需临时禁用某个代理源,在 [setting.py](setting.py) 的 `PROXY_FETCHER_EXCLUDE` 黑名单中添加其 `name` 即可

### 免费代理源

目前实现的采集免费代理网站有(排名不分先后, 下面仅是对其发布的免费代理情况, 付费代理测评可以参考[这里](https://zhuanlan.zhihu.com/p/33576641)):
目前实现的采集免费代理网站有(排名不分先后, 下面仅是对其发布的免费代理情况, 付费代理测评可以参考[这里](https://zhuanlan.zhihu.com/p/33576641)):

| 代理名称 | 状态 | 更新速度 | 可用率 | 地址 | 代码 |
|---------------| ---- | -------- | ------ | ----- |------------------------------------------------|
| 66代理 | ✔ | ★ | * | [地址](http://www.66ip.cn/) | [`freeProxy02`](/fetcher/proxyFetcher.py#L50) |
| 开心代理 | ✔ | ★ | * | [地址](http://www.kxdaili.com/) | [`freeProxy03`](/fetcher/proxyFetcher.py#L63) |
| FreeProxyList | ✔ | ★ | * | [地址](https://www.freeproxylists.net/zh/) | [`freeProxy04`](/fetcher/proxyFetcher.py#L74) |
| 快代理 | ✔ | ★ | * | [地址](https://www.kuaidaili.com/) | [`freeProxy05`](/fetcher/proxyFetcher.py#L92) |
| 冰凌代理 | ✔ | ★★★ | * | [地址](https://www.binglx.cn/) | [`freeProxy06`](/fetcher/proxyFetcher.py#L111) |
| 云代理 | ✔ | ★ | * | [地址](http://www.ip3366.net/) | [`freeProxy07`](/fetcher/proxyFetcher.py#L123) |
| 小幻代理 | ✔ | ★★ | * | [地址](https://ip.ihuan.me/) | [`freeProxy08`](/fetcher/proxyFetcher.py#L133) |
| 免费代理库 | ✔ | ☆ | * | [地址](http://ip.jiangxianli.com/) | [`freeProxy09`](/fetcher/proxyFetcher.py#L143) |
| 89代理 | ✔ | | * | [地址](https://www.89ip.cn/) | [`freeProxy10`](/fetcher/proxyFetcher.py#L154) |
| 稻壳代理 | ✔ | ★★ | *** | [地址](https://www.docip.ne) | [`freeProxy11`](/fetcher/proxyFetcher.py#L164) |
| 谷德代理 | ✔ | ★★ | *** | [地址](https://www.goodips.com) | [`freeProxy12`](/fetcher/proxyFetcher.py#L174) |
| 66代理 | ✔ | ★ | * | [地址](http://www.66ip.cn/) | [`ip66.py`](/fetcher/sources/ip66.py) |
| 开心代理 | ✔ | ★ | * | [地址](http://www.kxdaili.com/) | [`kxdaili.py`](/fetcher/sources/kxdaili.py) |
| FreeProxyList | ✔ | ★ | * | [地址](https://www.freeproxylists.net/zh/) | [`freeproxylist.py`](/fetcher/sources/freeproxylist.py) |
| 快代理 | ✔ | ★ | * | [地址](https://www.kuaidaili.com/) | [`kuaidaili.py`](/fetcher/sources/kuaidaili.py) |
| 云代理 | ✔ | ★ | * | [地址](http://www.ip3366.net/) | [`ip3366.py`](/fetcher/sources/ip3366.py) |
| 小幻代理 | ✔ | ★ | * | [地址](https://ip.ihuan.me/) | [`ihuan.py`](/fetcher/sources/ihuan.py) |
| 免费代理库 | ✔ | | * | [地址](http://ip.jiangxianli.com/) | [`jiangxianli.py`](/fetcher/sources/jiangxianli.py) |
| 89代理 | ✔ | ☆ | * | [地址](https://www.89ip.cn/) | [`ip89.py`](/fetcher/sources/ip89.py) |
| 稻壳代理 | ✔ | ★★ | *** | [地址](https://www.docip.ne) | [`docip.py`](/fetcher/sources/docip.py) |
| 谷德代理 | ✔ | ★★ | *** | [地址](https://www.goodips.com) | [`goodips.py`](/fetcher/sources/goodips.py) |
| Proxifly | ✔ | ★★ | ** | [地址](https://proxifly.dev) | [`proxifly.py`](/fetcher/sources/proxifly.py) |


如果还有其他好的免费代理网站, 可以在提交在[issues](https://github.com/jhao104/proxy_pool/issues/71), 下次更新时会考虑在项目中支持。
Expand Down
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- 自动扫描启用的代理源,无需在配置中列举
- 新增 `PROXY_FETCHER_EXCLUDE` 黑名单配置
- 新增 `proxyPool.py fetcher` 命令查看启用的代理源
8. 新增代理源 **Proxifly**; (2026-06-01)


## 2.4.2 (2024-01-18)
Expand Down
41 changes: 0 additions & 41 deletions fetcher/sources/binglx.py

This file was deleted.

42 changes: 42 additions & 0 deletions fetcher/sources/proxifly.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
File Name: proxifly.py
Description : Proxifly代理源
Author : JHao
date: 2026/06/01
-------------------------------------------------
Change Activity:
2026/06/01:
-------------------------------------------------
"""
__author__ = 'JHao'

from fetcher.baseFetcher import BaseFetcher
from handler.logHandler import LogHandler
from util.webRequest import WebRequest

logger = LogHandler("fetcher")


class ProxiFlyFetcher(BaseFetcher):
"""Proxifly https://proxifly.dev"""

name = "proxifly"
url = "https://proxifly.dev/"

enabled = True # 是否启用

def fetch(self):
r = WebRequest().get("https://cdn.jsdelivr.net/gh/proxifly/free-proxy-list@main/proxies/all/data.json", timeout=10)
try:
for each in r.json:
if each.get("geolocation", {}).get("country", "") == "CN" and each.get("protocol") == "http":
yield self.parseProxiesFromText(each.get('proxy', "")).pop()
except Exception as e:
logger.error("ProxyFetch - proxifly: %s" % e)


if __name__ == '__main__':
for proxy in ProxiFlyFetcher().fetch():
print(proxy)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ APScheduler==3.10.0;python_version>="3.10"
APScheduler==3.2.0;python_version<"3.10"
click==8.0.1
Flask==2.1.1
werkzeug==2.1.0
werkzeug==3.1.6
54 changes: 40 additions & 14 deletions tests/unit/test_fetcher_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ class TestFetcherInterface(object):
FETCHER_CLASSES = [
("fetcher.sources.ip66", "Ip66Fetcher"),
("fetcher.sources.kxdaili", "KxdailiFetcher"),
("fetcher.sources.binglx", "BinglxFetcher"),
("fetcher.sources.ip3366", "Ip3366Fetcher"),
("fetcher.sources.jiangxianli", "JiangxianliFetcher"),
("fetcher.sources.ip89", "Ip89Fetcher"),
Expand All @@ -70,6 +69,7 @@ class TestFetcherInterface(object):
("fetcher.sources.scdn", "ScdnFetcher"),
("fetcher.sources.zdaye", "ZdayeFetcher"),
("fetcher.sources.ihuan", "IhuanFetcher"),
("fetcher.sources.proxifly", "ProxiFlyFetcher"),
]

def test_all_fetchers_have_name_url_enabled(self):
Expand Down Expand Up @@ -123,19 +123,6 @@ def test_fetch(self, mock_wr):
assert "1.2.3.4:8080" in result


class TestBinglxFetcher(object):

@patch("fetcher.sources.binglx.WebRequest")
def test_fetch(self, mock_wr):
from fetcher.sources.binglx import BinglxFetcher
# binglx 使用 proxy_list[1:] 跳过第一行,需要 header 行
html = _html_table([("IP", "Port"), ("1.2.3.4", "8080")])
tree = etree.HTML(html)
mock_wr.return_value.get.return_value = _make_response(tree=tree)
result = list(BinglxFetcher().fetch())
assert "1.2.3.4:8080" in result


class TestIp3366Fetcher(object):

@patch("fetcher.sources.ip3366.WebRequest")
Expand Down Expand Up @@ -369,3 +356,42 @@ def test_fetch_no_key_returns_empty(self, mock_wr):
mock_wr.return_value.get.return_value = ti_resp
result = list(IhuanFetcher().fetch())
assert result == []


class TestProxiFlyFetcher(object):

@patch("fetcher.sources.proxifly.WebRequest")
def test_fetch(self, mock_wr):
from fetcher.sources.proxifly import ProxiFlyFetcher
json_data = [
{"proxy": "1.2.3.4:8080", "protocol": "http", "geolocation": {"country": "CN"}},
{"proxy": "5.6.7.8:3128", "protocol": "http", "geolocation": {"country": "CN"}},
]
mock_wr.return_value.get.return_value = _make_response(json_data=json_data)
result = list(ProxiFlyFetcher().fetch())
assert "1.2.3.4:8080" in result
assert "5.6.7.8:3128" in result

@patch("fetcher.sources.proxifly.WebRequest")
def test_fetch_filters_non_cn(self, mock_wr):
from fetcher.sources.proxifly import ProxiFlyFetcher
json_data = [
{"proxy": "1.2.3.4:8080", "protocol": "http", "geolocation": {"country": "CN"}},
{"proxy": "9.9.9.9:8080", "protocol": "http", "geolocation": {"country": "US"}},
]
mock_wr.return_value.get.return_value = _make_response(json_data=json_data)
result = list(ProxiFlyFetcher().fetch())
assert "1.2.3.4:8080" in result
assert "9.9.9.9:8080" not in result

@patch("fetcher.sources.proxifly.WebRequest")
def test_fetch_filters_non_http(self, mock_wr):
from fetcher.sources.proxifly import ProxiFlyFetcher
json_data = [
{"proxy": "1.2.3.4:8080", "protocol": "http", "geolocation": {"country": "CN"}},
{"proxy": "9.9.9.9:8080", "protocol": "https", "geolocation": {"country": "CN"}},
]
mock_wr.return_value.get.return_value = _make_response(json_data=json_data)
result = list(ProxiFlyFetcher().fetch())
assert "1.2.3.4:8080" in result
assert "9.9.9.9:8080" not in result
Loading