python - 如何使用 POST 方法发送 pandas 数据帧并在 Hug/其他 REST API 框架中接收它? pickle.loads 发送后无法取消pickle

标签 python pandas request python-requests hug

如何使用 POST 方法发送 pandas DataFrame?

例如以下hug server监听 POST 请求并使用 pickled pandas DataFrame 进行响应:

import hug
import pickle
import traceback
import pandas as pd

@hug.post()
def call(pickle_dump):
    print(type(pickle_dump))
    try:
        df = pickle.loads(pickle_dump)
        return pickle.dumps(df.iloc[0])
    except:
        print(traceback.format_exc())
        return pickle.dumps(pd.DataFrame())

当发出以下POST请求时:

import requests
import pandas as pd

df = pd.DataFrame(pd.np.random.randn(10,20))
r = requests.post('http://localhost:8000/call', data = {'pickle_dump':pickle.dumps(df)})
pickle.loads(r.text)

服务器返回此回溯:

<class 'str'>
Traceback (most recent call last):
  File "post.py", line 9, in call
    df = pickle.loads(pickle_dump)
TypeError: a bytes-like object is required, not 'str'

127.0.0.1 - - [23/Jul/2018 17:12:12] "POST /call HTTP/1.1" 200 10

同样,客户端返回:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-292-956952cbfca9> in <module>()
      5 r = requests.post('http://localhost:8000/call', data = {'pickle_dump':pickle.dumps(df)})
      6 
----> 7 pickle.loads(r.text)

TypeError: a bytes-like object is required, not 'str'

这似乎与以下事实有关:当将字节对象发送到 hug api 时,字节会通过以下方式转换为 str:

例如,pickle.dumps(b'test') 在客户端返回 b'\x80\x03C\x04testq\x00.'。 当 Hug 服务器接收到它时,它会变成 str('\x80\x03C\x04testq\x00.') (缺少 b)。可以使用 pickle.loads('\x80\x03C\x04testq\x00.'.encode()[1:]) 将对象解码回其原始形式。

在 DataFrame 上应用上述过程会导致 UnpicklingError:

> pickle.dumps(pd.DataFrame())
b'\x80\x03cpandas.core.frame\nDataFrame\nq\x00)\x81q\x01}q\x02(X\t\x00\x00\x00_metadataq\x03]q\x04X\x04\x00\x00\x00_typq\x05X\t\x00\x00\x00dataframeq\x06X\x05\x00\x00\x00_dataq\x07cpandas.core.internals\nBlockManager\nq\x08)\x81q\t(]q\n(cpandas.core.indexes.base\n_new_Index\nq\x0bcpandas.core.indexes.base\nIndex\nq\x0c}q\r(X\x04\x00\x00\x00nameq\x0eNX\x04\x00\x00\x00dataq\x0fcnumpy.core.multiarray\n_reconstruct\nq\x10cnumpy\nndarray\nq\x11K\x00\x85q\x12C\x01bq\x13\x87q\x14Rq\x15(K\x01K\x00\x85q\x16cnumpy\ndtype\nq\x17X\x02\x00\x00\x00O8q\x18K\x00K\x01\x87q\x19Rq\x1a(K\x03X\x01\x00\x00\x00|q\x1bNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK?tq\x1cb\x89]q\x1dtq\x1ebu\x86q\x1fRq h\x0bh\x0c}q!(h\x0eNh\x0fh\x10h\x11K\x00\x85q"h\x13\x87q#Rq$(K\x01K\x00\x85q%h\x1a\x89]q&tq\'bu\x86q(Rq)e]q*]q+}q,X\x06\x00\x00\x000.14.1q-}q.(X\x06\x00\x00\x00blocksq/]q0X\x04\x00\x00\x00axesq1h\nustq2bub.'

反转泡菜

pickle.loads('\x80\x03cpandas.core.frame\nDataFrame\nq\x00)\x81q\x01}q\x02(X\t\x00\x00\x00_metadataq\x03]q\x04X\x04\x00\x00\x00_typq\x05X\t\x00\x00\x00dataframeq\x06X\x05\x00\x00\x00_dataq\x07cpandas.core.internals\nBlockManager\nq\x08)\x81q\t(]q\n(cpandas.core.indexes.base\n_new_Index\nq\x0bcpandas.core.indexes.base\nIndex\nq\x0c}q\r(X\x04\x00\x00\x00nameq\x0eNX\x04\x00\x00\x00dataq\x0fcnumpy.core.multiarray\n_reconstruct\nq\x10cnumpy\nndarray\nq\x11K\x00\x85q\x12C\x01bq\x13\x87q\x14Rq\x15(K\x01K\x00\x85q\x16cnumpy\ndtype\nq\x17X\x02\x00\x00\x00O8q\x18K\x00K\x01\x87q\x19Rq\x1a(K\x03X\x01\x00\x00\x00|q\x1bNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK?tq\x1cb\x89]q\x1dtq\x1ebu\x86q\x1fRq h\x0bh\x0c}q!(h\x0eNh\x0fh\x10h\x11K\x00\x85q"h\x13\x87q#Rq$(K\x01K\x00\x85q%h\x1a\x89]q&tq\'bu\x86q(Rq)e]q*]q+}q,X\x06\x00\x00\x000.14.1q-}q.(X\x06\x00\x00\x00blocksq/]q0X\x04\x00\x00\x00axesq1h\nustq2bub.'.encode()[1:])

