匿名代理IP是一种网络代理服务,它允许用户通过一个中介服务器来访问互联网,同时隐藏用户的真实IP地址。这种服务的主要目的是提供隐私保护,防止用户在网络上被追踪和识别。

匿名代理的分类

‌‌匿名代理IP‌通过隐藏用户真实网络地址实现隐私保护,主要分为‌透明代理、匿名代理和‌高匿代理三种等级.

  • 透明代理‌:仅转发请求不隐藏IP,目标网站可获取真实地址。‌‌
  • 匿名代理‌:隐藏真实IP但暴露代理属性,适用于基础隐私保护。‌‌
  • 高匿代理‌:完全隐藏IP及代理特征,可规避反爬检测机制。‌‌

实战经验

需求背景

实战地址:https://xkz.nfra.gov.cn/bx/ 国家金融监督管理总局

目标:根据机构名称查询对应的机构住所信息

操作流程:

  1. 调用列表接口,根据机构名称获取匹配的机构 ID;
  2. 调用详情接口,通过机构 ID 获取详情页面,提取机构住所信息。

分析:需要查询详情首先要先获取机构对应的id,所以一共涉及两个接口,一个是根据机构名称获取机构列表,然后从列表中获取id,再使用id查询机构对应的详情信息,从详情返回的网页源代码中检出机构住所的内容

初始实现方案

核心接口分析

接口功能 请求方式 接口地址 关键参数 返回数据
获取机构 ID POST https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do useState=3、fullName = 机构名称 包含机构 ID 的 JSON 数据
获取机构详情 GET https://xkz.nfra.gov.cn/bx/PtBRST/showLicenceInfo.do id = 机构 ID 包含机构信息的 HTML 页面

初始代码实现

import json
import re
import time

import requests


def get_company_id(company_name):
    """调用接口获取机构id"""
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do"
    params = {
        "useState": 3,
        "fullName": company_name
    }
    data = {
        "start": 0,
        "limit": 10
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    }
    response = requests.post(url, params=params, data=data, headers=headers)
    if response.status_code == 200:
        json_data = response.json()
        if json_data.get("success"):
            # 遍历datas中的每个对象
            for item in json_data["datas"]:
                # 匹配fullName与入参company_name
                if item.get("fullName") == company_name:
                    id = item.get("id")
                    return id # 找到匹配项,返回id

            # 遍历完未找到匹配项
            print(f"未找到fullName为'{company_name}'的记录")
            return None
        else:
            print(f"接口返回异常:{json_data.get('msg')}")
            return None
    return None
def get_company_address(company_id):
    """调用接口获取机构详情"""
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/showLicenceInfo.do"
    params = {
        "id": company_id
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
    }
    response = requests.get(url, params=params, headers=headers)
    if response.status_code == 200:
        pattern = r'机构住所:\s*</td>\s*<td>\s*(.*)\s*</td>'
        result = re.search(pattern, response.text)

        if result:
            address = result.group(1)
            print("提取到的机构地址:", address)
            return address
        else:
            print("未找到目标地址")
            return None
    return None

问题暴露:请求频率限制

经过调用,发现一个问题,当接口调用次数达到30次时,两个接口都会显示“您的操作过于频繁,请五分钟后再试。” 所以我们需要借助到匿名代理,不断更换我们的真实ip躲避同一个ip请求次数过多的检测。

初识匿名代理

由于之前没有使用过匿名代理的经验,所以也没有获取匿名代理ip的渠道,在百度搜索了一下,找到了一个可以获取到免费代理ip的网址,经过测试,大部分的代理ip都是可用的

站大爷:https://www.zdaye.com/free/

我们将代理ip放到代码中,发现还是不行,代码如下:

