├── .gitignore ├── MANIFEST ├── README.md ├── setup.py ├── test.py └── wiz ├── __init__.py ├── api.py ├── auth.py ├── base.py ├── client.py ├── config.py ├── e.py ├── note.py ├── notebook.py ├── request.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.pyc 4 | build/ 5 | dist/ 6 | 7 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | setup.py 3 | wiz/__init__.py 4 | wiz/api.py 5 | wiz/auth.py 6 | wiz/base.py 7 | wiz/client.py 8 | wiz/config.py 9 | wiz/e.py 10 | wiz/note.py 11 | wiz/notebook.py 12 | wiz/request.py 13 | wiz/utils.py 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 为知笔记python-sdk 2 | 3 | 由于公司业务需要,需要从为知上同步数据过来,就做了这个功能还不全的sdk.(要做全也很简单,只不过现在这个已经满足我们需要了). 4 | 5 | `开发/测试环境: ubuntu 12.04 python2.7.3` 6 | 7 | ## 安装 8 | 9 | > sudo python setup.py install 10 | 11 | > sudo pip install wiz 12 | 13 | ## 使用 14 | 15 | ### 创建client 16 | 17 | > client = Wiz('youremail', 'yourpassword') 18 | 19 | 20 | ### 列出所有笔记本 21 | 22 | > notebooks = client.ls_notebooks() 23 | 24 | ### 列出某一笔记本上的所有笔记 25 | 26 | > notebook.ls() 27 | 28 | ### 查看某一笔记html内容和text内容 29 | 30 | > note.data.body 31 | 32 | > note.data.text 33 | 34 | ### 查看某一笔记中的图片 35 | 36 | > note.data.images 37 | 38 | ### 下载某一笔记上的第一张图片 39 | 40 | > note.data.images[0].data # 返回图片二进制数据 41 | 42 | ### 查看某一笔记上带的附件 43 | 44 | > note.data.attachments 45 | 46 | ### 下载某一笔记上的第一个附件 47 | 48 | > note.data.attachments[0].data # 下载下来是附件本身的二进制数据,非web版上下载的zip 49 | 50 | 51 | ## 其他功能 52 | 53 | 还有删除笔记本,重命名笔记本.由于我只需要这些功能 就行了,其他的接口,比如对笔记本做增加,删除,修改. **如果您需要,告诉我,我可以给您加上去.** 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-06 14:25:42 6 | # Filename : setup.py 7 | # Description : 8 | from distutils.core import setup 9 | import os 10 | import wiz 11 | 12 | setup( 13 | name = 'wiz', 14 | version = wiz.version, 15 | author = 'moment-x', 16 | author_email = 'q8886888@qq.com', 17 | license = 'GPL3', 18 | description = 'wiz note sdk', 19 | url = 'https://github.com/lujinda/wiz-python', 20 | packages = [ 21 | 'wiz', 22 | ] 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:58:01 6 | # Filename : test.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | 10 | from wiz.client import Wiz 11 | from wiz.note import Note 12 | 13 | def down_images(note): 14 | assert isinstance(note, Note) 15 | for image in note.data.images: 16 | with open('/tmp/' + image.name, 'wb') as fd: 17 | fd.write(image.data) 18 | 19 | def down_attrs(note): 20 | assert isinstance(note, Note) 21 | for attr in note.data.attachments: 22 | with open('/tmp/' + attr.name, 'wb') as fd: 23 | fd.write(attr.data) 24 | 25 | wiz = Wiz('q8886888@qq.com', 'zxc123') 26 | note_count = 0 27 | for notebook in wiz.ls_notebooks(): 28 | print('notebook', notebook.name) 29 | for note in notebook.ls(): 30 | print(note.title) 31 | note_count += 1 32 | 33 | print(note_count) 34 | 35 | # notebook = wiz.ls_notebooks()[0] # ls_notebooks 列出所有笔记本 36 | # note = notebook.ls()[0] # notebook.ls 列出该笔记本下的所有笔记 37 | # print(note.data.text) # 笔记数据,以文本形式列出 38 | # 39 | # down_images(note) 40 | # down_attrs(note) 41 | # 42 | # print(wiz.find_notes('a*')[0].data.body) 43 | # 44 | -------------------------------------------------------------------------------- /wiz/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:58:44 6 | # Filename : __init__.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | 10 | version = '0.6' 11 | 12 | -------------------------------------------------------------------------------- /wiz/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:37:51 6 | # Filename : api.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | try: 10 | import httplib 11 | from urllib import quote_plus 12 | except ImportError as e: # py3 13 | from http import client as httplib 14 | from io import StringIO 15 | from urllib.parse import quote_plus 16 | 17 | from wiz import request, config, e 18 | from functools import wraps 19 | 20 | __all__ = ['api_request'] 21 | 22 | def downattr(path): 23 | url = config.host + path 24 | response = request.get(url) 25 | return response.raw_body 26 | 27 | def api_request(path, method = 'GET', data = None, cookies = None): 28 | url = config.host + path 29 | data = data or {} 30 | data['api_version'] = config.version 31 | data['client_type'] = config.client_type 32 | headers = { 33 | 'Accept' : 'application/json, text/javascript, */*; q=0.01', 34 | 'Accept-Language' : 'zh-CN,en-US;q=0.7,en;q=0.3', 35 | } 36 | method = getattr(request, method.lower()) 37 | response = method(url = url, data = data, headers = headers, 38 | cookies = cookies) 39 | 40 | result = response.json() 41 | result['code'] = int(result['code']) 42 | error = '' 43 | message = result['message'] 44 | if result['code'] != 200: 45 | error = message 46 | 47 | return result, error 48 | 49 | -------------------------------------------------------------------------------- /wiz/auth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-05 13:53:48 6 | # Filename : auth.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | from wiz.api import api_request 10 | from wiz import e 11 | 12 | class AuthManager(object): 13 | def __new__(cls, *args, **kwargs): 14 | if hasattr(cls, '_instance'): 15 | return cls._instance 16 | 17 | _instance = object.__new__(cls, *args, **kwargs) 18 | AuthManager._instance = _instance 19 | 20 | return _instance 21 | 22 | def __init__(self, username = None, password = None, access_token = None): 23 | self.__username, self.__password = username, password 24 | self.access_token = access_token 25 | 26 | def login(self, username = None, password = None): 27 | if self.access_token: 28 | return 29 | username = username or self.__username 30 | password = password or self.__password 31 | 32 | result, error = api_request('/api/login', method = 'POST', 33 | data = {'user_id': username, 'password': password, 34 | 'token': ''}) 35 | 36 | if error: 37 | raise e.WizLoginFailed(error) 38 | 39 | self.__login_success_after(result) 40 | 41 | def __login_success_after(self, login_response): 42 | self.access_token = { 43 | 'token' : login_response['token'], 44 | 'user_id' : login_response['user']['user_id'], 45 | 'kb_guid' : login_response['kb_guid'], 46 | 'cert_no' : login_response['cookie_str'], 47 | } 48 | 49 | -------------------------------------------------------------------------------- /wiz/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-05 13:58:25 6 | # Filename : base.py 7 | # Description : 8 | from __future__ import unicode_literals, print_function 9 | 10 | from wiz import e 11 | from wiz.api import api_request 12 | 13 | __all__ = ['BaseManager'] 14 | 15 | class BaseManager(object): 16 | def __init__(self, auth_manager): 17 | self._auth_manager = auth_manager 18 | 19 | def api_request(self, path, method = 'GET', data = None, auto_raise = True): 20 | data = data or {} 21 | data.update(self._auth_manager.access_token) 22 | 23 | cert_no = data.pop('cert_no') 24 | wiz_id = data.get('user_id') 25 | token = data.get('token') 26 | if cert_no and wiz_id and token: 27 | cookies = { 28 | 'CertNo' : cert_no, 29 | 'WizID' : wiz_id, 30 | 'token' : token, 31 | } 32 | else: 33 | cookies = {} 34 | 35 | result, error = api_request(path, method, data, 36 | cookies) 37 | if error and auto_raise: 38 | if result['code'] == 301: 39 | raise e.WizTokenInvalid(error) 40 | else: 41 | raise e.WizException(error) 42 | 43 | return result, error 44 | 45 | 46 | -------------------------------------------------------------------------------- /wiz/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:49:06 6 | # Filename : client.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | 10 | from wiz import e, utils 11 | from wiz.api import api_request 12 | from wiz.notebook import NotebookManager 13 | from wiz.auth import AuthManager 14 | 15 | __all__ = ['Wiz'] 16 | 17 | class Wiz(object): 18 | def __init__(self, username = None, password = None, auto_login = True, access_token = None): 19 | self.auth_manager = AuthManager(username, password, access_token) 20 | self.notebook_manager = NotebookManager(self.auth_manager) 21 | if auto_login: 22 | self.login() 23 | 24 | @utils.adapter('notebook_manager') 25 | def ls_notebooks(self): 26 | pass 27 | 28 | @utils.adapter('auth_manager') 29 | def login(self, username = None, password = None): 30 | pass 31 | 32 | @property 33 | def is_logged(self): 34 | return bool(self.auth_manager.access_token.get('user_id')) 35 | 36 | def find_notes(self, keyword, count = 200): 37 | return self.notebook_manager.find_notes(keyword, count = 200) 38 | 39 | def get_access_token(self): 40 | if not self.is_logged: 41 | return {} 42 | 43 | return self.auth_manager.access_token 44 | 45 | -------------------------------------------------------------------------------- /wiz/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:38:09 6 | # Filename : config.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | 10 | host = 'https://note.wiz.cn' 11 | version = '5' 12 | client_type = 'web2.0' 13 | 14 | -------------------------------------------------------------------------------- /wiz/e.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-02 10:53:15 6 | # Filename : e.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | import sys 10 | 11 | class WizException(Exception): 12 | def __init__(self, reason): 13 | self.reason = reason 14 | Exception.__init__(self, reason) 15 | sys.stderr.write(reason + '\n') 16 | 17 | class WizLoginFailed(WizException): 18 | pass 19 | 20 | class WizTokenInvalid(WizException): 21 | pass 22 | 23 | 24 | -------------------------------------------------------------------------------- /wiz/note.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-05 17:10:26 6 | # Filename : note.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | from wiz import api 10 | from wiz.utils import ObjectDict, cache_self, ZipFile 11 | import os 12 | try: 13 | from StringIO import StringIO 14 | except ImportError: 15 | from io import StringIO 16 | 17 | class NoteData(ObjectDict): 18 | pass 19 | 20 | class Note(ObjectDict): 21 | @property 22 | @cache_self 23 | def data(self): 24 | return self._manager.get_note_data(self.guid) 25 | 26 | class _AttrData(object): 27 | def __init__(self, url, name = None): 28 | self.url = url 29 | self.name = name or os.path.basename(url) 30 | 31 | @property 32 | @cache_self 33 | def data(self): 34 | return api.downattr(self.url) 35 | 36 | class NoteImageData(_AttrData): 37 | pass 38 | 39 | class NoteAttrData(_AttrData): 40 | @property 41 | @cache_self 42 | def data(self): 43 | _zip_data = super(NoteAttrData, self).data 44 | _zip_io = StringIO(_zip_data) 45 | with ZipFile(_zip_io) as _zip_instance: 46 | return _zip_instance.read(_zip_instance.namelist()[0]) 47 | 48 | -------------------------------------------------------------------------------- /wiz/notebook.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-11-05 13:55:24 6 | # Filename : notebook.py 7 | # Description : 8 | from __future__ import print_function, unicode_literals 9 | from wiz.base import BaseManager 10 | from wiz.utils import ObjectDict, get_text_from_html, get_image_from_html 11 | from wiz.note import Note, NoteData, NoteImageData, NoteAttrData 12 | 13 | class Notebook(ObjectDict): 14 | def rename(self, new_title): 15 | return self._manager.rename(self['id'], new_title) 16 | 17 | def rm(self): 18 | return self._manager.rm(self['id']) 19 | 20 | def ls(self, count = 200): 21 | """list all notes in this notebook""" 22 | return self._manager.ls_notes(self['id'], count) 23 | 24 | def find(self, keyword): 25 | return self._manager.find_notes(self['id'], keyword) 26 | 27 | class NotebookManager(BaseManager): 28 | def ls(self): 29 | result, error = self.api_request('/api/category/all') 30 | notebooks = [] 31 | 32 | # 用户可能会修改默认的那个笔记本名 33 | if '/My Notes/' not in [n['location'] for n in result['list']]: 34 | result['list'].append({u'type': u'category', u'location': u'/My Notes/', 35 | u'category_name': '我的笔记'}) 36 | 37 | for raw_notebook in result['list']: 38 | notebooks.append(Notebook({ 39 | 'name' : raw_notebook['category_name'], 40 | 'path' : raw_notebook['location'], 41 | 'id' : raw_notebook['location'], 42 | '_manager' : self, 43 | })) 44 | 45 | return notebooks 46 | 47 | 48 | def rename(self, _id, new_title): 49 | result, error = self.api_request('/api/category/item', method = 'PUT', data = { 50 | 'old_category_path' : _id, 51 | 'new_title' : new_title, 52 | }) 53 | 54 | def rm(self, _id): 55 | result, error = self.api_request('/api/category/item', method = 'DELETE', data = { 56 | 'old_category_path' : _id, 57 | }) 58 | 59 | def ls_notes(self, _id, count = 200): 60 | return self.__get_notes('category', _id, count) 61 | 62 | def __get_notes(self, action_cmd, action_value, count = 200): 63 | result, error = self.api_request('/api/document/list', method = 'GET', data = { 64 | 'action_cmd' : action_cmd, 65 | 'action_value' : action_value, 66 | 'count' : count, 67 | 'auto' : 'true', 68 | }) 69 | 70 | notes = [] 71 | for raw_note in result['list']: 72 | notes.append(Note({ 73 | 'title' : raw_note['document_title'], 74 | 'tag_guids' : raw_note['document_tag_guids'], 75 | 'version' : raw_note['version'], # 被修改了多少次 76 | 'create_time' : raw_note['dt_created'], 77 | 'modify_time' : raw_note['dt_data_modified'], 78 | 'md5' : raw_note['data_md5'], 79 | 'attachment_count': raw_note['document_attachment_count'], 80 | 'guid' : raw_note['document_guid'], 81 | '_manager' : self, 82 | })) 83 | 84 | return notes 85 | 86 | def find_notes(self, keyword, count = 200): 87 | return self.__get_notes('keyword', keyword, count) 88 | 89 | def __get_note_attrs(self, guid): 90 | result, error = self.api_request('/api/attachment/list', method = 'GET', data = { 91 | 'document_guid' : guid, 92 | }) 93 | 94 | attrs = [{'name': attr_data['attachment_name'], 'url': attr_data['download_url']}\ 95 | for attr_data in result['list']] 96 | 97 | return attrs 98 | 99 | def get_note_data(self, guid): 100 | result, error = self.api_request('/api/document/info', method = 'GET', data = { 101 | 'document_guid' : guid, 102 | }) 103 | 104 | document_info = result['document_info']; 105 | if int(document_info['document_attachment_count']): 106 | attachments = self.__get_note_attrs(guid) 107 | else: 108 | attachments = [] 109 | 110 | note_data = NoteData({ 111 | 'body' : document_info['document_body'], 112 | 'md5' : document_info['data_md5'], 113 | 'text' : get_text_from_html(document_info['document_body']), 114 | 'images' : [NoteImageData(img_url) for img_url in get_image_from_html(document_info['document_body'])], 115 | 'attachments' : [NoteAttrData(**attr_data) for attr_data in attachments], 116 | }) 117 | 118 | return note_data 119 | 120 | -------------------------------------------------------------------------------- /wiz/request.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf-8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com.com 5 | # Last modified : 2015-06-20 10:41:41 6 | # Filename : request.py 7 | # Description : from __future__ import unicode_literals 8 | try: 9 | import httplib # py2 10 | try: 11 | from CStringIO import StringIO 12 | except ImportError: 13 | from StringIO import StringIO 14 | from urllib import quote_plus 15 | except ImportError as e: 16 | from http import client as httplib # py3 17 | from io import StringIO 18 | 19 | 20 | import urllib 21 | import logging 22 | import json 23 | import gzip 24 | import mimetypes 25 | import utils 26 | 27 | 28 | class CookieManager(object): 29 | def __init__(self): 30 | self.cookies = {} 31 | 32 | def set_cookie(self, set_cookie, value = None): 33 | if not set_cookie: 34 | return 35 | 36 | if value: 37 | name = set_cookie 38 | self.cookies[name] = value 39 | return 40 | 41 | 42 | for cookie_string in set_cookie.split(','): 43 | cookie_string = cookie_string.strip() 44 | cookie_item = cookie_string.split(';')[0].strip() 45 | name, value = cookie_item.split('=', 1) 46 | 47 | self.cookies[name.strip()] = value.strip() 48 | 49 | def generate_cookie(self): 50 | if not self.cookies: 51 | return None 52 | 53 | return [';'.join(["%s=%s" % (cookie_name, 54 | cookie_value) for cookie_name, cookie_value in self.cookies.items() 55 | ])] 56 | 57 | cookie_manager = CookieManager() 58 | 59 | 60 | def urlencode(data): 61 | params = [] 62 | for key, value in data.items(): 63 | if value == None: 64 | continue 65 | 66 | params.append("%s=%s" % (utils.utf8(key), 67 | quote_plus(utils.utf8(value)))) 68 | 69 | params_string = '&'.join(params) 70 | return params_string 71 | 72 | def get_mime(file_name): 73 | return mimetypes.guess_type(file_name)[0] or 'application/octet-stream' 74 | 75 | class Headers(dict): 76 | def __init__(self, headers): 77 | for _key, _values in headers: 78 | self.setdefault(_key, []).append(_values) 79 | 80 | super(Headers, self).__init__() 81 | 82 | def get_header(self, name, value = None): 83 | name = name.strip() 84 | if name not in self.keys(): 85 | return value 86 | 87 | return self[name][0] 88 | 89 | def get_headers(self, name): 90 | name = name.strip() 91 | return self.get(name, []) 92 | 93 | class Response(object): 94 | def __init__(self, response): 95 | self.response = response 96 | self.status_code = response.status 97 | self.reason = response.reason 98 | self.headers = Headers(response.getheaders()) 99 | cookie_manager.set_cookie(self.headers.get_header('set-cookie')) 100 | self.raw_body = None 101 | self.content_type = self.headers.get_header('content-type', '') 102 | self.content = self.__parse_content(response) 103 | self.text = self.content 104 | 105 | def json(self): 106 | if 'json' in self.content_type: 107 | try: 108 | return json.loads(self.content) 109 | except ValueError: 110 | return {} 111 | 112 | else: 113 | return {} 114 | 115 | def __parse_content(self, response): 116 | """返回正文内容,结果是unicode""" 117 | content_encoding = self.headers.get('content-encoding', '') 118 | content = response.read() 119 | self.raw_body = content 120 | if content_encoding == 'gzip': 121 | content = self.ungzip_content(content) 122 | 123 | content_type = response.getheader('content-type').lower() 124 | if not ('json' in content_type or 'text' in content_type): 125 | return content 126 | 127 | _parts = (content_type.split('charset=', 1) + ['utf-8'])[:2] 128 | charset = _parts[1] 129 | _pos = charset.find(';') 130 | if _pos != -1: 131 | charset = charset[:charset.find(';')] 132 | 133 | if charset.lower() == 'gb2312': 134 | charset = 'gbk' 135 | 136 | return content.decode(charset) 137 | 138 | def ungzip_content(self, content): 139 | _buf = StringIO(content) 140 | with gzip.GzipFile(mode = 'rb', fileobj = _buf) as _gzip_file: 141 | _content = _gzip_file.read() 142 | 143 | _buf.close() 144 | 145 | return _content 146 | 147 | class Request(object): 148 | def __init__(self, method, url, headers = None, data = None, files = None, debug = False, cookies = None): 149 | assert url.startswith('http') 150 | url = utils.utf8(url) 151 | self.url = url 152 | self.method = method 153 | self.data = data or {} 154 | self.files = files 155 | self.body = None 156 | 157 | cookies = cookies or {} 158 | 159 | for name, value in cookies.items(): 160 | cookie_manager.set_cookie(name, value) 161 | 162 | _split_url = httplib.urlsplit(url) 163 | self.host = _split_url.netloc 164 | self.uri = _split_url.path 165 | 166 | if _split_url.query: 167 | self.uri += '?' + _split_url.query 168 | 169 | if _split_url.scheme == 'https': 170 | Connection = httplib.HTTPSConnection 171 | else: 172 | Connection = httplib.HTTPConnection 173 | 174 | self.__conn = Connection(host = self.host) 175 | self.__conn.set_debuglevel(debug and logging.DEBUG or 0) 176 | 177 | self.headers = headers or {} 178 | self.generate_header(headers) 179 | 180 | def set_header(self, key, value): 181 | self.headers[utils.utf8(key)] = utils.utf8(value) 182 | 183 | def generate_header(self, headers = None): 184 | headers = headers or {} 185 | self.set_header('User-Agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:36.0) Gecko/20100101 Firefox/36.0)') 186 | self.set_header('Host', self.host) 187 | cookie_header = cookie_manager.generate_cookie() 188 | if cookie_header: 189 | self.set_header('Cookie', cookie_header) 190 | 191 | 192 | for key, value in headers.items(): 193 | self.set_header(key, value) 194 | 195 | def __request(self): 196 | def utf8_headers(headers): 197 | _headers = {} 198 | for key, value in headers.items(): 199 | _headers[utils.utf8(key)] = utils.utf8(value) 200 | 201 | return _headers 202 | 203 | conn = self.__conn 204 | conn.request(utils.utf8(self.method), utils.utf8(self.uri), body = utils.utf8(self.body), 205 | headers = utf8_headers(self.headers)) 206 | 207 | response = conn.getresponse() 208 | 209 | return Response(response) 210 | 211 | def request(self): 212 | return self.__request() 213 | 214 | def post(self): 215 | if not self.files: 216 | self.body = urlencode(self.data) 217 | self.set_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') 218 | return self.__request() 219 | boundary = '---------------------------14484134827975982172037180455' 220 | self.set_header('Content-Type', 'multipart/form-data; boundary=' + boundary) 221 | self.body = self.__made_multipart_data(boundary) 222 | return self.__request() 223 | 224 | def put(self): 225 | return self.post() 226 | 227 | def delete(self): 228 | return self.post() 229 | 230 | def __made_multipart_data(self, boundary): 231 | body = '' 232 | CRLF = '\r\n' 233 | for _name in self.files: 234 | _file = self.files[_name] 235 | if isinstance(_file, (list, tuple)): 236 | file_name, file_content, mime_type = (_file + ('', ''))[:3] 237 | mime_type = mime_type or get_mime(file_name) 238 | else: 239 | file_name = _name 240 | file_content = _file 241 | mime_type = get_mime(file_name) 242 | 243 | _body_string = '--{boundary}{CRLF}Content-Disposition: form-data; name="{name}"; filename="{filename}"{CRLF}Content-Type: {mimetype}{CRLF2}{content}{CRLF}'.format(boundary = boundary, name = _name, filename = file_name, CRLF = CRLF, mimetype = mime_type, CRLF2 = CRLF * 2, content = file_content) 244 | body += _body_string 245 | 246 | for _key, _value in self.data.items(): 247 | _body_string = '--{boundary}{CRLF}Content-Disposition: form-data; name="{name}"{CRLF2}{content}{CRLF}'.format(boundary = boundary, name = _key, CRLF = CRLF, CRLF2 = CRLF * 2, content = _value) 248 | 249 | body += _body_string 250 | 251 | body += '--' + boundary + '--' 252 | 253 | return body 254 | 255 | def get(self): 256 | self.body = None 257 | if '?' in self.url: 258 | _split_char = '&' 259 | else: 260 | _split_char = '?' 261 | 262 | if self.data: 263 | self.uri += _split_char + urlencode(self.data) 264 | 265 | return self.__request() 266 | 267 | 268 | def get(url, *args, **kwargs): 269 | request = Request('GET', url, *args, **kwargs) 270 | return request.get() 271 | 272 | def post(url, *args, **kwargs): 273 | request = Request('POST', url, *args, **kwargs) 274 | return request.post() 275 | 276 | def put(url, *args, **kwargs): 277 | request = Request('PUT', url, *args, **kwargs) 278 | return request.put() 279 | 280 | def delete(url, *args, **kwargs): 281 | request = Request('DELETE', url, *args, **kwargs) 282 | 283 | return request.delete() 284 | 285 | 286 | -------------------------------------------------------------------------------- /wiz/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #coding:utf8 3 | # Author : tuxpy 4 | # Email : q8886888@qq.com 5 | # Last modified : 2015-03-19 16:40:12 6 | # Filename : cattle/utils.py 7 | # Description : 8 | 9 | from __future__ import unicode_literals 10 | import base64 11 | import sys 12 | from functools import wraps 13 | import re 14 | import zipfile 15 | from HTMLParser import HTMLParser 16 | 17 | is_py3 = sys.version[0] == '3' 18 | 19 | def urlsafe_b64encode(s): 20 | """ 21 | 为了兼容python2的,返回str,并不返回 bytes 22 | """ 23 | if not isinstance(s, bytes): 24 | s = s.encode('utf-8') 25 | value = base64.urlsafe_b64encode(s) 26 | 27 | return value.decode() 28 | 29 | def native_str(*args): 30 | """ 31 | 把py2 的参数中str转成unicode 32 | """ 33 | if is_py3: 34 | result = args 35 | else: 36 | result = map(lambda s: isinstance(s, str) and s.decode('utf-8') or s, args) 37 | 38 | return result if len(args) != 1 else result[0] 39 | 40 | def utf8(s): 41 | if is_py3: 42 | return isinstance(s, str) and s.encode('utf-8') or s 43 | else: 44 | return isinstance(s, unicode) and s.encode('utf-8') or str(s) 45 | 46 | 47 | def adapter(manager_name): 48 | def outer_wrap(func): 49 | @wraps(func) 50 | def inner_wrap(self, *args, **kwargs): 51 | _manager = getattr(self, manager_name) 52 | func_name = func.__name__.split('_')[0] 53 | _manager_func = getattr(_manager, func_name) 54 | return _manager_func(*args, **kwargs) 55 | 56 | return inner_wrap 57 | 58 | return outer_wrap 59 | 60 | class ObjectDict(object): 61 | def __init__(self, _data): 62 | self.__data = {} 63 | for _key, _value in _data.items(): 64 | if _key.startswith('_'): 65 | setattr(self, _key, _value) 66 | else: 67 | self.__data[_key] = _value 68 | 69 | def __str__(self): 70 | return self.__data.__str__() 71 | 72 | def __repr__(self): 73 | return self.__data.__repr__() 74 | 75 | def __getattr__(self, name): 76 | try: 77 | return self[name] 78 | except KeyError as e: 79 | raise AttributeError 80 | 81 | def __getitem__(self, name): 82 | return self.__data[name] 83 | 84 | 85 | 86 | # https://github.com/lujinda/wiz-python/issues/5 87 | class _DeHTMLParser(HTMLParser): 88 | def __init__(self): 89 | HTMLParser.__init__(self) 90 | self.__text = [] 91 | 92 | def handle_data(self, data): 93 | if self.lasttag in ("script", "style"): 94 | return 95 | 96 | text = data.strip() 97 | if len(text) > 0: 98 | text = re.sub('[ \t\r\n]+', ' ', text) 99 | self.__text.append(text + ' ') 100 | 101 | def handle_starttag(self, tag, attrs): 102 | if tag == 'p': 103 | self.__text.append('\n\n') 104 | elif tag == 'br': 105 | self.__text.append('\n') 106 | 107 | def handle_startendtag(self, tag, attrs): 108 | if tag == 'br': 109 | self.__text.append('\n\n') 110 | 111 | def text(self): 112 | return ''.join(self.__text).strip() 113 | 114 | 115 | 116 | def get_text_from_html(html): 117 | html = native_str(html) 118 | parser = _DeHTMLParser() 119 | parser.feed(html) 120 | parser.close() 121 | return parser.text() 122 | 123 | 124 | def get_image_from_html(html): 125 | return re.findall(r'