需求前因:
我们写了一个抓取Telegram社交账号群聊信息的数据爬虫,每个爬虫脚本都使用了一个Telegram账号,如果多个账号使用同一个ip进行抓取,自然会封号,就gg了,所以我们需要多个Socks5代理,但是想了想很不划算,谷歌上搜了一下,一个代理一个月竟然要80$,富人爆金币,穷人靠变异。
设想思路:
我自己经常用的Socks5节点是本地127.0.0.1:7890来自我的ssr付费节点Clash客户端订阅实现,但是他只能选择一个节点,想切换IP需要手动选择节点,依然在7890上。
所以,我的订阅有50个节点,可以让每个节点都启动在本地不同端口上吗?于是有了这个话
调研抄作业:
OK,有了思路就开干,但是咱确实没干过,谷歌搜了一下,不靠谱。但是在Youtobe上看到一个博主,实现了这个技术:
里面提到了,将订阅的内容进行转换,然后利用V2rayN的自定义配置服务器功能来实现。
实现步骤:
在Clash中点击编辑:
查看Config内容
然后复制内容,去在线转换,但是这里就不用博主用的那个转换网址了,我发现他的在线是上传的,会在他服务器保存,所以我 借鉴 了他的js代码部分,来本地写了一个(核心代码附在文末)
于是得到了一个yaml文件
把他放在V2rayN同目录,然后打开V2rayN,选择第一个 添加自定义配置服务器,然后选择这个文件,注意socks5端口随意写,但是不要跟配置文件生成的端口号冲突。
然后右键设为活动服务器
到这里,V2rayN就配置完成了,但是怎么测试是否有效呢,
1.使用浏览器代理配置一些端口测试,或者使用代理端口,不过我觉得挺费劲的。所以写了个python自动脚本测试。
实现效果:
批量测试代理可用性思路
- 脚本批量链接代理地址 socks5://127.0.0.1:{端口起-端口止}
- 连接后请求一个接口获取本地IP,也就是说这个接口会返回请求者的IP,也就是咱们的隧道代理的节点IP,然后打印出来
- 统计可用的端口数,和得到的内容数IP去重,得到IP数量(有些无良节点商一个IP设置两个节点)
代码实现:
设置的IP接口为 https://cloudflare-cdn.make-everything.pics/ip.php
<?php
if ($HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"])
{
$ip = $HTTP_SERVER_VARS["HTTP_X_FORWARDED_FOR"];
}
elseif ($HTTP_SERVER_VARS["HTTP_CLIENT_IP"])
{
$ip = $HTTP_SERVER_VARS["HTTP_CLIENT_IP"];
}
elseif ($HTTP_SERVER_VARS["REMOTE_ADDR"])
{
$ip = $HTTP_SERVER_VARS["REMOTE_ADDR"];
}
elseif (getenv("HTTP_X_FORWARDED_FOR"))
{
$ip = getenv("HTTP_X_FORWARDED_FOR");
}
elseif (getenv("HTTP_CLIENT_IP"))
{
$ip = getenv("HTTP_CLIENT_IP");
}
elseif (getenv("REMOTE_ADDR"))
{
$ip = getenv("REMOTE_ADDR");
}
else
{
$ip = "Unknown";
}
echo $ip ;
?>
Python实现批量链接Socks5代理并获取IP内容
import requests
import socks
# 设置代理地址和端口范围
proxy_base_url = "socks5://127.0.0.1:"
proxy_ports = range(16000, 16051)
# 目标URL
target_url = "https://cloudflare-cdn.make-everything.pics/ip.php"
# 初始化可用端口和内容集合
available_ports = set()
unique_contents = set()
# 批量链接代理并获取内容
for port in proxy_ports:
proxy_url = f"{proxy_base_url}{port}"
# 设置代理
proxies = {
'http': proxy_url,
'https': proxy_url
}
# 使用requests发送GET请求
try:
response = requests.get(target_url, proxies=proxies, timeout=5)
content = response.text
# 记录可用端口
available_ports.add(port)
# 记录内容,去重
unique_contents.add(content)
print(f"{proxy_url} 》》》 {content}")
except requests.exceptions.RequestException as e:
print(f"{proxy_url} 》》》 (该端口链接失败)")
# 打印结果
print(f"\n共 {len(available_ports)} 个可用端口")
print(f"去重后共 {len(unique_contents)} 个可用IP")
SSR节点转本地Socks5多端口的html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>节点配置转换工具</title>
<!-- 引入 jQuery -->
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<!-- 引入 js-yaml -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.0.0/js-yaml.min.js"></script>
<!-- 引入 Bootstrap 4 CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h1 class="mb-4">节点配置转换工具</h1>
<div class="alert alert-info" role="alert">
<strong>相关内容:</strong>
<ul>
<li><a href="https://github.com/2dust/v2rayN/releases/tag/6.23" target="_blank">v2rayN代理工具</a> - https://github.com/2dust/v2rayN/releases/tag/6.23</li>
<li><a href="https://bulianglin.com/archives/51.html" target="_blank">本地订阅转换工具</a> - https://bulianglin.com/archives/51.html</li>
<li><a href="https://youtu.be/01F8xUxqmkY" target="_blank">视频教程</a> - https://youtu.be/01F8xUxqmkY</li>
</ul>
</div>
<div class="form-group">
<label for="inputYAML">输入节点配置信息:</label>
<textarea class="form-control" id="inputYAML" rows="10" placeholder="在这里输入节点配置信息"></textarea>
</div>
<div class="form-group">
<label for="startPort">起始端口号:</label>
<input type="number" class="form-control" id="startPort" value="11000" />
</div>
<button class="btn btn-primary" id="processButton">生成配置文件</button>
<div class="mt-3" id="infoDiv"></div>
<div class="mt-3" id="outputDiv">
<h4>生成成功后的内容:</h4>
<textarea class="form-control" id="outputYAML" rows="10" readonly></textarea>
</div>
</div>
<!-- 引入 Bootstrap 4 JS 和 Popper.js -->
<script src="https://code.jquery.com/jquery-3.6.4.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#processButton').click(function () {
const inputYAML = $('#inputYAML').val();
const startPort = parseInt($('#startPort').val());
const infoDiv = $('#infoDiv');
const outputDiv = $('#outputDiv');
try {
const yamlData = jsyaml.load(inputYAML);
const numProxies = yamlData.proxies.length;
const newYAML = {
'allow-lan': true,
dns: {
enable: true,
'enhanced-mode': 'fake-ip',
'fake-ip-range': '198.18.0.1/16',
'default-nameserver': ['114.114.114.114'],
nameserver: ['https://doh.pub/dns-query']
},
listeners: [],
proxies: yamlData.proxies
};
newYAML.listeners = Array.from({ length: numProxies }, (_, i) => ({
name: `mixed${i}`,
type: 'mixed',
port: startPort + i,
proxy: yamlData.proxies[i].name
}));
const newYAMLString = jsyaml.dump(newYAML);
// 显示生成成功后的内容
$('#outputYAML').val(newYAMLString);
const blob = new Blob([newYAMLString], { type: 'text/yaml' });
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = 'config.yaml';
downloadLink.textContent = '点击此处下载生成的节点文件';
// 清空原有内容
infoDiv.html("");
outputDiv.html("");
// 将生成的内容追加到 #outputDiv
outputDiv.append("<h4>生成成功:</h4>");
outputDiv.append(downloadLink);
outputDiv.append("<textarea class='form-control' id='outputYAML' rows='10' readonly>" + newYAMLString + "</textarea>");
infoDiv.html(`起始端口:${startPort},结束端口:${startPort + numProxies - 1},共发现了${numProxies}个节点,生成了${numProxies}个端口`);
} catch (error) {
infoDiv.html('发生异常,请确认文件格式正确,或尝试刷新页面重试!');
}
});
});
</script>
</body>
</html>