def get_company_id(company_name):
    """调用接口获取机构id"""
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do"
    params = {
        "useState": 3,
        "fullName": company_name
    }
    data = {
        "start": 0,
        "limit": 10
    }
    proxy = {
        'http': 'http://47.92.143.92:8080'
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    }
    response = requests.post(url, params=params, data=data, headers=headers, proxies=proxy)
    if response.status_code == 200:
        json_data = response.json()
        if json_data.get("success"):
            # 遍历datas中的每个对象
            for item in json_data["datas"]:
                # 匹配fullName与入参company_name
                if item.get("fullName") == company_name:
                    id = item.get("id")
                    return id # 找到匹配项,返回id

            # 遍历完未找到匹配项
            print(f"未找到fullName为'{company_name}'的记录")
            return None
        else:
            print(f"接口返回异常:{json_data.get('msg')}")
            return None
    return None

以为是免费的代理ip不好用,于是注册成为了站大爷的用户,手动获取到了一部分短效优质代理,放在代码中发现还是会报请求频繁。

临时解决方案:休眠重试

时间紧任务重,于是只能放弃,改用临时解决方案,当接口请求频繁时休眠两分钟,然后再递归调用方法本身达到请求重试的效果,代码如下:


def get_company_id(company_name):
    """调用接口获取机构id"""
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do"
    params = {
        "useState": 3,
        "fullName": company_name
    }
    data = {
        "start": 0,
        "limit": 10
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    }
    response = requests.post(url, params=params, data=data, headers=headers)
    if response.status_code == 200:
        json_data = response.json()
        if json_data.get("success"):
            # 遍历datas中的每个对象
            for item in json_data["datas"]:
                # 匹配fullName与入参company_name
                if item.get("fullName") == company_name:
                    id = item.get("id")
                    return id # 找到匹配项,返回id

            # 遍历完未找到匹配项
            print(f"未找到fullName为'{company_name}'的记录")
            return None
        else:
            print(f"接口返回异常:{json_data.get('msg')}")
            # 发生异常时休眠两分钟
            time.sleep(120)
            return get_company_id(company_name)
    return None
def get_company_address(company_id):
    """调用接口获取机构详情"""
    url = "https://xkz.nfra.gov.cn/bx/PtBgQq/showLicenceInfo.do"
    params = {
        "id": company_id
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
    }
    response = requests.get(url, params=params, headers=headers)
    if response.status_code == 200:
        pattern = r'机构住所:\s*</td>\s*<td>\s*(.*)\s*</td>'
        result = re.search(pattern, response.text)

        if result:
            address = result.group(1)
            print("提取到的机构地址:", address)
            return address
        else:
            print("未找到目标地址,可能请求过于频繁")
            # 发生异常时休眠两分钟
            time.sleep(120)
            return get_company_address(company_id)
    return None

实战回顾:匿名代理在接口请求中的应用与问题解决

初始疑问:高匿代理为何未生效?

时隔一周,之前的任务已经完成了,但是代理ip的事还是一直有疑惑,为什么明明是可用的高匿代理放在代码中还是无法使用?

问题突破:HTTP 代理与 HTTPS 网站的兼容问题

我再次访问站大爷,获取了一个免费高匿代理,不过这次我不打算将他写到代码中,我直接点开系统设置-wifi-详细信息-代理-http代理。我将获取到的ip和端口填写到输入框中点击了保存,再次百度查询我的本机ip,发现还是会显示我的真实ip地址

本机ip查询:https://ip.900cha.com/

于是我将这个问题丢给豆包,豆包给我的回答是,我设置的是http代理,而我访问的网站是https,我恍然大悟,于是我将代理ip再次填写到https那一栏,再次访问本机ip查询,发现可以正常显示本机ip为代理ip的地址了。

代码初步修改:配置 HTTPS 代理

我将之前的代码修改如下:

