bilibili-API-collect
bilibili-API-collect copied to clipboard
查 up 空间的 gRPC Proto 可能有问题?会报错 `-352`
具体实现在这儿:https://github.com/SK-415/bilireq/blob/main/bilireq/grpc/utils/metadata.py
无论怎么发,这个 DynSpaceReq
都会报 -352
错误。
await grpc_get_user_dynamics(uid=491589125, auth=auth)
Traceback (most recent call last):
File "/root/bilireq/bilireq/grpc/utils/__init__.py", line 48, in wrapper
result = await func(
File "/root/bilireq/bilireq/grpc/dynamic/__init__.py", line 26, in grpc_get_user_dynamics
return await stub.DynSpace(req, **kwargs)
File "/root/bilireq/.venv/lib/python3.10/site-packages/grpc/aio/_call.py", line 290, in __await__
raise _create_rpc_error(self._cython_call._initial_metadata,
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
status = StatusCode.UNKNOWN
details = "-352"
debug_error_string = "UNKNOWN:Error received from peer {grpc_message:"-352", grpc_status:2, created_time:"2023-08-28T09:28:45.915381792+08:00"}"
>
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/root/bilireq/test/test_grpc.py", line 91, in <module>
asyncio.run(main())
File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/root/bilireq/test/test_grpc.py", line 74, in main
dynamics = await grpc_get_user_dynamics(uid=491589125, auth=auth)
File "/root/bilireq/bilireq/grpc/utils/__init__.py", line 66, in wrapper
raise GrpcError(
bilireq.exceptions.GrpcError: 错误码: -352, 信息: -352
device 改 android, 版本号改 7380300, channel 改 master 看看
我之前爬关注列表每次报错-352后,进去都要求做一个验证码,做了之后就不报错了
安卓客户端吗?
device 改 android, 版本号改 7380300, channel 改 master 看看
修改后无效,仍然 -352
UA 改了没, 你抓个包看看和正常 APP 请求的差别. 还有发送时 gzip 要关掉.
UA 改了没, 你抓个包看看和正常 APP 请求的差别. 还有发送时 gzip 要关掉.
用的是grpc的请求库,其他所有接口都没问题,只有这一个接口是报错的
最近风控是紧一点.
应该是可以自定义 UA, 关闭 gzip 的.
貌似没找到关闭 gzip 的方法,好像这个 python 的 grpc 库没提供 ua 的话用的是手机抓包抓来的,修改了 build 也依然不行
找到个 example, Ref: CSDN, Google
// Example: Change client compression at the call level
response, call = client.t_client.with_call(
pb2.t_client_requests(data=str_random),
compression=grpc.Compression.Gzip, // 这儿改 grpc.Compression.Deflate 看看
metadata=((('client_key',"clinet_value"),)),
timeout=10,
# wait_for_ready=True,
) # timeout超出时长抛出异常
eid 那里怎么有个 \u003d, no padding 的. UA 里面也有版本号, 相对应改一下. access_key 截取前32位. grpc.biliapi.net 改 app.bilibili.com.
看看可不可行.
还有你 access_key, mid 漏了
上面的 json 是手机小黄鸟抓包抓来的, no padding 不清楚是为啥 ua的话版本号对上了,
'user-agent': 'Dalvik/2.1.0 (Linux; U; Android 13; KB2003 '
'Build/RKQ1.211119.001) 7.38.0 os/android model/KB2003 '
'mobi_app/android build/7380300 channel/master innerVer/7380300 '
'osVer/13 network/2 grpc-java-cronet/1.36.1',
截取了前 32 位也改了host,仍然报错
发送端 Gzip 关了没, 我记得有个逆向大佬明确指出这玩意就是风控点
抓个包比对一下正常请求, 或者说 APP 的请求和你 python 程序发出的请求的差异, 包括请求头和发送的二进制内容. 可以抓包, APP 随意打开个动态, 然后拿 proto 解析一下 APP 请求的内容, 然后复制过来, 确保请求的 DynSpaceReq 是一致的, 然后比对发送的二进制内容.
如果还不行我就不太清楚为什么了. 参考 https://github.com/SocialSisterYi/bilibili-API-collect/issues/686, Web 端有信息上报的机制, 客户端也有信息上报的机制, 如果是这里的原因就相当麻烦了.
无法复现, 甚至请求头鉴权部分只剩空的x-bili-device-bin
都可以 你可以试试手写grpc协议, 或者试试换个ip
python:
import requests
from bilibili_pb2 import DynSpaceReq
msg = DynSpaceReq()
msg.host_uid = 491589125
# msg.local_time = 8
# msg.page = 1
proto = msg.SerializeToString()
print(proto)
data = b'\0' + len(proto).to_bytes(4) + proto
headers = {
'User-Agent': 'Dalvik/2.1.0 (Linux; Android) os/android',
'Content-Type': 'application/grpc',
'x-bili-device-bin': '',
}
resp = requests.post('https://app.bilibili.com/bilibili.app.dynamic.v2.Dynamic/DynSpace', data=data, headers=headers)
resp.raise_for_status()
print(len(resp.content) > 1000)
# True
proto:
syntax = "proto3";
package bilibili.app.dynamic.v2;
message DynSpaceReq {
int64 host_uid = 1;
int32 local_time = 4;
int64 page = 5;
}
无法复现, 甚至请求头鉴权部分只剩空的
x-bili-device-bin
都可以 你可以试试手写grpc协议, 或者试试换个ip python:
我瞅着就像 gzip 暴露了客户端, 要不然就是 IP 黑了或者 access_key 黑了...
不过手搓 gRPC 消息就真的猛了 😂
前几天我爬太猛了估计 b站就加了这个-352 换个ip就好了
无法复现, 甚至请求头鉴权部分只剩空的
x-bili-device-bin
都可以 你可以试试手写grpc协议, 或者试试换个ip python:我瞅着就像 gzip 暴露了客户端, 要不然就是 IP 黑了或者 access_key 黑了...
不过手搓 gRPC 消息就真的猛了 😂
那更不对了啊,因为加不加 access_key,甚至换ip,加代理都没用,该不会真是这个python库的问题吧(
无法复现,甚至请求头鉴权部分空间的
x-bili-device-bin
都可以你可以尝试手写grpc协议,或者尝试换个ip python:我瞧着就像gzip暴露了客户端,要不然就是IP黑了或者access_key黑了... 手搓gRPC消息就真的猛了😂
那更不用了啊,因为加不加access_key,甚至换ip,加代理都没用,该不会真是这个python库的问题吧(
-352应该是23号加的 我感觉就是ip问题 23号之前我爬动态爬了10多t流量 好像就是那天早上加了-352 在此之前是没有的 我用了代理池就没这个问题 库问题应该是4xx 你可以用go试下 如果还是-352那应该就是ip问题 开tun模式试下
我试了下,删掉了所有的metadata,只留了ua和device bin,出了个新的报错:
这是开了 grpc.Compression.Deflate 的
(bilireq-3.10) root@as:~/bilireq# python test/test_grpc.py
{'user-agent': 'Dalvik/2.1.0 (Linux; Android) os/android',
'x-bili-device-bin': b''}
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
status = StatusCode.UNIMPLEMENTED
details = "grpc: Decompressor is not installed for grpc-encoding "deflate""
debug_error_string = "UNKNOWN:Error received from peer {grpc_message:"grpc: Decompressor is not installed for grpc-encoding \"deflate\"", grpc_status:12, created_time:"2023-08-30T09:06:53.605232597+08:00"}"
>
这是开了 grpc.Compression.NoCompression 和 grpc.Compression.Gzip 的
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
status = StatusCode.UNKNOWN
details = "Stream removed"
debug_error_string = "UNKNOWN:Error received from peer ipv4:114.230.222.142:443 {created_time:"2023-08-30T09:32:16.5829016+08:00", grpc_status:2, grpc_message:"Stream removed"}"
>
这个报错在半个月之前是加了个 buvid 就解决了,然后现在加 buvid 也不管用了,貌似我只要给 device-bin 里填充了内容就会 -352
用 httpx 手搓的grpc请求是正常的,换了库就出问题了
msg = DynSpaceReq()
msg.host_uid = 491589125
# msg.local_time = 8
# msg.page = 1
proto = msg.SerializeToString()
print(proto)
data = b"\0" + len(proto).to_bytes(4, "big") + proto
headers = {
"User-Agent": "Dalvik/2.1.0 (Linux; Android) os/android",
"Content-Type": "application/grpc",
"x-bili-device-bin": "",
}
resp = httpx.post("https://app.bilibili.com/bilibili.app.dynamic.v2.Dynamic/DynSpace", data=data, headers=headers)
resp.raise_for_status()
gresp = DynSpaceRsp()
gresp.ParseFromString(resp.content[5:])
print(gresp)
那有可能是这个库存在什么特征, 给针对性 ban 掉了, 你有抓包对比过二进制数据了吗. 不过我用 Rust 的 tonic 是没问题的, 可能因为 tonic 并不是 gRPC 的官方实现?
这是开了 grpc.Compression.NoCompression 和 grpc.Compression.Gzip 的
grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with: status = StatusCode.UNKNOWN details = "Stream removed" debug_error_string = "UNKNOWN:Error received from peer ipv4:114.230.222.142:443 {created_time:"2023-08-30T09:32:16.5829016+08:00", grpc_status:2, grpc_message:"Stream removed"}" >
这个报错在半个月之前是加了个 buvid 就解决了,然后现在加 buvid 也不管用了,貌似我只要给 device-bin 里填充了内容就会 -352
Stream removed 似乎不是风控的, 换 grpc.biliapi.net 这个原生 gRPC 接口看看? Ref: https://lightrun.com/answers/adap-flower-rpc-error-stream-removed
刚刚我也使用 Rust tonic 测试了一下, 亦无法复现这问题, 合理怀疑产生这问题的原因:
- gRPC 库存在特征, 被针对性风控
- IP 或者 access_key 黑了
所以尝试更换 IP, 最好换运营商, 例如用手机流量, 换 gRPC 库换 access_key 或者不要 access_key 再试试.
测试样例(仅供参考, 从一个大程序摘下来的)
#[tokio::test]
async fn test_v2_dynamic() -> Result<(), String> {
let client_arc = GRPC_HTTPS_CLIENT_TEST.clone();
let hyper_https_client = client_arc.get("a1playurl").unwrap().clone();
let user_info = ClientUserInfo {
uid: ***,
access_key: "***".to_owned(),
..Default::default()
};
let client_info = ClientInfo {
user_info,
app_info: ClientAppInfo::default(),
device_info: ClientDeviceInfo::default(),
};
let bili_header = BiliGrpcHeaders::new(&client_info);
let bili_upstream_uri = hyper::Uri::from_static("https://app.bilibili.com");
let req = bapis::app::dynamic::v2::DynSpaceReq {
host_uid: 491589125,
local_time: 8,
page: 1,
..Default::default()
};
let resp = moss_handler!(
bili_header,
bili_upstream_uri,
hyper_https_client,
bapis::app::dynamic::v2::dynamic_client::DynamicClient
)
.dyn_space(req)
.await;
match resp {
Ok(resp) => {
use tokio::fs;
let resp_str = format!("{:#?}", resp.get_ref());
fs::write("cn_space.txt", resp_str).await.unwrap();
// println!("{:?}", resp);
Ok(())
}
Err(e) => {
println!("{:?}", e);
let bili_error = BiliError::from(e);
Err(format!("{}", bili_error))
}
}
}
Reply:
我认为IP不是问题的关键,此问题的广泛出现理论上和IP并不相关,而且其中包括了大量非数据中心的常规民用线路的IP。
参考上述讨论,很有可能是python的grpc库出现问题了,可以以此为方向尝试解决?
鉴于 Python 是 gRPC 官方支持的语言之一, 我搜了搜没找到合适的第三方库, 我觉得像前面 My-Responsitories那样简单手搓个 gRPC Client 就好, 需要什么功能再加就是()
鉴于 Python 是 gRPC 官方支持的语言之一, 我搜了搜没找到合适的第三方库, 我觉得像前面 My-Responsitories那样简单手搓个 gRPC Client 就好, 需要什么功能再加就是()
要说没有的话,确实也有一些,比如 grpclib 或者 aiogrpc(三年没更新了),不过迁移成本有点高,也不确定是不是 grpcio 造成的问题还是 python 的问题,总之仍需要进一步测试
我也同样出现-352问题,这是我的metdata配置:https://github.com/cctyl/BiliRecommBehaviorCorrection/blob/main/src/main/java/io/github/cctyl/interceptor/GrpcInterceptor.java
这是我的请求代码:
@Test void test() { DynamicRpcProto.DynDetailReply dynDetailReply = dynamicBlockingStub.dynDetail(DynamicRpcProto.DynDetailReq.newBuilder() .setDynamicId("824093109029699590") .build()); log.info(dynDetailReply.toString()); }
直接出现了UNKNOWN: -352的问题
直接出现了UNKNOWN: -352的问题
你康康你代码里 build 写 7110300
, version 却写了 7.38.0
, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.
直接出现了UNKNOWN: -352的问题
你康康你代码里 build 写
7110300
, version 却写了7.38.0
, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.
哈哈好的。我改一下,谢谢指点。其实我发现是我的access_key格式错了,改了就正常了。我在想它们的服务器是否真的会校验多个字段的一致性,可能发送多次会导致风控,我这还是首次发起
直接出现了UNKNOWN: -352的问题
你康康你代码里 build 写
7110300
, version 却写了7.38.0
, 还有一次性的 sessionId 给写死了, UA 里面的设备 model 也和你 metadata 里面的对不上, 被风控也不奇怪.
这个sessionId 生成的规则是什么呢?proto的注释是 0-9 a-z,任意混合成32位即可吗
这个sessionId 生成的规则是什么呢?proto的注释是 0-9 a-z,任意混合成32位即可吗
~~不清楚, 没深究, 应该随机问题不大, 我就那么操作的. 还有更正一下是 8 位, 0-9, a-f.~~
刚刚看了一眼, 确认是随机的 8 位 0-9, a-f 字符
static {
byte[] bArr = new byte[4];
new Random().nextBytes(bArr);
m = ByteString.of(bArr, 0, 4).hex(); // 即 sessionId
}