├── src ├── icon.png ├── alfred │ ├── cache.pyc │ ├── core.pyc │ ├── util.pyc │ ├── config.pyc │ ├── request.pyc │ ├── storage.pyc │ ├── __init__.pyc │ ├── feedback.pyc │ ├── util.py │ ├── __init__.py │ ├── config.py │ ├── storage.py │ ├── cache.py │ ├── feedback.py │ ├── core.py │ └── request.py └── info.plist ├── lookupip.alfredworkflow └── README.md /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/icon.png -------------------------------------------------------------------------------- /src/alfred/cache.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/cache.pyc -------------------------------------------------------------------------------- /src/alfred/core.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/core.pyc -------------------------------------------------------------------------------- /src/alfred/util.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/util.pyc -------------------------------------------------------------------------------- /src/alfred/config.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/config.pyc -------------------------------------------------------------------------------- /src/alfred/request.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/request.pyc -------------------------------------------------------------------------------- /src/alfred/storage.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/storage.pyc -------------------------------------------------------------------------------- /lookupip.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/lookupip.alfredworkflow -------------------------------------------------------------------------------- /src/alfred/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/__init__.pyc -------------------------------------------------------------------------------- /src/alfred/feedback.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kodango/lookup-ip/HEAD/src/alfred/feedback.pyc -------------------------------------------------------------------------------- /src/alfred/util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import hashlib, random 4 | 5 | hashDigest = lambda s: hashlib.md5(s).hexdigest() 6 | 7 | uid = lambda: hashDigest('{}'.format(random.getrandbits(25))) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Intro 2 | ===== 3 | 4 | Alfred 2 workflow: 根据淘宝地址库来查询 IP 地址信息 5 | 6 | Taobao IP 地址库: http://ip.taobao.com/index.php 7 | 8 | Usage 9 | ===== 10 | 11 | Alfred 框中输入 lip, 参数可以为: 12 | 13 | lip # 查询本机的 IP 地址 14 | lip 199.91.73.222 178.79.131.110 # 查询指定的多个 IP 地址 15 | lip 199.91.73.222,178.79.131.110 # 查询指定的多个 IP 地址 16 | lip 199.91.73.222178.79.131.110 # 查询指定的多个 IP 地址 17 | -------------------------------------------------------------------------------- /src/alfred/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Alfred Python 4 | A simple python module for alfred workflow。 5 | 6 | JinnLynn 7 | http://jeeker.net 8 | The MIT License 9 | 10 | For more information, see the project page: 11 | https://github.com/JinnLynn/alfred-python 12 | ''' 13 | from __future__ import absolute_import, division, unicode_literals 14 | 15 | __version__ = '0.3.1' 16 | __author__ = 'JinnLynn ' 17 | __license__ = 'The MIT License' 18 | __copyright__ = 'Copyright 2013 JinnLynn' 19 | 20 | from .core import * 21 | from .feedback import Feedback, Item 22 | from . import util 23 | from . import cache 24 | from . import config 25 | from . import storage 26 | from . import request 27 | -------------------------------------------------------------------------------- /src/alfred/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import os, json, codecs 4 | 5 | from . import core 6 | 7 | def _getFilepath(): 8 | config_dir = os.path.join(core._config_base_dir, core.bundleID()) 9 | if not os.path.exists(config_dir): 10 | os.makedirs(config_dir) 11 | return os.path.join(config_dir, 'config.json') 12 | 13 | def _save(configs): 14 | filepath = _getFilepath() 15 | with codecs.open(filepath, 'w', 'utf-8') as f: 16 | json.dump(configs, f, indent=4) 17 | 18 | def getAll(): 19 | filepath = _getFilepath() 20 | try: 21 | with codecs.open(filepath, 'r', 'utf-8') as f: 22 | return json.load(f) 23 | except: 24 | pass 25 | return {} 26 | 27 | def get(key, default=None): 28 | configs = getAll() 29 | return configs.get(key, default) 30 | 31 | def set(**kwargs): 32 | configs = getAll() 33 | for k, v in kwargs.items(): 34 | configs[k] = v 35 | _save(configs) 36 | 37 | def delete(key): 38 | configs = getAll() 39 | if key not in configs: 40 | return 41 | configs.pop(key) 42 | _save(configs) 43 | 44 | def clean(): 45 | filepath = _getFilepath() 46 | if os.path.exists(filepath): 47 | os.remove(filepath) -------------------------------------------------------------------------------- /src/alfred/storage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import os, subprocess 4 | 5 | from . import core, util, request 6 | 7 | def getLocalPath(source_link): 8 | storage_dir = os.path.join(core._storage_base_dir, core.bundleID()) 9 | if not os.path.exists(storage_dir): 10 | os.makedirs(storage_dir) 11 | _, ext = os.path.splitext(source_link) 12 | filename = '{}{}'.format(util.hashDigest(source_link), ext) 13 | return os.path.join(storage_dir, filename) 14 | 15 | def getLocalIfExists(source_link, download=False): 16 | filepath = getLocalPath(source_link) 17 | if os.path.exists(filepath): 18 | return filepath 19 | if download: 20 | singleDownload(source_link) 21 | return getLocalIfExists(source_link, False) 22 | 23 | def isLocalExists(source_link): 24 | filepath = getLocalPath(source_link) 25 | return os.path.exists(filepath) 26 | 27 | def batchDownload(links, wait=True): 28 | if isinstance(links, basestring): 29 | links = links.split(',') 30 | if not links or not isinstance(links, list): 31 | return 32 | process = [] 33 | for link in links: 34 | if isLocalExists(link): 35 | continue 36 | sub = subprocess.Popen( 37 | 'python "{}" "{}"'.format(os.path.abspath(__file__), link), 38 | shell = True, 39 | stdin = subprocess.PIPE, 40 | stdout = subprocess.PIPE, 41 | stderr = subprocess.PIPE 42 | ) 43 | if sub: 44 | process.append(sub) 45 | if wait: 46 | # 等待所有的下载进程结束 47 | for sub in process: 48 | sub.wait() 49 | 50 | def singleDownload(link): 51 | if not link or isLocalExists(link): 52 | return 53 | try: 54 | filepath = getLocalPath(link) 55 | request.download(link, filepath) 56 | except: 57 | pass 58 | 59 | if __name__ == '__main__': 60 | singleDownload(core.argv(1)) -------------------------------------------------------------------------------- /src/alfred/cache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import os, json, time, shutil, codecs 4 | import hashlib 5 | 6 | from . import util 7 | from . import core 8 | 9 | # { 'expire_time' : 0, name: '', data' : {} } 10 | 11 | _DEFAULT_EXPIRE = 60 * 60 * 24 12 | 13 | _cache_dir = os.path.join(core._cache_base_dir, core.bundleID()) 14 | 15 | def _getFilepath(name): 16 | if not os.path.exists(_cache_dir): 17 | os.makedirs(_cache_dir) 18 | # convert to md5, more safe for file name 19 | return os.path.join(_cache_dir, '{}.json'.format(util.hashDigest(name))) 20 | 21 | def _getContent(name): 22 | try: 23 | filepath = _getFilepath(name) 24 | with codecs.open(filepath, 'r', 'utf-8') as f: 25 | return json.load(f) 26 | except: 27 | pass 28 | 29 | def set(name, data, expire=_DEFAULT_EXPIRE): 30 | filepath = _getFilepath(name) 31 | try: 32 | cache = { 33 | 'expire_time' : time.time() + expire, 34 | 'name' : name, 35 | 'data' : data 36 | } 37 | with codecs.open(filepath, 'w', 'utf-8') as f: 38 | json.dump(cache, f, indent=4) 39 | except: 40 | pass 41 | 42 | def get(name, default=None): 43 | try: 44 | cache = _getContent(name) 45 | if cache['expire_time'] >= time.time(): 46 | return cache['data'] 47 | except: 48 | pass 49 | delete(name) 50 | return default 51 | 52 | def delete(name): 53 | cache_file = _getFilepath(name) 54 | if os.path.exists(cache_file): 55 | os.remove(cache_file) 56 | 57 | def clean(): 58 | cache_dir = os.path.join(core._cache_base_dir, core.bundleID()) 59 | if os.path.exists(cache_dir): 60 | shutil.rmtree(cache_dir) 61 | 62 | def cleanExpired(): 63 | if not os.path.exists(_cache_dir): 64 | return 65 | to_remove = [] 66 | for f in os.listdir(_cache_dir): 67 | if not f.endswith('.json'): 68 | continue 69 | filepath = os.path.join(_cache_dir, f) 70 | try: 71 | with codecs.open(filepath, 'r', 'utf-8') as fp: 72 | cache = json.load(fp) 73 | if cache['expire_time'] < time.time(): 74 | to_remove.append(filepath) 75 | except Exception, e: 76 | to_remove.append(filepath) 77 | for f in to_remove: 78 | os.remove(f) 79 | 80 | def timeout(name): 81 | try: 82 | cache = _getContent(name) 83 | if cache['expire_time'] >= time.time(): 84 | return cache['expire_time'] - time.time() 85 | except: 86 | pass 87 | return -1 -------------------------------------------------------------------------------- /src/alfred/feedback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | from xml.etree import ElementTree 4 | import xml.sax.saxutils as saxutils 5 | import os, copy, random 6 | 7 | from .util import uid 8 | 9 | class Item(object): 10 | def __init__(self, **kwargs): 11 | self.content = { 12 | 'title' : kwargs.get('title', ''), 13 | 'subtitle' : kwargs.get('subtitle', ''), 14 | 'icon' : kwargs.get('icon') if kwargs.get('icon') else 'icon.png' 15 | } 16 | 17 | it = kwargs.get('icontype', '').lower() 18 | self.icon_type = it if it in ['fileicon', 'filetype'] else None 19 | 20 | valid = kwargs.get('valid', None) 21 | if isinstance(valid, basestring) and valid.lower() == 'no': 22 | valid = 'no' 23 | elif isinstance(valid, bool) and not valid: 24 | valid = 'no' 25 | else: 26 | valid = None 27 | 28 | self.attrb = { 29 | 'uid' : kwargs.get('uid', uid()), 30 | 'arg' : kwargs.get('arg', None), 31 | 'valid' : valid, 32 | 'autocomplete' : kwargs.get('autocomplete', None), 33 | 'type' : kwargs.get('type', None) 34 | } 35 | 36 | self.content = dict((k, v) for k, v in self.content.items() if v is not None) 37 | self.attrb = dict((k, v) for k, v in self.attrb.items() if v is not None) 38 | 39 | def copy(self): 40 | return copy.copy(self) 41 | 42 | def getXMLElement(self): 43 | item = ElementTree.Element('item', self.attrb) 44 | for k, v in self.content.items(): 45 | attrb = {} 46 | if k == 'icon' and self.icon_type: 47 | attrb['type'] = self.icon_type 48 | sub = ElementTree.SubElement(item, k, attrb) 49 | sub.text = v 50 | return item 51 | 52 | class Feedback(object): 53 | def __init__(self): 54 | self.items = [] 55 | 56 | def __repr__(self): 57 | return self.get() 58 | 59 | def addItem(self, **kwargs): 60 | item = kwargs.pop('item', None) 61 | item = item if isinstance(item, Item) else Item(**kwargs) 62 | self.items.append(item) 63 | 64 | def clean(self): 65 | self.items = [] 66 | 67 | def isEmpty(self): 68 | return not bool(self.items) 69 | 70 | def get(self, unescape = False): 71 | ele_tree = ElementTree.Element('items') 72 | for item in self.items: 73 | ele_tree.append(item.getXMLElement()) 74 | res = ElementTree.tostring(ele_tree) #! 不要使用encoding='utf-8'等其它编码,防止某些特殊字符使得alfred解析错误 75 | if unescape: 76 | return saxutils.unescape(res) 77 | return res 78 | 79 | def output(self): 80 | print(self.get()) -------------------------------------------------------------------------------- /src/alfred/core.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import os, sys, subprocess, codecs 4 | import plistlib 5 | from datetime import datetime 6 | import unicodedata 7 | import traceback 8 | 9 | from .feedback import Feedback, Item 10 | 11 | _bundle_id = None 12 | _config_base_dir = os.path.expanduser('~/Library/Application Support/Alfred 2/Workflow Data/') 13 | _cache_base_dir = os.path.expanduser('~/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/') 14 | _log_base_dir = os.path.expanduser('~/Library/Logs/Alfred 2') 15 | _storage_base_dir = '/tmp/Alfred 2' 16 | 17 | PY2 = sys.version_info.major == 2 18 | PY3 = sys.version_info.major == 3 19 | 20 | def bundleID(): 21 | global _bundle_id 22 | if not _bundle_id: 23 | try: 24 | plist_path = os.path.abspath('./info.plist') 25 | prefs = plistlib.readPlist(plist_path) 26 | _bundle_id = prefs['bundleid'].strip() 27 | if not _bundle_id: 28 | raise ValueError('bundle id missing.') 29 | except: 30 | raiseWithFeedback() 31 | return _bundle_id 32 | 33 | def setDefaultEncodingUTF8(): 34 | reload(sys) 35 | sys.setdefaultencoding('utf-8') 36 | del sys.setdefaultencoding 37 | 38 | def decode(s): 39 | return unicodedata.normalize("NFC", s.decode("utf-8")) 40 | 41 | def log(s): 42 | log_dir = os.path.join(_log_base_dir, bundleID()) 43 | if not os.path.exists(log_dir): 44 | os.makedirs(log_dir) 45 | now = datetime.now() 46 | log_file = os.path.join(log_dir, '{}.log'.format(now.strftime('%Y-%m-%d'))) 47 | log_text = '{}: {}\n'.format(now.strftime('%Y-%m-%d %H:%M:%S.%f'), s) 48 | with codecs.open(log_file, 'a', 'utf-8') as f: 49 | f.write(log_text) 50 | 51 | def argv(pos, default=None): 52 | try: 53 | arg = sys.argv[pos] 54 | except: 55 | return default 56 | return arg 57 | 58 | def exitWithFeedback(**kwargs): 59 | retcode = kwargs.pop('retcode', 0) 60 | fb = Feedback() 61 | fb.addItem(**kwargs) 62 | fb.output() 63 | sys.exit(retcode) 64 | 65 | def exit(msg='', retcode=0): 66 | if msg: 67 | print(msg) 68 | sys.exit(retcode) 69 | 70 | def query(word): 71 | scpt = 'tell application "Alfred 2" to search "{}"'.format(word) 72 | subprocess.call(['osascript', '-e', scpt]) 73 | 74 | def notify(title, subtitle, text='', sound=True): 75 | try: 76 | import objc, AppKit 77 | app = AppKit.NSApplication.sharedApplication() 78 | NSUserNotification = objc.lookUpClass("NSUserNotification") 79 | NSUserNotificationCenter = objc.lookUpClass("NSUserNotificationCenter") 80 | notification = NSUserNotification.alloc().init() 81 | notification.setTitle_(title) 82 | notification.setSubtitle_(subtitle) 83 | notification.setInformativeText_(text) 84 | if sound: 85 | notification.setSoundName_("NSUserNotificationDefaultSoundName") 86 | NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(notification) 87 | except Exception as e: 88 | log('Notification failed. {}'.format(e)) 89 | 90 | # ONLY used in 'try...except...' 91 | def raiseWithFeedback(feedback=None): 92 | exc = traceback.format_exc() 93 | if not exc or len(exc.split('\n')) < 4: 94 | return 95 | excs = [s.strip() for s in exc.split('\n')] 96 | item = Item(title=excs[3], subtitle=(': ').join(excs[1:3]), valid=False) 97 | if not isinstance(feedback, Feedback): 98 | exitWithFeedback(item=item) 99 | feedback.addItem(item=item) 100 | feedback.output() 101 | exit() -------------------------------------------------------------------------------- /src/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.kodango.lookupip 7 | category 8 | Internet 9 | connections 10 | 11 | 7367F5F3-AA5B-46B0-B220-5721AA879147 12 | 13 | 14 | destinationuid 15 | D96D2348-AF6D-492A-B0AF-B822FFFD63F9 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | 21 | 22 | 23 | createdby 24 | kodango 25 | description 26 | 根据淘宝地址库来查询 IP 地址信息 27 | disabled 28 | 29 | name 30 | Lookup IP 31 | objects 32 | 33 | 34 | config 35 | 36 | argumenttype 37 | 1 38 | escaping 39 | 6 40 | keyword 41 | lip 42 | runningsubtext 43 | 正在查询... 44 | script 45 | #!/usr/bin/env python 46 | # coding=utf-8 47 | 48 | import os 49 | import re 50 | import sys 51 | import json 52 | import urllib, urllib2 53 | 54 | from alfred.feedback import Feedback 55 | 56 | # Taobao restful query api 57 | API = 'http://ip.taobao.com/service' 58 | 59 | # Regular expression for a valid ip address 60 | REGEXP_IP = r'(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)' 61 | 62 | def query_ip(ip = None): 63 | '''Query the ip information with the taobao api.''' 64 | if ip is None: # Query the local ip instead 65 | url = '%s/getIpInfo2.php' % API 66 | data = urllib.urlencode({'ip': 'myip'}) # Post 67 | else: 68 | url = '%s/getIpInfo.php?ip=%s' % (API, ip) 69 | data = None # Get 70 | 71 | req = urllib2.Request(url, data) # exception? 72 | response = urllib2.urlopen(req) 73 | 74 | return json.loads(response.read()) 75 | 76 | def generate_feedback_results(ip_query): 77 | '''Generate the feedback results.''' 78 | fb = Feedback() 79 | 80 | if ip_query: 81 | ip_list = re.findall(REGEXP_IP, ip_query) 82 | else: 83 | ip_list = [None] 84 | 85 | for ip in ip_list: 86 | info = query_ip(ip) 87 | #print ip, info 88 | 89 | if info['code'] == 1: # query failed 90 | kwargs = { 91 | 'title': u'查询 IP 地址失败', 92 | 'subtitle': u'错误原因: %s' % info['data'], 93 | 'valid': False 94 | } 95 | else: # query success 96 | data = info['data'] 97 | 98 | title = u'%s 位于 %s %s %s' % (data['ip'], data['country'], 99 | data['region'], data['city']) 100 | 101 | if data['isp']: 102 | subtitle = u'运营商: %s %s' % (data['area'], data['isp']) 103 | else: 104 | subtitle = None 105 | 106 | kwargs = { 107 | 'title': title, 108 | 'subtitle': subtitle, 109 | 'arg': '%s\n%s' % (title, subtitle) 110 | } 111 | 112 | fb.addItem(**kwargs) 113 | 114 | fb.output() 115 | 116 | def main(): 117 | '''The main entry.''' 118 | # Note: do not use single quote here, because Alfred doesn't give choice to 119 | # escape a single quote. 120 | ip_query = "{query}" 121 | generate_feedback_results(ip_query) 122 | 123 | if __name__ == '__main__': 124 | main() 125 | subtext 126 | 查询 IP 地址信息, 来源: http://ip.taobao.com/index.php 127 | title 128 | Lookup IP 129 | type 130 | 3 131 | withspace 132 | 133 | 134 | type 135 | alfred.workflow.input.scriptfilter 136 | uid 137 | 7367F5F3-AA5B-46B0-B220-5721AA879147 138 | version 139 | 0 140 | 141 | 142 | config 143 | 144 | autopaste 145 | 146 | clipboardtext 147 | {query} 148 | 149 | type 150 | alfred.workflow.output.clipboard 151 | uid 152 | D96D2348-AF6D-492A-B0AF-B822FFFD63F9 153 | version 154 | 0 155 | 156 | 157 | readme 158 | Intro 159 | ===== 160 | 161 | Alfred 2 workflow: 根据淘宝地址库来查询 IP 地址信息 162 | 163 | Taobao IP 地址库: http://ip.taobao.com/index.php 164 | 165 | Usage 166 | ===== 167 | 168 | Alfred 框中输入 lip, 参数可以为: 169 | 170 | lip # 查询本机的 IP 地址 171 | lip 199.91.73.222 178.79.131.110 # 查询指定的多个 IP 地址 172 | lip 199.91.73.222,178.79.131.110 # 查询指定的多个 IP 地址 173 | lip 199.91.73.222178.79.131.110 # 查询指定的多个 IP 地址 174 | uidata 175 | 176 | 7367F5F3-AA5B-46B0-B220-5721AA879147 177 | 178 | ypos 179 | 10 180 | 181 | D96D2348-AF6D-492A-B0AF-B822FFFD63F9 182 | 183 | ypos 184 | 10 185 | 186 | 187 | webaddress 188 | http://kodango.com 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/alfred/request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import, division, unicode_literals 3 | import os 4 | import base64 5 | 6 | from . import core 7 | 8 | if core.PY2: 9 | from urllib import ContentTooShortError 10 | from urllib2 import HTTPError, URLError 11 | from urllib import urlencode 12 | from urllib2 import Request as urlRequest 13 | from urllib2 import build_opener 14 | from urllib2 import HTTPHandler, HTTPSHandler, HTTPCookieProcessor 15 | import Cookie 16 | from cookielib import CookieJar 17 | 18 | if core.PY3: 19 | from urllib.error import ContentTooShortError, HTTPError, URLError 20 | from urllib.parse import urlencode 21 | from urllib.request import Request as urlRequest 22 | from urllib.request import build_opener 23 | from urllib.request import HTTPHandler, HTTPSHandler, HTTPCookieProcessor 24 | from http import cookies as Cookie 25 | from http.cookiejar import CookieJar 26 | 27 | 28 | """ 29 | Simple module to request HTTP 30 | 31 | JinnLynn 32 | http://jeeker.net 33 | 34 | get(url, **kwargs) 35 | post(url, **kwargs) 36 | download(url, **kwargs) 37 | 38 | Request( 39 | 'http://jeeker.net', 40 | data = {}, 41 | type = 'GET', # GET POST default:GET 42 | referer = '', 43 | user_agent = '', 44 | cookie = None, # CookieJar, Cookie.S*Cookie, dict, string 45 | auth = {'usr':'', 'pwd':''}, # Only Basic Authorization 46 | debug = False 47 | ) 48 | """ 49 | 50 | _DEFAULT_TIMEOUT = 90 51 | 52 | def get(url, **kwargs): 53 | kwargs.update(type='GET') 54 | return Request(url, **kwargs) 55 | 56 | def post(url, **kwargs): 57 | kwargs.update(type='POST') 58 | return Request(url, **kwargs) 59 | 60 | def download(url, local, **kwargs): 61 | if not local: 62 | raise ValueError('local filepath is empty') 63 | try: 64 | if not os.path.exists(os.path.dirname(local)): 65 | os.makedirs(os.path.dirname(local)) 66 | res = Request(url, **kwargs) 67 | read_size = 0 68 | real_size = int(res.header['content-length']) 69 | with open(local, 'wb') as f: 70 | while True: 71 | block = res.response.read(1024*8) 72 | if not block: 73 | break 74 | f.write(block) 75 | read_size += len(block) 76 | if read_size < real_size: 77 | raise ContentTooShortError( 78 | 'retrieval incomplete: got only {} out of {} bytes'.formate(read_size, real_size), 79 | None 80 | ) 81 | except Exception as e: 82 | raise e 83 | 84 | class Request(object): 85 | def __init__(self, url, **kwargs): 86 | self.request = None 87 | self.response = None 88 | self.code = -1 89 | self.info = {} 90 | self.cookieJar = None 91 | self.reason = '' 92 | 93 | data = kwargs.get('data', None) 94 | if data: 95 | if isinstance(data, dict): 96 | data = urlencode(data) 97 | if not isinstance(data, basestring): 98 | raise ValueError('data must be string or dict') 99 | 100 | request_type = kwargs.get('type', 'POST') 101 | if data and isinstance(request_type, basestring) and request_type.upper()!='POST': 102 | url = '{}?{}'.format(url, data) 103 | data = None # GET data must be None 104 | 105 | self.request = urlRequest(url, data) 106 | 107 | # referer 108 | referer = kwargs.get('referer', None) 109 | if referer: 110 | self.request.add_header('referer', referer) 111 | 112 | # user-agent 113 | user_agent = kwargs.get('user_agent', None) 114 | if user_agent: 115 | self.request.add_header('User-Agent', user_agent) 116 | 117 | # auth 118 | auth = kwargs.get('auth', None) 119 | if auth and isinstance(auth, dict) and 'usr' in auth: 120 | auth_string = base64.b64encode('{}:{}'.format(auth.get('usr',''), auth.get('pwd',''))) 121 | self.request.add_header('Authorization', 'Basic {}'.format(auth_string)) 122 | 123 | # cookie 124 | cookie = kwargs.get('cookie', None) 125 | cj = None 126 | if cookie: 127 | if isinstance(cookie, CookieJar): 128 | cj = cookie 129 | elif isinstance(cookie, dict): 130 | result = [] 131 | for k, v in cookie.items(): 132 | result.append('{}={}'.format(k, v)) 133 | cookie = '; '.join(result) 134 | elif isinstance(cookie, Cookie.BaseCookie): 135 | cookie = cookie.output(header='') 136 | if isinstance(cookie, basestring): 137 | self.request.add_header('Cookie', cookie) 138 | if cj is None: 139 | cj = CookieJar() 140 | 141 | #! TODO: proxy 142 | 143 | 144 | # build opener 145 | debuglevel = 1 if kwargs.get('debug', False) else 0 146 | opener = build_opener( 147 | HTTPHandler(debuglevel=debuglevel), 148 | HTTPSHandler(debuglevel=debuglevel), 149 | HTTPCookieProcessor(cj) 150 | ) 151 | 152 | # timeout 153 | timeout = kwargs.get('timeout') 154 | if not isinstance(timeout, int): 155 | timeout = _DEFAULT_TIMEOUT 156 | 157 | try: 158 | self.response = opener.open(self.request, timeout=timeout) 159 | self.code = self.response.getcode() 160 | self.header = self.response.info().dict 161 | self.cookieJar = cj 162 | except HTTPError as e: 163 | self.code = e.code 164 | self.reason = '{}'.format(e) 165 | raise e 166 | except URLError as e: 167 | self.code = -1 168 | self.reason = e.reason 169 | raise e 170 | except Exception as e: 171 | self.code = -1 172 | self.reason = '{}'.format(e) 173 | raise e 174 | 175 | def isSuccess(self): 176 | return 200 <= self.code < 300 177 | 178 | def getContent(self): 179 | return self.response.read() --------------------------------------------------------------------------------