def get_company_id(company_name):
    """调用接口获取机构id"""
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do"
    params = {
        "useState": 3,
        "fullName": company_name
    }
    data = {
        "start": 0,
        "limit": 10
    }
    proxy = {
        'https': 'http://47.92.143.92:8080'
    }
    # 自定义表头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    }
    response = requests.post(url, params=params, data=data, headers=headers, proxies=proxy)
    if response.status_code == 200:
        json_data = response.json()
        if json_data.get("success"):
            # 遍历datas中的每个对象
            for item in json_data["datas"]:
                # 匹配fullName与入参company_name
                if item.get("fullName") == company_name:
                    id = item.get("id")
                    return id # 找到匹配项,返回id

            # 遍历完未找到匹配项
            print(f"未找到fullName为'{company_name}'的记录")
            return None
        else:
            print(f"接口返回异常:{json_data.get('msg')}")
            return None
    return None

新问题出现:TLS 协议不兼容报错

结果报错:urllib3.exceptions.SSLError: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1028)

把报错丢给豆包,说是TLS协议不兼容。最终给了我一个改造后的代码,使用urllib3进行请求,可以指定TLS版本为1.2

最终解决方案:指定 TLS1.2 协议的完整代码

最终代码如下:

import time

import requests
import ssl
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager

# 兼容旧版本 urllib3:不用 create_ssl_context,直接用 ssl 库创建上下文
class ForceTLS12Adapter(HTTPAdapter):
    def init_poolmanager(self, connections, maxsize, block=False):
        # 直接用 ssl 库创建 TLS 1.2 上下文
        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        # 启用安全加密套件(匹配政府网站要求)
        context.set_ciphers(
            "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
        )
        # 禁用不安全协议和特性
        context.options |= ssl.OP_NO_TLSv1
        context.options |= ssl.OP_NO_TLSv1_1
        context.options |= ssl.OP_NO_SSLv3
        context.verify_mode = ssl.CERT_NONE  # 禁用证书验证
        self.poolmanager = PoolManager(
            num_pools=connections,
            maxsize=maxsize,
            block=block,
            ssl_context=context
        )

def get_company_id(company_name):
    url = "https://xkz.nfra.gov.cn/bx/PtBRST/getLicence.do"
    params = {"useState": 3, "fullName": company_name}
    data = {"start": 0, "limit": 10}
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
        "Content-Type": "application/x-www-form-urlencoded",
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "Referer": "https://xkz.nfra.gov.cn/bx/PtBRST/licenceQuery.html",
    }
    proxy = {'https': 'http://114.80.36.176:3081'}

    session = requests.Session()
    session.mount("https://", ForceTLS12Adapter())  # 绑定兼容版适配器
    session.proxies = proxy

    try:
        response = session.post(
            url=url,
            params=params,
            data=data,
            headers=headers,
            timeout=20,
            verify=False  # 禁用证书验证(与适配器呼应)
        )
        response.raise_for_status()
        print(f"请求成功,状态码:{response.status_code}")
    except requests.exceptions.SSLError as e:
        print(f"SSL 握手失败:{e}")
        time.sleep(60)
        return get_company_id(company_name)
    except requests.exceptions.ProxyError as e:
        print(f"代理失效/被拒:{e}")
        return None
    except Exception as e:
        print(f"其他错误:{e}")
        time.sleep(60)
        return get_company_id(company_name)

    # 后续处理逻辑不变
    if response.status_code == 200:
        try:
            json_data = response.json()
            print(json_data)
        except ValueError:
            print("返回非 JSON 数据,可能被拦截")
            return None
    return None
if __name__ == "__main__":
    get_company_id('天安财产保险股份有限公司连云港中心支公司')

验证结果与总结

经过实际验证,改造后的代码成功解决了代理生效与 TLS 协议兼容问题,在频繁手动请求后仍能正常返回目标接口报文。

此次实战不仅解决了具体技术问题,更深入理解了代理协议匹配、TLS 版本兼容等网络请求底层逻辑,为后续处理类似接口爬取、跨域请求等场景提供了可复用的解决方案。后续可进一步优化代理池管理、动态切换 IP 等功能,以应对高频率请求场景下的代理失效问题。