结果:

---------------------------------------------------------------------------
UnpicklingError                           Traceback (most recent call last)
<ipython-input-314-7082d60a5569> in <module>()
----> 1 pickle.loads('\x80\x03cpandas.core.frame\nDataFrame\nq\x00)\x81q\x01}q\x02(X\t\x00\x00\x00_metadataq\x03]q\x04X\x04\x00\x00\x00_typq\x05X\t\x00\x00\x00dataframeq\x06X\x05\x00\x00\x00_dataq\x07cpandas.core.internals\nBlockManager\nq\x08)\x81q\t(]q\n(cpandas.core.indexes.base\n_new_Index\nq\x0bcpandas.core.indexes.base\nIndex\nq\x0c}q\r(X\x04\x00\x00\x00nameq\x0eNX\x04\x00\x00\x00dataq\x0fcnumpy.core.multiarray\n_reconstruct\nq\x10cnumpy\nndarray\nq\x11K\x00\x85q\x12C\x01bq\x13\x87q\x14Rq\x15(K\x01K\x00\x85q\x16cnumpy\ndtype\nq\x17X\x02\x00\x00\x00O8q\x18K\x00K\x01\x87q\x19Rq\x1a(K\x03X\x01\x00\x00\x00|q\x1bNNNJ\xff\xff\xff\xffJ\xff\xff\xff\xffK?tq\x1cb\x89]q\x1dtq\x1ebu\x86q\x1fRq h\x0bh\x0c}q!(h\x0eNh\x0fh\x10h\x11K\x00\x85q"h\x13\x87q#Rq$(K\x01K\x00\x85q%h\x1a\x89]q&tq\'bu\x86q(Rq)e]q*]q+}q,X\x06\x00\x00\x000.14.1q-}q.(X\x06\x00\x00\x00blocksq/]q0X\x04\x00\x00\x00axesq1h\nustq2bub.'.encode()[1:])

UnpicklingError: 

我愿意使用任何允许我使用 HTTP 请求发送和接收 pandas DataFrame 的框架。

服务端和客户端运行在同一环境,且包版本相同。

如何使用 HTTP 方法发送和接收 pandas DataFrame?

最佳答案

似乎对 pickled 字符串进行 b64 编码似乎可以缓解这个问题。为了简洁起见,我将用一个例子来演示。

假设我有以下数据框:

>>> import pandas as pd
>>> df = pd.DataFrame({'a': [0, 1, 2, 3]})
>>> df
   a
0  0
1  1
2  2
3  3

现在,让我们将对象 pickle 为类似字节的字符串,然后对 pickled 字符串进行 b64 编码:

>>> import pickle
>>> pickled = pickle.dumps(df)
>>> import base64
>>> pickled_b64 = base64.b64encode(pickled)
>>> pickled_b64
b'gANjcGFuZGFzLmNvcmUuZnJhbWUKRGF0YUZyYW1lCnEAKYFxAX1xAihYCQAAAF9tZXRhZGF0YXEDXXEEWAQAAABfdHlwcQVYCQAAAGRhdGFmcmFtZXEGWAUAAABfZGF0YXEHY3BhbmRhcy5jb3JlLmludGVybmFscwpCbG9ja01hbmFnZXIKcQgpgXEJKF1xCihjcGFuZGFzLmNvcmUuaW5kZXhlcy5iYXNlCl9uZXdfSW5kZXgKcQtjcGFuZGFzLmNvcmUuaW5kZXhlcy5iYXNlCkluZGV4CnEMfXENKFgEAAAAbmFtZXEOTlgEAAAAZGF0YXEPY251bXB5LmNvcmUubXVsdGlhcnJheQpfcmVjb25zdHJ1Y3QKcRBjbnVtcHkKbmRhcnJheQpxEUsAhXESQwFicROHcRRScRUoSwFLAYVxFmNudW1weQpkdHlwZQpxF1gCAAAATzhxGEsASwGHcRlScRooSwNYAQAAAHxxG05OTkr/////Sv////9LP3RxHGKJXXEdWAEAAABhcR5hdHEfYnWGcSBScSFoC2NwYW5kYXMuY29yZS5pbmRleGVzLnJhbmdlClJhbmdlSW5kZXgKcSJ9cSMoaA5OWAUAAABzdGFydHEkSwBYBAAAAHN0b3BxJUsEWAQAAABzdGVwcSZLAXWGcSdScShlXXEpaBBoEUsAhXEqaBOHcStScSwoSwFLAUsEhnEtaBdYAgAAAGk4cS5LAEsBh3EvUnEwKEsDWAEAAAA8cTFOTk5K/////0r/////SwB0cTJiiUMgAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAABxM3RxNGJhXXE1aAtoDH1xNihoDk5oD2gQaBFLAIVxN2gTh3E4UnE5KEsBSwGFcTpoGoldcTtoHmF0cTxidYZxPVJxPmF9cT9YBgAAADAuMTQuMXFAfXFBKFgGAAAAYmxvY2tzcUJdcUN9cUQoWAgAAABtZ3JfbG9jc3FFY2J1aWx0aW5zCnNsaWNlCnFGSwBLAUsBh3FHUnFIWAYAAAB2YWx1ZXNxSWgsdWFYBAAAAGF4ZXNxSmgKdXN0cUtidWIu'

