万方 protobuf 反序列化

protobuf 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。
在网络传输方面,相比传统的json,有着更快、更小,且加密性好的特点。


在实际应用中,万方数据库官网发送的请求,就用到了这。
一般网站,比如cnki,我们检索内容,只需要拼接一个http请求,便能获取到数据。
在这里插入图片描述



但是,万方数据库,却是这样的,看不到请求body都是一些乱码一样的数据,连返回的数据也是。
在这里插入图片描述
在这里插入图片描述



这正是由于其使用了protobuf序列化,我们通过它发送请求头中也可以看到有 “application/grpc-web+proto” 这样的字样。
在这里插入图片描述




其大致流程:实例化一个proto格式对象(长得类似c的结构体),将检索式塞到该对象属性中,对其进行序列化为二进制,发送请求,对接收到的数据进行反序列化。
因此我们重点就在于拿到这个proto对象的结构。



有关万方protobuf发送请求的大部分内容,可以参考大佬的文章: https://blog.csdn.net/qq_35491275/article/details/111721639



这里就只对拿到数据,将其反序列化的大致步骤,进行讲解。
本人是通过python发送请求,根据上面参考文章说的,要将数据反序列化,便需要获取到proto格式,比如有哪些变量和变量类型。
而这都需要通过在浏览器F12,打断点,一步步调试js函数才能获得。本人由于对js不了解,也不太会调试js,所以也是变相换个方法去实现。



F12------》Source------》XHR/fetch Breakpoints,将接口请求地址复制进去,这样调试的时候就会自动在相关位置断点了。
在这里插入图片描述



在Call Stack调用栈中,看到了SearchService字样,点击进去。
在这里插入图片描述
在这里插入图片描述



后面通过打断点,一步步调试,便能在一些方法名中找到需要的变量和类型。
本人这里就不做调试,我说说自己当时的思路。

因为整个检索流程,就主要经过了两份js,所以我直接ctrl+F,搜索SearchService字样,发现都在api.1f9fdaa9.js这一份当中。
然后我再ctrl+F,搜索 “toObject = function(e, t) {” 字样,一般每个message结构对应的变量名和序号就在这个函数中,变量类型则一般在 “serializeBinaryToWriter = function(e, t) {” 函数中,大概有90多个匹配的。这一步不清楚可以看上面参考博客,便知道为什么这样找这些message结构。将他们列出来,写入一份.proto文件中。
这里变量名好像可以自己命名,主要是序号需要保持一致,但为了解析数据方便,这里变量名也尽量保持了和js中的一致,最终得到右边的proto文件。
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述




然后通过protoc.exe编译器,输入命令 protoc proto文件名 --python_out=./ 得到相关py文件。

在这里插入图片描述




新建一个python工程,将刚刚生成的py文件丢入工程中。便可成功反序列化出数据。

在这里插入图片描述


这里请求时使用了blackboxprotobuf模块,可以减少去获取请求的proto结构这一步骤。具体一些点的可以参考他人博客。

参考代码:main.py

# This is a sample Python script.

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.


import blackboxprotobuf
import urllib
from urllib import parse
import requests
import tkinter
import asyncio
import os
import wf_proto_pb2 as wf_proto_pb2


def intToBytes(value, length):
  result = []
  for i in range(0, length):
    result.append(value >> (i * 8) & 0xff)
  result.reverse()
  return result


def prowfdata(response):
    # 转码
    response = str(response.encode('latin1').decode('unicode-escape').encode('latin1').decode('utf-8'))
    return response


def searchw(searchkey):
    searchkey = urllib.parse.unquote(searchkey)
    json = eval(searchkey)
    url = json["url"]
    headers = json["headers"]
    deserialize_data = json["deserialize_data"]
    message_type = json["message_type"]
    unwanted_bytes = json["unwanted_bytes"]

    proxies = {'http': 'http://127.0.0.1:8888', 'https': 'http://127.0.0.1:8888'}
    form_data = bytes(blackboxprotobuf.encode_message(deserialize_data, message_type))
    bytes_head = bytes(intToBytes(len(form_data), 5))

    requests.packages.urllib3.disable_warnings()
    try:
        response = requests.post(url=url, headers=headers,data=bytes_head + form_data, timeout=10, verify=False)
    except Exception as e:
        response = requests.post(url=url, headers=headers,data=bytes_head + form_data, timeout=10, verify=False, proxies=proxies)



    search_response2 = wf_proto_pb2.SearchService_SearchResponse()
    search_response2.ParseFromString(response.content[5:])
    return prowfdata(str(search_response2))





if __name__ == '__main__':
    searchkey = '{"url":"https://s.wanfangdata.com.cn/SearchService.SearchService/search","headers":{"accept":"*/*","Referer":"https://s.wanfangdata.com.cn","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36","Content-Type":"application/grpc-web+proto",},"deserialize_data":{"1":{"1":"paper","2":"(题名:(dna))","4":{"1":"Type","2":"(Periodical OR Thesis OR Conference)"},"5":1,"6":10,"8":"\\u0000"},"2":3},"message_type":{"1":{"type":"message","message_typedef":{"1":{"type":"bytes", "name":""}, "2":{"type":"bytes", "name":""},"4":{"type":"message","message_typedef":{"1":{"type":"bytes", "name":""},"2":{"type":"bytes", "name":""}},"name":""}, "5":{"type":"int", "name":""},"6":{"type":"int", "name":""}, "8":{"type":"bytes", "name":""}},"name":""}, "2":{"type":"int", "name":""}},"unwanted_bytes":"0"}'
    print(searchw(searchkey))
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
using System; //需要用到MemoryStream using System.IO; using UnityEngine; //引入ProtoBuf命名空间 using ProtoBuf; /// /// 测试类 /// public class TestProtobuf : MonoBehaviour { /// /// 用于测试的数据类 /// [ProtoContract] //声明这个类能被序列化 public class UserData { //声明每一个需要被序列化的成员,编号从1开始 [ProtoMember(1)] public int id; [ProtoMember(2)] public string name; [ProtoMember(3)] public int level; } //测试代码 void Start() { //将要被序列化的UserData示例 UserData user1 = new UserData (); user1.id = 1; user1.name = "User1"; user1.level = 10; //打印user1 Debug.Log (string.Format ("user1-> id:{0}, name:{1}, level:{2}", user1.id, user1.name, user1.level)); //序列化 byte[] buff = null; using (MemoryStream ms = new MemoryStream ()) { Serializer.Serialize (ms, user1); ms.Position = 0; int length = (int)ms.Length; buff = new byte[length]; ms.Read (buff, 0, length); } //输出字节数组 Debug.Log (string.Format("Serialized data-> {0}", BitConverter.ToString(buff))); //反序列化 UserData user2 = default(UserData); using (MemoryStream ms = new MemoryStream (buff)) { user2 = Serializer.Deserialize (ms); } //打印反序列化生成的user2 Debug.Log (string.Format ("user2-> id:{0}, name:{1}, level:{2}", user2.id, user2.name, user2.level)); } } 作者:qufangliu 链接:https://www.jianshu.com/p/d9be1b3d2446 來源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值