匿名代理IP是一种网络代理服务,它允许用户通过一个中介服务器来访问互联网,同时隐藏用户的真实IP地址。这种服务的主要目的是提供隐私保护,防止用户在网络上被追踪和识别。
匿名代理的分类
匿名代理IP通过隐藏用户真实网络地址实现隐私保护,主要分为透明代理、匿名代理和高匿代理三种等级.
- 透明代理:仅转发请求不隐藏IP,目标网站可获取真实地址。
- 匿名代理:隐藏真实IP但暴露代理属性,适用于基础隐私保护。
- 高匿代理:完全隐藏IP及代理特征,可规避反爬检测机制。
实战经验
需求背景
实战地址:https://xkz.nfra.gov.cn/bx/ 国家金融监督管理总局
目标:根据机构名称查询对应的机构住所信息
操作流程:
- 调用列表接口,根据机构名称获取匹配的机构 ID;
- 调用详情接口,通过机构 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 等功能,以应对高频率请求场景下的代理失效问题。