├── .gitignore ├── MANIFEST ├── wiz ├── __init__.py ├── config.py ├── e.py ├── note.py ├── client.py ├── base.py ├── api.py ├── auth.py ├── utils.py ├── notebook.py └── request.py ├── setup.py ├── README.md └── test.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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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'