You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地轮转代理隧道脚本超时故障排查及定制需求求助

问题描述

我是编程新手,现在需要做一个可通过IP:Port访问的单代理,用于爬虫绕过Cloudflare防火墙,能直接在浏览器里配置。要求每次页面刷新时,把请求原封不动转发到从列表随机选的外部代理,列表里的代理不需要登录验证。

我擅长PHP但实现不了这个需求,作为纯Python新手,拼凑了下面的脚本:

import socket
import socketserver
import select
import itertools

# proxylist
PROXY_LIST = ['xxx.xxx.xxx.220:3128', 'xxx.xxx.xxx.22:3128', 'xxx.xxx.xxx.105:3128']
proxy_pool = itertools.cycle(PROXY_LIST)

class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

class HTTPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(4096).strip()
        proxy = next(proxy_pool)
        hostname, port = proxy.split(':') 
        remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        remote.connect(("xxx.xxx.xxx.22", 3128))  # for testing a static proxy, not from above list
        remote.send(data)

        inputs = [self.request, remote]
        while True:
            readable, writable, exceptional = select.select(inputs, [], inputs, 60)
            if not readable and not writable and not exceptional:
                break

            for s in readable:
                if s is remote:
                    out = self.request
                else:
                    out = remote
                data = s.recv(4096)
                if data:
                    out.send(data)

            for s in exceptional:
                inputs.remove(s)
                s.close()

        remote.close()
        self.request.close()

if __name__ == "__main__":
    with ThreadingTCPServer(('localhost', 3128), HTTPRequestHandler) as server:
        server.serve_forever()

脚本启动无报错,能连接到本地代理(wget输出显示),但连接后没响应直到超时。已经在关闭防火墙的Windows和Debian系统测试过,外部代理本身是正常可用的。测试命令:

wget -v google.de -e use_proxy=yes -e http_proxy=127.0.0.1:3128

终止脚本时的报错:

Exception occurred during processing of request from ('127.0.0.1', 56714)
Traceback (most recent call last):
  File "xxx\Python\Python311\Lib\socketserver.py", line 691, in process_request_thread
    self.finish_request(request, client_address)
  File "xxx\Python\Python311\Lib\socketserver.py", line 361, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "xxx\Python\Python311\Lib\socketserver.py", line 755, in __init__
    self.handle()
  File "xxx\Test\test.py", line 36, in handle
    data = s.recv(4096)
           ^^^^^^^^^^^^

需要排查问题,或者有没有现成的简单隧道代理脚本实现轮转转发,另外后续要加MySQL查询功能,随机从数据库取代理,脚本要可定制。


问题排查与解决方案

核心问题分析

  • 请求格式被破坏:初始data = self.request.recv(4096).strip()用了strip(),会删除HTTP请求头的换行、空格等关键格式符,导致外部代理无法识别合法请求,直接丢弃或不响应。
  • 随机代理未生效:代码里remote.connect(("xxx.xxx.xxx.22", 3128))是硬编码的固定代理,完全没用到随机选取的代理(即使是测试也不符合需求)。
  • Socket转发逻辑缺陷:未处理recv()返回空字节(表示对方连接关闭)的情况,导致循环一直阻塞直到超时;异常处理时直接修改inputs列表,会引发迭代错误。

修正后的代码

import socket
import socketserver
import select
import random

# 代理列表
PROXY_LIST = ['xxx.xxx.xxx.220:3128', 'xxx.xxx.xxx.22:3128', 'xxx.xxx.xxx.105:3128']

class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    # 允许地址重用,避免重启脚本时端口占用报错
    allow_reuse_address = True

class HTTPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # 接收原始请求数据,禁止修改格式
        data = self.request.recv(4096)
        if not data:
            self.request.close()
            return

        # 随机选择一个代理
        proxy = random.choice(PROXY_LIST)
        hostname, port = proxy.split(':')
        port = int(port)

        remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            remote.connect((hostname, port))
            # 用sendall确保所有数据发送完成
            remote.sendall(data)

            inputs = [self.request, remote]
            while True:
                readable, _, exceptional = select.select(inputs, [], inputs, 60)
                if not readable and not exceptional:
                    break

                # 迭代副本避免修改列表导致的错误
                for s in readable.copy():
                    recv_data = s.recv(4096)
                    if not recv_data:
                        # 对方关闭连接,清理资源
                        inputs.remove(s)
                        s.close()
                        continue
                    # 转发数据到对应socket
                    if s is remote:
                        self.request.sendall(recv_data)
                    else:
                        remote.sendall(recv_data)

                # 处理异常socket
                for s in exceptional.copy():
                    inputs.remove(s)
                    s.close()

        except Exception as e:
            print(f"代理转发出错: {e}")
        finally:
            remote.close()
            self.request.close()

if __name__ == "__main__":
    # 监听所有网卡,允许外部设备访问
    with ThreadingTCPServer(('0.0.0.0', 3128), HTTPRequestHandler) as server:
        print(f"代理服务器启动,监听地址: 0.0.0.0:3128")
        server.serve_forever()

代码说明

  1. 随机代理选择:用random.choice()每次随机选取代理,完全符合"每次刷新随机切换"的需求。
  2. 保留原始请求:去掉strip(),确保HTTP请求格式完整,外部代理能正确解析。
  3. 完善Socket处理:
    • 检查recv()返回空的情况,及时关闭连接并退出循环。
    • 迭代时使用列表副本,避免修改原列表引发的迭代错误。
    • 用sendall()替代send(),确保所有请求数据都发送完成。
  4. 实用性优化:添加地址重用、监听所有网卡的配置,提升脚本稳定性和可用性。

后续扩展MySQL支持

要实现从MySQL随机获取代理,只需修改代理获取逻辑:

  1. 安装依赖:pip install pymysql
  2. 添加MySQL查询函数:
import pymysql

def get_random_proxy_from_db():
    db_config = {
        'host': '你的数据库地址',
        'user': '数据库用户名',
        'password': '数据库密码',
        'database': '数据库名'
    }
    conn = pymysql.connect(**db_config)
    try:
        with conn.cursor() as cursor:
            # 假设表名为proxies,is_active标记可用代理,ip_port存储代理地址
            cursor.execute("SELECT ip_port FROM proxies WHERE is_active = 1 ORDER BY RAND() LIMIT 1")
            result = cursor.fetchone()
            if result:
                return result[0]
            else:
                # 数据库无可用代理时, fallback到本地列表
                return random.choice(PROXY_LIST)
    finally:
        conn.close()
  1. 在handle()函数中替换代理获取代码:
proxy = get_random_proxy_from_db()

现成替代方案

如果不想维护底层Socket代码,可以用更成熟的工具快速实现:

  • mitmproxy:支持脚本扩展,可轻松实现请求转发和代理轮转,适配爬虫场景。
  • aiohttp:异步HTTP框架,编写代理服务器更简洁,性能更高。

内容的提问来源于stack exchange,提问作者wwworm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 00:24:52