ProxyPool 代理校验器扩展指南:读懂内置三类校验,十分钟写出自己的代理验证器
【免费下载链接】proxy_poolPython ProxyPool for web spider项目地址: https://gitcode.com/gh_mirrors/pr/proxy_pool
ProxyPool 项目(Python ProxyPool for web spider)使用helper/validator.py集中管理代理校验逻辑,通过装饰器将校验方法分为预校验、HTTP 可用性校验与 HTTPS 支持校验三类。本文以 validator.md 为主干,结合 helper/validator.py、helper/check.py、helper/fetch.py 等源码与 tests/unit/test_validator.py 测试用例,完整讲解内置校验的注册机制、执行顺序与判定规则,并给出可直接落地的自定义校验器写法。读完本文,你将掌握"校验器如何被调度、如何被判定、如何自定义"的完整链路,能够针对自己的业务场景(如校验匿名度、验证指定站点可达性、识别透明代理等)快速扩展 ProxyPool 的代理校验能力。
一、校验器整体设计:装饰器注册,集中管理
代理校验方法全部定义在helper/validator.py中,核心是ProxyValidator类(继承自withMetaclass(Singleton),即单例模式),它维护三个类级列表并对外暴露三个装饰器:
| 校验类型 | 装饰器 | 存储列表 | 说明 |
|---|---|---|---|
preValidator | @ProxyValidator.addPreValidator | pre_validator | 预校验,在代理抓取后、可用性校验前调用 |
httpValidator | @ProxyValidator.addHttpValidator | http_validator | 代理可用性校验,通过则认为代理可用 |
httpsValidator | @ProxyValidator.addHttpsValidator | https_validator | 校验代理是否支持 HTTPS |
从 helper/validator.py 的源码可以看到,装饰器本身只是简单的注册动作:
class ProxyValidator(withMetaclass(Singleton)): pre_validator = [] http_validator = [] https_validator = [] @classmethod def addPreValidator(cls, func): cls.pre_validator.append(func) return func @classmethod def addHttpValidator(cls, func): cls.http_validator.append(func) return func @classmethod def addHttpsValidator(cls, func): cls.https_validator.append(func) return func三个装饰器做的事情完全对称:把被装饰的函数追加到对应列表,并原样返回该函数。因此:
- 每种校验可以定义任意多个方法;
- 只有所有方法都返回
True,该校验类型才算通过("与"逻辑,任一返回False即失败); - 由于是类属性列表,所有装饰在模块加载期执行,且单例保证全项目共享同一份注册表。
1.1 内置校验实现一览
helper/validator.py 内置了四个校验器,覆盖"格式 → 可用性 → HTTPS 支持"三阶段:
@ProxyValidator.addPreValidator def formatValidator(proxy): """检查代理格式""" return True if IP_REGEX.fullmatch(proxy) else False @ProxyValidator.addHttpValidator def httpTimeOutValidator(proxy): """ http检测超时 """ proxies = {"http": "http://{proxy}".format(proxy=proxy), "https": "https://{proxy}".format(proxy=proxy)} try: r = head(conf.httpUrl, headers=HEADER, proxies=proxies, timeout=conf.verifyTimeout) return True if r.status_code == 200 else False except Exception as e: return False @ProxyValidator.addHttpsValidator def httpsTimeOutValidator(proxy): """https检测超时""" proxies = {"http": "http://{proxy}".format(proxy=proxy), "https": "https://{proxy}".format(proxy=proxy)} try: r = head(conf.httpsUrl, headers=HEADER, proxies=proxies, timeout=conf.verifyTimeout, verify=False) return True if r.status_code == 200 else False except Exception as e: return False @ProxyValidator.addHttpValidator def customValidatorExample(proxy): """自定义validator函数,校验代理是否可用, 返回True/False""" return TrueformatValidator使用正则IP_REGEX = re.compile(r"(.*:.*@)?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}")做fullmatch全匹配,(.*:.*@)?前缀意味着它支持username:password@ip:port这类带认证信息的代理格式(见 helper/validator.py 与文件头部的变更记录)。注意正则并不校验 IP/端口数值范围的合法性,999.1.1.1:80也会匹配通过,这一点在 tests/unit/test_validator.py 的参数化用例中有明确体现。httpTimeOutValidator/httpsTimeOutValidator使用requests.head()发探测请求,超时时间与探测 URL 都来自配置(详见下文"与配置项的联动"),任何异常(超时、连接失败、SSL 错误)一律返回False。customValidatorExample是一个恒返回True的占位示例,是官方留作扩展参考的模板(对应 tests/unit/test_validator.py 中的test_always_returns_true用例),实际生产使用时应替换为真实校验逻辑。
1.2 与配置项的联动
两个 HTTP 探测校验器直接读取ConfigHandler(见 handler/configHandler.py),对应 setting.py 中的配置:
| 配置项 | 环境变量 | 默认值 | 作用 |
|---|---|---|---|
HTTP_URL | HTTP_URL | http://httpbin.org | HTTP 可用性校验的探测目标网站 |
HTTPS_URL | HTTPS_URL | https://www.qq.com | HTTPS 支持校验的探测目标网站 |
VERIFY_TIMEOUT | VERIFY_TIMEOUT | 10(秒) | 探测请求的超时时间,同时被httpTimeOutValidator与httpsTimeOutValidator使用 |
配置读取支持环境变量覆盖,优先级为os.environ>setting.py默认值。另外 setting.py 中的MAX_FAIL_COUNT(默认0)与POOL_SIZE_MIN(默认20)虽不直接出现在校验函数中,但会在下文"校验结果如何流入代理池"部分与校验结果协同工作。
二、校验执行顺序与完整调用链
2.1 执行顺序图
校验按"预校验 → HTTP 可用性 → HTTPS 支持"三级串行执行:
preValidator校验通过的代理才会进入可用性校验;httpValidator校验通过后认为代理可用,更新入代理池;httpsValidator校验通过后视为代理支持 HTTPS,更新代理的https属性为True。
2.2 源码级调用链
校验的实际执行并不在validator.py内部,而是由 helper/check.py 中的DoValidator类驱动。它定义了三个与注册列表一一对应的遍历方法:
@classmethod def httpValidator(cls, proxy): for func in ProxyValidator.http_validator: if not func(proxy.proxy): return False return True @classmethod def httpsValidator(cls, proxy): for func in ProxyValidator.https_validator: if not func(proxy.proxy): return False return True @classmethod def preValidator(cls, proxy): for func in ProxyValidator.pre_validator: if not func(proxy): return False return True注意一个细节:preValidator传入的是整个proxy对象(func(proxy)),而httpValidator/httpsValidator传入的是proxy.proxy字符串(func(proxy.proxy))。也就是说,自定义预校验函数接收Proxy对象,可以访问source、region等元数据;而可用性/HTTPS 校验函数接收"ip:port"形式的字符串,便于直接拼接到proxies参数中。
DoValidator.validator()是统一入口,串起三个阶段的判定并更新代理状态(helper/check.py):
@classmethod def validator(cls, proxy, work_type): http_r = cls.httpValidator(proxy) https_r = False if not http_r else cls.httpsValidator(proxy) proxy.check_count += 1 proxy.last_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") proxy.last_status = True if http_r else False if http_r: if proxy.fail_count > 0: proxy.fail_count -= 1 proxy.https = True if https_r else False if work_type == "raw": proxy.region = cls.regionGetter(proxy) if cls.conf.proxyRegion else "" else: proxy.fail_count += 1 return proxy这里有三点值得注意:
- 短路语义:
https_r = False if not http_r else cls.httpsValidator(proxy),即 HTTP 校验不通过时根本不会执行HTTPS 校验; - 状态维护:
https属性只有在 HTTP 通过时才更新(True/False),HTTP 失败时保持原值;fail_count在失败时 +1、成功时 -1(不低于 0);check_count、last_time、last_status每次校验都会刷新; - 地域属性:
work_type == "raw"(原始代理阶段)且开启PROXY_REGION时,会调用regionGetter通过https://api.ip.sb/geoip/{ip}查询代理的国家代码,结果写入region字段。
Proxy对象的字段定义见 helper/proxy.py,其中https属性(self._https,默认False)就是 HTTPS 校验结果的落点,最终会通过to_dict/to_json序列化并存入代理池。
2.3 两个触发场景:raw 与 use
DoValidator.validator的work_type参数区分两个执行场景(见 helper/check.py):
raw(原始校验):代理刚从抓取器产出时执行。入口在 helper/fetch.py,Fetcher.run()对每个抓取结果先执行DoValidator.preValidator(_.proxy),格式合法的代理才 yield 给Checker("raw", proxy_queue);_ThreadChecker.__ifRaw中,校验通过的代理若不存在则put入池,失败则丢弃;use(使用中校验):对池内既有代理的周期性复核。__ifUse中,校验失败且fail_count > conf.maxFailCount时删除,否则保留并重新入池——这正对应 setting.py 的MAX_FAIL_COUNT(默认0,即一次失败即剔除)。
调度节奏由 helper/scheduler.py 控制:proxy_fetch(采集+原始校验)每 5 分钟执行一次,proxy_check(池内复核)每 2 分钟执行一次,且当池内代理数低于POOL_SIZE_MIN时会先触发抓取。校验本身采用 20 个线程并行消费队列(Checker中range(20)),高吞吐地处理代理队列。
三、扩展校验:从模板到实战
原文档明确指出:helper/validator.py 中已内置自定义校验的示例,扩展方式为——自定义函数返回True或False,用ProxyValidator的装饰器区分校验类型,然后把函数定义在helper/validator.py中(或任何会被导入的位置)即可自动注册,无需修改调度器或检查器代码。
3.1 示例 1:自定义代理可用性校验
@ProxyValidator.addHttpValidator def customValidatorExample01(proxy): """自定义代理可用性校验函数""" proxies = {"http": "http://{proxy}".format(proxy=proxy)} try: r = requests.get("http://www.baidu.com/", headers=HEADER, proxies=proxies, timeout=5) return True if r.status_code == 200 and len(r.content) > 200 else False except Exception as e: return False相比内置的httpTimeOutValidator(仅用head()检查状态码 200),这个自定义版本改用requests.get()并追加了len(r.content) > 200的内容长度条件,能过滤掉"能连接但响应体异常"的代理。HEADER是 helper/validator.py 中定义的全局请求头(含 User-Agent、Accept 等),自定义函数可直接复用。
3.2 示例 2:自定义 HTTPS 校验
@ProxyValidator.addHttpsValidator def customValidatorExample02(proxy): """自定义代理是否支持 HTTPS 校验函数""" proxies = {"https": "https://{proxy}".format(proxy=proxy)} try: r = requests.get("https://www.baidu.com/", headers=HEADER, proxies=proxies, timeout=5, verify=False) return True if r.status_code == 200 and len(r.content) > 200 else False except Exception as e: return FalseHTTPS 校验与 HTTP 校验的差别集中在三点:proxies字典用https协议前缀、探测目标是https://地址、verify=False关闭 SSL 证书验证(避免因目标站点证书链问题误判代理不可用)。原文档也特别提醒:运行代理可用性校验时,所有被ProxyValidator.addHttpValidator装饰的函数会依次按定义顺序执行,只有当所有函数都返回True时才判断代理可用;HttpsValidator的运行机制完全相同。
3.3 扩展校验的生效与验证
- 自动注册,无需配置:装饰器在模块导入时即把函数追加到
ProxyValidator的注册列表,运行python proxyPool.py(调度器启动)后即对每轮校验生效; - 按定义顺序执行:
DoValidator直接遍历列表,后定义的函数排在后面,因此先定义、先执行,可通过调整定义顺序控制校验优先级; - 单例共享:
ProxyValidator是单例,Web API、调度器、检查器线程访问的是同一份注册表,不存在多实例不一致问题。
四、校验器正确性如何被保障:测试用例解读
tests/unit/test_validator.py 为内置校验器提供了完整的单元测试,是理解校验语义的最佳辅助材料:
IP_REGEX格式匹配(TestIPRegex):合法的"1.2.3.4:8080"、带认证的"user:pass@1.2.3.4:8080"均匹配;空串、"abc"、缺端口的"1.2.3.4"、":8080"、端口含字母的"1.2.3.4:abc"等多段冒号的"1.2.3.4:8080:extra"均不匹配。测试同时注明"999.1.1.1:80"这类越界 IP 也会匹配——正则只做格式约束,不做数值范围校验;formatValidator(TestFormatValidator):与正则测试结论一致,合法格式返回True,非法格式返回False;httpTimeOutValidator(TestHttpTimeOutValidator):通过mock掉helper.validator.head,验证status_code=200 → True、status_code=502 → False、抛TimeoutError → False三条路径,覆盖了"成功/失败/异常"全部分支;httpsTimeOutValidator(TestHttpsTimeOutValidator):除上述三条分支外,还断言调用时传入了verify=False,印证 HTTPS 校验关闭证书验证的实现细节;customValidatorExample(TestCustomValidatorExample):确认占位示例恒返回True,即不干预默认校验结果。
这些测试可以直接运行验证:
pytest tests/unit/test_validator.py -v五、实战建议:自定义校验器的设计要点
结合源码调用链,编写高质量自定义校验器时有几个关键取舍:
- 用
HEAD还是GET:内置校验用head()轻量快速;需要校验响应体内容(如防空白页、防跳转)时改用get()并检查len(r.content),代价是带宽和延迟更高。若代理数量大,建议保持 HEAD + 状态码的轻量策略。 - 控制超时与重试:校验器跑在 20 线程的检查池里,每个校验器的
timeout会直接叠加到整体校验耗时上。内置版本统一用配置项VERIFY_TIMEOUT(默认 10 秒),自定义版本应设置合理超时并try/except兜底,任何异常都必须返回False。 - 注意
proxies字典的协议前缀:HTTP 校验传"http://{proxy}",HTTPS 校验传"https://{proxy}";如需两者同时验证,可像内置实现那样同时放入两个键。 - 预校验的特殊入参:
preValidator系列函数接收的是Proxy对象而非字符串(见 helper/check.py),可用于基于来源、地域等元数据做前置过滤,例如"只允许指定来源的代理进入可用性校验"。 - 返回值必须严格是布尔语义:装饰器注册不强制校验返回类型,但
DoValidator用if not func(...)判定,任何非真值(None、0、空串)都会被当作校验失败。
六、小结
ProxyPool 的校验器体系是一个"装饰器注册 + 集中遍历执行"的插件式设计:ProxyValidator用三个装饰器把校验逻辑挂载到三类列表中,DoValidator按"预校验 → HTTP → HTTPS"的顺序全量遍历并执行与逻辑,Checker的 raw/use 双场景把判定结果转化为入池、保留或剔除动作。理解这条链路后,扩展一个自定义校验器只需三步:写一个返回True/False的函数 → 选择合适的装饰器 → 放入 helper/validator.py 中导入生效,无需改动调度、检查或存储的任何代码。相关源码入口:helper/validator.py、helper/check.py、helper/fetch.py、tests/unit/test_validator.py、setting.py、handler/configHandler.py。
【免费下载链接】proxy_poolPython ProxyPool for web spider项目地址: https://gitcode.com/gh_mirrors/pr/proxy_pool
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考