37682


[昨日質問させていただいた内容](https://ja.stackoverflow.com/q/37599/19110)は解決したのですが、その後

[code]

    POST data should be bytes or an iterable of bytes. It cannot be of type str.
    
[/code]

というエラーコードに悩んでいます。  
調べたところスタックトレースを見ると良いともあったのですが、できるように色々いじって見たのですが、他にエラーが増えてしまうだけでした。

プログラムとして現状

[code]

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    #Docomoの雑談対話APIを使ってチャットできるスクリプト
    
    import sys
    import urllib.request
    import json
    import os
    
    APP_URL = 'https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue'
    
    class DocomoChat(object):
        #Docomoの雑談対話APIでチャット
    
        def __init__(self, api_key):
            api_key = os.environ.get('DOCOMO_DIALOGUE_API_KEY', api_key)
            super(DocomoChat, self).__init__()
            self.api_url = APP_URL + ('?APIKEY=%s'%api_key)
            self.context, self.mode = None, None
    
        def __send_message(self, input_message='', custom_dict=None):
            req_data = {'utt': input_message}
            if self.context:
                req_data['context'] = self.context
            if self.mode:
                req_data['mode'] = self.mode
            if custom_dict:
                req_data.update(custom_dict)
            request = urllib.request.Request(self.api_url, json.dumps(req_data))
            request.add_header('Content-Type', 'application/json')
            try:
                response = urllib.request.urlopen(request)
            except Exception as e:
                print (e)
                sys.exit()
            return response
    
        def __process_response(self, response):
            resp_json = json.load(response)
            self.context = resp_json['context'].encode('utf-8')
            self.mode    = resp_json['mode'].encode('utf-8')
            return resp_json['utt'].encode('utf-8')
    
        def send_and_get(self, input_message):
            response = self.__send_message(input_message)
            received_message = self.__process_response(response)
            return received_message
    
    
        def set_name(self, name, yomi):
            response = self.__send_message(custom_dict={'nickname': name, 'nickname_y': yomi})
            received_message = self.__process_response(response)
            return received_message
    
    
    def main():
        chat = DocomoChat('api_key')
        resp = chat.set_name('あなたのニックネーム', 'ニックネームのヨミガナ')
        print (('相手　 : %s'% resp))
        message = ''
        while message != 'バイバイ':
            message = eval(input('あなた : '))
            resp = chat.send_and_get(message)
            print (('相手　 : %s'%resp))
    
    if __name__ == "__main__":
        main()
    
[/code]

このような状態です。

質問ばかりの頼りきりで申し訳有りません。  
宜しくお願いします。


CC BY-SA 3.0
2017-08-31T17:02:23.040
37648
2017-09-04T05:51:14.730
2017-09-02T15:11:50.390
19110
25104
question
python python3
ドコモAPIを使用した対話プログラムのエラー: POST data should be bytes or an iterable of bytes


[`urllib.request.Request`](https://docs.python.jp/3/library/urllib.request.html#urllib.request.Request)
の `data` 引数は bytes 型である必要があります。この引数に対して現状のコードでは
[`json.dumps`](https://docs.python.jp/3/library/json.html#json.dumps)
関数の戻り値がそのまま渡されていますが、これは str 型です。今回は `Content-Type` が `application/json`
なのでそのまま bytes 型に変換し

[code]

    json.dumps(JSONを表すデータ).encode('何らかのエンコーディング')
    
[/code]

という形で渡してあげれば良いです。 (補足：`Content-Type` が標準の `application/x-www-form-urlencoded`
の場合、更に URL エンコードする必要があります。)

念の為、こちらで動作したコードを [gist
に上げておきました](https://gist.github.com/nekketsuuu/e7b7a4f232af46a74306660c5c41a211)。

* * *

### 参考

  * [docomo 雑談対話 API のリファレンス](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=dialogue&p_name=api_1#tag01)
  * [Python3: JSON POST Request WITHOUT requests library](https://stackoverflow.com/q/25491541/5989200) \-- Stack Overflow

### 追記

他に気づいた点を簡単に書いておきます。

  * Python 2 の `raw_input` 関数に対応する Python 3 の関数は `input` です。
  * 一般に、任意の入力が考えられる場所で `eval` を使うのは危ないです。今回の場合、単に不要です。
  * Python 2 と Python 3 では Unicode 文字列の扱いが変わったため、 `str.encode` 関数の挙動が変わっています。Python 2 では Unicode 文字列と単なる文字列が区別されていたため、文字列から文字列へ変換するために `encode` 関数が使われていました。Python 3 では文字列型の区別が無くなり、更に bytes 型が追加されたため、`encode` 関数は str 型から bytes 型への変換に使われるようになりました。簡単なサンプルコードを下に示しておきます。
[code]     $ python2

    >>> u'あいう'
    u'\u3042\u3044\u3046'
    >>> u'あいう'.encode('utf-8')
    '\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'
    >>> type(u'あいう')
    <type 'unicode'>
    >>> type(u'あいう'.encode('utf-8'))
    <type 'str'>
    >>> exit()
    
    $ python3
    >>> u'あいう'    # Python 3 では u が不要です
    'あいう'
    >>> 'あいう'.encode('utf-8')
    b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'
    >>> type(u'あいう')
    <class 'str'>
    >>> type('あいう'.encode('utf-8'))
    <class 'bytes'>
    >>> exit()
    
[/code]


CC BY-SA 3.0
2017-09-02T15:31:06.317
37682
2017-09-04T05:51:14.730
2017-09-04T05:51:14.730
19110
19110
37648
answer