因此 64 编码的字符串也是类似字节的字符串,但它不包含十六进制转义序列,因此当它转换为字符串时,在将其编码为字节时仍然保留该字符串。

现在,让我们模仿 hug 对字符串的作用,正如您所注意到的:

>>> hug_pickled_str = pickled_b64.decode('utf-8')
>>> hug_pickled_str
'gANjcGFuZGFzLmNvcmUuZnJhbWUKRGF0YUZyYW1lCnEAKYFxAX1xAihYCQAAAF9tZXRhZGF0YXEDXXEEWAQAAABfdHlwcQVYCQAAAGRhdGFmcmFtZXEGWAUAAABfZGF0YXEHY3BhbmRhcy5jb3JlLmludGVybmFscwpCbG9ja01hbmFnZXIKcQgpgXEJKF1xCihjcGFuZGFzLmNvcmUuaW5kZXhlcy5iYXNlCl9uZXdfSW5kZXgKcQtjcGFuZGFzLmNvcmUuaW5kZXhlcy5iYXNlCkluZGV4CnEMfXENKFgEAAAAbmFtZXEOTlgEAAAAZGF0YXEPY251bXB5LmNvcmUubXVsdGlhcnJheQpfcmVjb25zdHJ1Y3QKcRBjbnVtcHkKbmRhcnJheQpxEUsAhXESQwFicROHcRRScRUoSwFLAYVxFmNudW1weQpkdHlwZQpxF1gCAAAATzhxGEsASwGHcRlScRooSwNYAQAAAHxxG05OTkr/////Sv////9LP3RxHGKJXXEdWAEAAABhcR5hdHEfYnWGcSBScSFoC2NwYW5kYXMuY29yZS5pbmRleGVzLnJhbmdlClJhbmdlSW5kZXgKcSJ9cSMoaA5OWAUAAABzdGFydHEkSwBYBAAAAHN0b3BxJUsEWAQAAABzdGVwcSZLAXWGcSdScShlXXEpaBBoEUsAhXEqaBOHcStScSwoSwFLAUsEhnEtaBdYAgAAAGk4cS5LAEsBh3EvUnEwKEsDWAEAAAA8cTFOTk5K/////0r/////SwB0cTJiiUMgAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAwAAAAAAAABxM3RxNGJhXXE1aAtoDH1xNihoDk5oD2gQaBFLAIVxN2gTh3E4UnE5KEsBSwGFcTpoGoldcTtoHmF0cTxidYZxPVJxPmF9cT9YBgAAADAuMTQuMXFAfXFBKFgGAAAAYmxvY2tzcUJdcUN9cUQoWAgAAABtZ3JfbG9jc3FFY2J1aWx0aW5zCnNsaWNlCnFGSwBLAUsBh3FHUnFIWAYAAAB2YWx1ZXNxSWgsdWFYBAAAAGF4ZXNxSmgKdXN0cUtidWIu'

现在让字符串在服务器端可用:

>>> ss_df = pickle.loads(base64.b64decode(hug_pickled_str.encode()))
>>> ss_df
   a
0  0
1  1
2  2
3  3

因此,您需要对 pickled 字符串进行 Base64 编码,并将该字符串作为数据传递给您的 API。

关于python - 如何使用 POST 方法发送 pandas 数据帧并在 Hug/其他 REST API 框架中接收它? pickle.loads 发送后无法取消pickle,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51473693/

相关文章:

python - Plotly:如何绘制具有跨不同列的匹配行的 Sankey 图?

python - keras中多标签图像的一种热编码

python - 为什么 float() 比 int() 快?

单值返回与嵌套的 Python 列表理解

python - 如何使用 pandas 中的列表和索引之间的比较来删除列表中的项目?

python urllib 错误

http - Curl/Guzzle - 获取没有正文的标题/响应代码

javascript - Node.js 请求 CERT_HAS_EXPIRED

python - 如何在 Python 中保存大型(热门)词典?

python - 如何有效计算不同长度(内部为na)的系列之间的成对距离?