00. 前言
这个项目是我在半年前开始的,当时的计划是每周抽一点工作外的空余时间来写一个python程序(工作不用Python),结果持续不到一个月就弃坑了...于是把他们放上来,用作记录和学习交流吧。
01. 目录文件分类
前言
有时候,想要对一个目录里的文件进行搜索或者分类操作往往是一件痛苦的事情,下面这个程序的目的是将目录下的文件树以某种分类规则进行排列。
用法
usage: classify.py [-h] [-t {ext,mtime,back}] directory
对目录进行文件整理归类.
positional arguments:
directory 目标目录路径
optional arguments:
-h, --help show this help message and exit
-t {ext,mtime,back}, --type {ext,mtime,back}
分类方式
按扩展名分类
python classify.py -t ext 目录路径
效果:
├── DS_Store
├── bat
├── bin
├── css
├── db
├── default
├── gif
├── gitattributes
├── gitignore
├── htaccess
├── jar
├── js
├── json
├── lib
├── log
├── md
├── php
├── plex
├── png
├── sql
├── sublime-project
├── sublime-workspace
├── tpl
├── txt
├── xml
├── y
└── yml
按修改时间分类
python classify.py -t mtime 目录路径
效果:
├── 2014
│ └── 10
│ └── 24
├── 2015
│ └── 12
│ └── 21
└── 2016
├── 6
│ ├── 28
│ ├── 29
│ └── 30
├── 7
│ ├── 1
│ └── 26
└── 8
├── 6
└── 7
按首字母/数字分类
python classify.py -t word 目录路径
效果:
├── 3
├── 4
├── 5
├── N
├── R
├── W
├── a
├── b
├── c
├── d
├── e
├── f
├── h
├── i
├── j
├── l
├── m
├── o
├── p
├── s
├── t
├── u
├── v
└── y
还原目录
python classify.py -t back 目录路径
效果:
├── assets
│ ├── 2e015166
│ ├── 4893405d
│ │ ├── detailview
│ │ ├── gridview
│ │ └── listview
│ ├── 4a5213fe
│ └── a2744ecd
│ ├── autocomplete
│ ├── jui
│ │ ├── css
│ │ │ └── base
│ │ │ └── images
│ │ └── js
│ ├── rating
│ ├── treeview
│ │ └── images
│ └── yiitab
├── css
├── protected
│ ├── commands
│ ├── components
│ ├── config
│ ├── controllers
│ ├── data
│ ├── extensions
│ │ └── smarty
│ │ ├── demo
│ │ │ ├── plugins
│ │ │ └── templates
│ │ ├── lexer
│ │ └── libs
│ │ ├── plugins
│ │ └── sysplugins
│ ├── filters
│ ├── messages
│ │ └── zh_cn
│ ├── models
│ ├── runtime
│ ├── sql_source
│ ├── tests
│ │ ├── functional
│ │ └── unit
│ └── views
│ ├── blog
│ ├── layouts
│ ├── login
│ ├── postadmin
│ └── useradmin
└── themes
└── classic
└── views
源码
#!/usr/bin/python
#-*- coding:utf-8 -*-
import argparse
import os
import json
import shutil
import sys
import time
from uuid import uuid4
DEFAULT_KEY = 'default'
BACKUP_FILE = '.backup.json'
# 防止迁移的时候出现文件重名的情况
def unique_covert(file_path):
new_path = '|'.join(file_path.split(os.sep)[:-1])
if not new_path:
return file_path
return os.path.basename(file_path) + ' (' + new_path + ')'
def coroutine(gen):
def wrapper(*arg, **kws):
coroutine = gen(*arg, **kws)
next(coroutine)
return coroutine
return wrapper
@coroutine
def save_back_up(target_dir):
string_len = len(target_dir)
back_up_file = os.path.join(target_dir, BACKUP_FILE)
if os.path.exists(back_up_file):
go_back(target_dir)
back_up_tree = {}
while True:
tup = yield
if not tup:
break
(now, prev) = tup
back_up_tree[now] = prev
with open(back_up_file, 'w') as buf:
json.dump(back_up_tree, buf, indent=4)
# 按扩展名分类
def classify_by_ext(target_dir, tmp_dir):
from collections import defaultdict
ext_files = defaultdict(list)
for dir_ in os.walk(target_dir):
for f in dir_[2]:
exts = f.split('.')
key = exts[-1] if len(exts) != 1 else DEFAULT_KEY
ext_files[key].append(os.path.join(dir_[0], f))
for ext, file_list in ext_files.items():
dest_dir = os.path.join(tmp_dir, ext)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
for file_path in file_list:
filename = unique_covert(os.path.relpath(file_path, target_dir))
dest = os.path.join(dest_dir, filename)
rel_file_path = os.path.relpath(file_path, target_dir)
shutil.move(file_path, dest)
yield (os.path.join(ext, filename), rel_file_path)
yield None
# 按修改时间分类
def classify_by_mtime(target_dir, tmp_dir):
for dir_ in os.walk(target_dir):
base_dir = dir_[0]
for f in dir_[2]:
abs_file_path = os.path.join(base_dir, f)
rel_file_path = os.path.relpath(abs_file_path, target_dir)
if os.path.islink(abs_file_path):
rel_dest_dir = 'link'
dest_dir = os.path.join(tmp_dir, 'link')
else:
mtime = os.stat(abs_file_path)[8]
(y, m, d) = map(str, time.localtime(mtime)[:3])
rel_dest_dir = os.path.join(y, m, d)
dest_dir = os.path.join(tmp_dir, rel_dest_dir)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
filename = unique_covert(rel_file_path)
dest_file = os.path.join(dest_dir, filename)
shutil.move(abs_file_path, dest_file)
yield (os.path.join(rel_dest_dir, filename), rel_file_path)
yield None
# 按字母分类
def classify_by_first_letter(target_dir, tmp_dir):
for dir_ in os.walk(target_dir):
base_dir = dir_[0]
for f in dir_[2]:
abs_file_path = os.path.join(base_dir, f)
rel_file_path = os.path.relpath(abs_file_path, target_dir)
first_char = f[0]
if first_char.isalnum():
dest_dir = os.path.join(tmp_dir, first_char)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
filename = unique_covert(rel_file_path)
dest_file = os.path.join(dest_dir, filename)
shutil.move(abs_file_path, dest_file)
yield (os.path.join(first_char, filename), rel_file_path)
else:
shutil.move(abs_file_path, os.path.join(tmp_dir, f))
yield (f, rel_file_path)
yield None
def go_back(target_dir):
target_dir = target_dir.decode('utf-8')
tmp_dir = os.path.join(os.path.dirname(os.path.dirname(target_dir)), str(uuid4()))
os.mkdir(tmp_dir)
back_up_tree = {}
back_up_file = os.path.join(target_dir, BACKUP_FILE)
if not os.path.exists(back_up_file):
raise Exception('已经是初始状态')
with open(back_up_file, 'rb') as buf:
back_up_tree = json.load(buf)
if not back_up_tree:
raise Exception('备份文件已损坏或不存在')
for src, old in back_up_tree.items():
src_file = os.path.join(target_dir, src)
dest_file = os.path.join(tmp_dir, old)
dest_dir = os.path.dirname(dest_file)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
shutil.move(src_file, dest_file)
shutil.rmtree(target_dir, ignore_errors=False)
os.rename(tmp_dir, target_dir)
def run(target_dir, classify_func):
tmp_dir = os.path.join(os.path.dirname(os.path.dirname(target_dir)), str(uuid4()))
os.mkdir(tmp_dir)
save_backup_gen = save_back_up(target_dir)
classify_gen = classify_func(target_dir, tmp_dir)
finished = 0
begin = time.time()
while True:
tup = classify_gen.send(None)
finished += 1
sys.stdout.write(u'已完成%s个文件\r' % finished)
sys.stdout.flush()
if not tup:
break
save_backup_gen.send(tup)
print(u'已完成%s个文件,耗时%s秒' % (finished, time.time() - begin))
shutil.rmtree(target_dir, ignore_errors=False)
os.rename(tmp_dir, target_dir)
try:
save_backup_gen.send(None)
except StopIteration:
pass
def _main():
parser = argparse.ArgumentParser(description='对目录进行文件整理归类.')
parser.add_argument('directory', type=str, help='目标目录路径')
parser.add_argument('-t', '--type', type=str, default='ext', choices=['ext', 'mtime', 'word', 'back'], help='分类方式')
args = parser.parse_args()
target_dir = args.directory
op = args.type
if op == 'ext':
run(target_dir, classify_by_ext)
elif op == 'mtime':
run(target_dir, classify_by_mtime)
elif op == 'word':
run(target_dir, classify_by_first_letter)
elif op == 'back':
go_back(target_dir)
print ('恢复完成')
else:
raise Exception('参数错误')
if __name__ == '__main__':
_main()
02. 12306余票查询工具
前言
利用12306提供的相关接口对12306的余票信息进行查询。
用法
usage: left_ticket.py [-h] [-f FROM_CITY] [-t TO_CITY] [-d DATE] [-s] [-l]
查询12306车次余票.
optional arguments:
-h, --help show this help message and exit
-f FROM_CITY, --from_city FROM_CITY
起始城市
-t TO_CITY, --to_city TO_CITY
目标城市
-d DATE, --date DATE 日期,格式如:2016-08-14
-s, --student 学生票
-l, --list_city 查看支持城市列表
查询车票
python left_ticket.py -f 唐家湾 -t 广州南 -d 8-26 # 年份默认为今年
输出
车次序号 起始站 出发站 终点站 时间 一等座 二等座
6e000C761003 珠海->唐家湾->广州南 09:48 54 257
6e000C762202 珠海->唐家湾->广州南 11:43 39 235
6e000C763002 珠海->唐家湾->广州南 13:40 43 307
6e000C763802 珠海->唐家湾->广州南 14:45 58 329
6e000C764602 珠海->唐家湾->广州南 16:43 57 312
6e000C765802 珠海->唐家湾->广州南 18:38 62 356
6e000C766202 珠海->唐家湾->广州南 19:45 55 357
6e000C767602 珠海->唐家湾->广州南 22:10 64 389
6e000C767802 珠海->唐家湾->广州南 22:33 64 384
向导模式
python left_ticket.py
输出
请输入起始城市(输入回车为珠海):
请输入目的城市:广州南
请输入出发日期(输入回车为2016-08-14):
是否成人票,是请按回车,不是请输入n:
正在查询...
车次序号 起始站 出发站 终点站 时间 一等座 二等座
6e000C768602 珠海->珠海->广州南 23:28 62 无
6e000C768802 珠海->珠海->广州南 23:58 112 33
查看支持城市
python left_ticket.py -l
输出
阜南 祁县东 黑水 涪陵 哈密 大灰厂 新余北 平安 钦州东 安陆 黎塘 高各庄 谷城 彭水 沙县 海安县 枣庄西 昆山南 克东 图强 大苴 恩施 水富 沂南 姚千户屯 冷水江东 ...
源码
#!/usr/bin/python
#-*- coding:utf-8 -*-
import argparse
import os
import json
import urllib2
import ssl
import sys
import re
import socket
from datetime import datetime
PURPOSE_CODES = ['ADULT', '0X00'] # 成人票,学生票
CITY_CACHE = None
CITY_CACHE_FILE = '.cities'
ADDR_CACHE_FILE = '.addr'
CITY_LIST_URL = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js'
ACTION_URL = 'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes={ticket_type}&queryDate={train_time}&from_station={from_city}&to_station={to_city}'
SSL_CTX = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
# 对月份进行补零
def add_zero(month):
if int(month) < 10:
month = '0' + str(int(month))
return month
# 默认为今天
def default_date():
now = datetime.now()
return '-'.join([str(now.year), str(add_zero(now.month)), str(add_zero(now.day))])
# 格式化输入日期
# 如:
# 8-14 -> 2016-08-14
# 2016:8:14 -> 2016-08-14
# -> 2016-08-14
def date_format(input_date):
if not input_date:
return default_date()
res = re.match(r'(([0-9]{4})[-|\\|:])?([0-9]{1,2})[-|\\|:]([0-9]{2})', input_date)
if res:
year = res.group(2)
month = res.group(3)
day = res.group(4)
now = datetime.now()
if not year:
year = now.year
if not month:
month = now.month
if not day:
day = now.day
return '-'.join([str(year), add_zero(str(month)), str(day)])
else:
print ('输入日期格式错误')
sys.exit(-1)
# 加载城市信息
def load_cities():
global CITY_CACHE
if CITY_CACHE is not None:
return CITY_CACHE
cache_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), CITY_CACHE_FILE)
need_reload = True
cities = {}
if os.path.exists(cache_file):
with open(cache_file, 'rb') as fp:
cities = json.load(fp)
if cities:
need_reload = False
if need_reload is True:
city_info = urllib2.urlopen(CITY_LIST_URL, context=SSL_CTX).read()
for res in re.finditer(r'@[a-z]{3}\|(.+?)\|([A-Z]{3})\|[a-z]+?\|[a-z]+?\|', city_info):
city = res.group(1)
code = res.group(2)
cities[city] = code
with open(cache_file, 'w') as fp:
json.dump(cities, fp)
CITY_CACHE = cities
return cities
# 查询操作
def search(from_city, to_city, train_time, ticket_type='ADULT'):
cities = load_cities()
try:
from_code = cities[from_city.decode('utf-8')]
except KeyError:
print('指定起始站点%s不存在' % from_city)
sys.exit(-1)
try:
to_code = cities[to_city.decode('utf-8')]
except KeyError:
print('指定目标站点%s不存在' % to_city)
sys.exit(-1)
url = ACTION_URL.format(from_city=from_code, to_city=to_code, train_time=train_time, ticket_type=ticket_type)
ret = json.loads(urllib2.urlopen(url, context=SSL_CTX, timeout=10).read())
if not ret or ret == -1 or not ret['data'].has_key('datas') or len(ret['data']['datas']) == 0:
print('没查询到相关的车次信息')
sys.exit(-1)
print ('车次序号 起始站 出发站 终点站 时间 一等座 二等座')
for r in ret['data']['datas']:
if (not r['zy_num'].encode('utf-8').isdigit()
and not r['ze_num'].encode('utf-8').isdigit()
or r['from_station_name'].encode('utf-8') != from_city):
continue
print (u'%s %s->%s->%s %s %s %s' %(
r['train_no'],
r['start_station_name'],
r['from_station_name'],
r['to_station_name'],
r['arrive_time'],
r['zy_num'],
r['ze_num']
))
# 获取ip
def getip():
url = 'http://jsonip.com'
if url == opener.geturl():
res = re.search('\d+\.\d+\.\d+\.\d+', urllib2.urlopen(url, timeout=5).read())
if res:
return res.group(0)
return None
# 根据ip获取地址
def getaddr(fresh=False):
addr_cache_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), ADDR_CACHE_FILE)
if not fresh and os.path.exists(addr_cache_file):
addr = None
with open(addr_cache_file, 'rb') as fp:
addr = fp.read()
if addr:
return addr
ip = getip()
if not ip:
return None
addr_info = urllib2.urlopen('http://ip.taobao.com/service/getIpInfo.php?ip=%s' % ip, timeout=5).read()
city = None
if addr_info:
addr_info = json.loads(addr_info)
city = addr_info['data']['city']
city = city.encode('utf-8').replace('市', '')
with open(addr_cache_file, 'w') as fp:
fp.write(city)
return city
def get_yn_input(msg):
while True:
res = raw_input('%s,是请按回车,不是请输入n:' % msg)
if res in ('', 'n'):
break
return True if res == None else False
# 默认模式
def guide():
try:
cities = load_cities()
city = getaddr()
except socket.timeout:
print ('请求超时')
sys.exit(-1)
if city and cities.has_key(city.decode('utf-8')):
from_city = raw_input('请输入起始站点(输入回车为%s):' % city)
if not from_city:
from_city = city
else:
from_city = raw_input('请输入起始站点:')
while True:
to_city = raw_input('请输入目的站点:')
if to_city:
break
dd = default_date()
train_time = raw_input('请输入出发日期(输入回车为%s):' % dd)
train_time = date_format(train_time) if train_time else dd
ticket_type = 'ADULT' if get_yn_input('是否成人票') else '0X00'
print('正在查询...\n')
search(from_city, to_city, train_time, ticket_type)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='查询12306车次余票.')
parser.add_argument('-f', '--from_city', type=str, help='起始城市')
parser.add_argument('-t', '--to_city', type=str, help='目标城市')
parser.add_argument('-d', '--date', type=str, help='日期,格式如:2016-08-14')
parser.add_argument('-s', '--student', action='store_true', help='学生票')
parser.add_argument('-l', '--list_city', action='store_true', help='查看支持城市列表')
args = parser.parse_args()
from_city = args.from_city
to_city = args.to_city
train_time = date_format(args.date)
ticket_type = PURPOSE_CODES[1] if args.student is True else PURPOSE_CODES[0]
list_city = args.list_city
if from_city is None and to_city is None and list_city is False:
guide()
else:
if list_city:
for city, code in load_cities().items():
print city,
elif from_city and to_city and ticket_type:
search(from_city, to_city, train_time, ticket_type)
else:
print ('参数错误')
03. 文本备份云仓库
前言
everbox是一个将evernote作为文件沙盒的接口集合,利用evernote作为文本的存储仓库,方便地对文本文件进行管理。
用法
usage: everbox.py [-h] {init,push,pushall,list,drop,drag,remove,pull,log} ...
文本备份助手.
optional arguments:
-h, --help show this help message and exit
操作命令:
{init,push,pushall,list,drop,drag,remove,pull,log}
init 新建一个仓库
push 添加文本到仓库
pushall 添加批量文本到仓库
list 列出仓库或文本
drop 删除一个仓库
drag 从远程拉取一个文件同时删除记录
remove 从仓库删除指定id的文本
pull 从仓库拉取文本
log 查看文本记录信息
准备工作
安装evernote sdk for python
pip install evernote
登录Evernote开发者,获取开发Token,把获取到的token替换掉代码中的dev_token。
基本操作
init 新建一个仓库
usage: everbox.py init [-h] box
新建一个仓库
positional arguments:
box 仓库名字 python everbox.py init test 创建成功,id为:0c6e25c4-538c-4008-87e2-7efe32e18280
list 列出仓库或文本
usage: everbox.py list [-h] [box]
列出仓库文本
positional arguments:
box 仓库id或仓库名字
获取所有仓库
python everbox.py list | 文本id | 仓库名称 |
6da27e72-ad2d-4cd0-a05a-f1fc12d9e44c 我的第一个笔记本
1902a691-62f3-4edc-a8bb-4db6d949da50 示例笔记本
获取仓库文本
python everbox.py list 6da2 | 文本id | 文本名称 |
b00204f8-41d0-43bb-8fc3-17b3a654360f README.md
f7c7b2be-c247-4c2a-8001-186d27942cce README.md
pushall 推送所有文本
usage: everbox.py pushall [-h] [-b BOX] [files [files ...]]
添加批量文本到仓库
positional arguments:
files 文本路径,多个以空格间隔
optional arguments:
-h, --help show this help message and exit
-b BOX, --box BOX 仓库id或仓库名字 python everbox.py pushall -b 6da2 README.md 已上传(1/1)个文本 python everbox.py pushall README.md 无指定仓库,将使用默认仓库
已上传(1/1)个文本
log 查看文件在仓库中的记录
usage: everbox.py log [-h] file
查看文本记录信息
positional arguments:
file 文本名称 python everbox.py log README.md
输出
| 文本id | 文本名称 | 仓库 | 创建时间
b00204f8-41d0-43bb-8fc3-17b3a654360f README.md 我的第一个笔记本 2016-08-16 17:14:07
f7c7b2be-c247-4c2a-8001-186d27942cce README.md 我的第一个笔记本 2016-08-16 17:15:02
pull 从仓库中拉取文件
usage: everbox.py pull [-h] [-b BOX] [-y] [files [files ...]] directory
从仓库拉取文本
positional arguments:
files 文本guid或名称(若用名称则取最新的同名
),多个以空格间隔
directory 拉取目录
optional arguments:
-h, --help show this help message and exit
-b BOX, --box BOX 仓库id或仓库名字
-y, --yes 忽略覆盖提示 python everbox.py pull b00204f8-41d0-43bb-8fc3-17b3a654360f .
输出
文件 /Users/tonnie/github/one-week/03-everbox/README.md 已存在,是否覆盖,是请按y,不是请输入n:y
成功拉取:1个文件
remove 从仓库删除指定的文本
usage: everbox.py remove [-h] guid
从仓库删除指定id的文本
positional arguments:
guid 文本guid python everbox.py remove d8bc4812-bfc2-44cd-9aee-bc7a92887e70
输出
删除成功
drag 从远程拉取一个文件同时删除记录
usage: everbox.py drag [-h] guid directory
从远程拉取一个文本同时删除记录
positional arguments:
guid 文本guid
directory 拉取目录 python everbox.py drag f7c7b2be-c247-4c2a-8001-186d27942cce ~
输出
拉取完成
删除成功
drop 删除一个仓库
usage: everbox.py drop [-h] box
删除一个仓库
positional arguments:
box 仓库id或仓库名字 python everbox.py drop 我的第一个笔记本
输出
删除成功
源码
#!usr/bin/python
#-*- coding:utf-8 -*-
import argparse
import binascii
import hashlib
import os
import sys
import base64
from datetime import datetime, date
try:
import evernote
from evernote.api.client import EvernoteClient
import evernote.edam.type.ttypes as Types
import evernote.edam.notestore.ttypes as NoteTypes
except:
print ('未安装evernote的扩展,请安装后重试')
sys.exit(-1)
try:
import socket
s = socket.create_connection(('sandbox.evernote.com', 80), 5)
except Exception,e:
print('无法连接到evernote,请检查网络连接')
sys.exit(-1)
dev_token = "S=s1:U=92d14:E=15de8ebccac:C=156913a9da8:P=1cd:A=en-devtoken:V=2:H=ca0bbceb23208c3cde8227aa5912761a"
from contextlib import contextmanager
_loading = True
def loading(hint):
while _loading:
sys.stdout.write(hint + '\r')
sys.stdout.flush()
# 耗时操作的提示
@contextmanager
def open_loading(hint):
from threading import Thread
global _loading
_loading = True
t = Thread(target=loading, args=[hint])
t.start()
yield
_loading = False
class LazyGet:
def __init__(self, construct_func, hint=None, end_hint=None, excp_hint=None):
self._func = construct_func
self._ins = None
self._hint = hint
self._end_hint = end_hint
self._excp_hint = excp_hint
def __call__(self):
if self._ins is not None:
return self._ins
print (self._hint)
try:
self._ins = self._func()
except Exception, e:
print (self._excp_hint)
sys.exit(-1)
print (self._end_hint)
return self._ins
get_client = LazyGet(lambda : EvernoteClient(token=dev_token), '初始化组件...', '初始化成功', '初始化失败')
get_box_store = LazyGet(lambda : get_client().get_note_store(), '连接evernote...', '连接成功', '连接失败')
get_boxes = LazyGet(lambda : get_box_store().listNotebooks(), '获取仓库信息...', '获取仓库成功', '获取失败')
def create_box(name):
box = Types.Notebook()
box.name = name
return box
def create_file(title, content, box_id='', image_resource=None):
hash_hex = ''
file = Types.Note()
if box_id:
file.notebookGuid = box_id
if image_resource:
(resource, image_hash) = image_resource
file.resources = [resource]
hash_hex = binascii.hexlify(image_hash)
file.title = title
file.content = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note><caption>%s</caption>''' % content
if hash_hex:
file.content += '<en-media type="image/png" hash="%s"/>' % hash_hex
file.content += '</en-note>'
return file
def parse_file(content):
file_content = ''
try:
content = content.split('<caption>')[1].split('</caption>')[0]
except:
pass
media_index = content.find('<en-media')
content = content[:media_index]
file_lst = content.split('<br/>')
for content in file_lst:
file_content += content
return file_content
def load_file(guid):
file = None
try:
file = get_box_store().getNote(dev_token, guid, True, True, False, False)
except Exception, e:
return None
file_content = parse_file(file.content)
return {
'title': file.title,
'content': base64.b64decode(file_content + '=' * (-len(file_content) % 4)),
'created': date.fromtimestamp(file.created / 100).strftime('%d/%m/%Y')
}
def get_box_by_id(box_id):
if len(box_id) < 4:
print ('id匹配长度至少为4')
sys.exit(-1)
for box in get_boxes():
if box.guid.startswith(box_id):
return box
return ''
def get_box_by_name(name):
for box in get_boxes():
if box.name == name:
return box
return ''
def get_box(id_or_name):
if not id_or_name:
return ''
box = get_box_by_id(args.box)
if not box:
box = get_box_by_name(args.box)
if not box:
print ('指定仓库不存在')
sys.exit(-1)
return box
def push_to_box(note):
note = get_box_store().createNote(dev_token, note)
return note
def get_yn_input(msg):
while True:
res = raw_input('%s,是请按y,不是请输入n:' % msg)
if res in ('y', 'n'):
break
return True if res == 'y' else False
def get_abs_dir(directory):
dest_dir = directory or os.path.abspath('.')
if not os.path.exists(dest_dir) or not os.path.isdir(dest_dir):
print ('指定目录不存在')
sys.exit(-1)
dest_dir = os.path.abspath(os.path.normpath(os.path.expanduser(dest_dir)))
return dest_dir
def datetime_format(time):
return datetime.fromtimestamp(int(str(time)[:-3])).strftime('%Y-%m-%d %H:%M:%S')
def init(args):
boxname = args.box
box = create_box(boxname)
try:
box = get_box_store().createNotebook(dev_token, box)
if not box:
raise
print ('创建成功,id为:%s' % box.guid)
except Exception,e:
print ('创建失败')
def pull(args):
dest_dir = get_abs_dir(args.directory)
skip_cover = args.yes
finished = 0
for f in args.files:
try:
flag = os.path.basename(f)
file = load_file(flag)
if not file:
box = get_box(args.box)
file_filter = NoteTypes.NoteFilter()
if box:
file_filter.notebookGuid = box.guid
file_filter.title = flag
files = get_box_store().findNotes(dev_token, file_filter, 0, 100).notes
file = load_file(files[-1].guid)
if not file:
raise
ouput_file = os.path.join(dest_dir, file['title'])
if os.path.exists(ouput_file):
if not skip_cover:
if not get_yn_input('文件 %s 已存在,是否覆盖' % ouput_file):
continue
with open(ouput_file, 'w') as fp:
fp.write(file['content'])
finished += 1
sys.stdout.write('已拉取:%s个文件\r' % finished)
sys.stdout.flush()
except Exception, e:
print ('文件:%s拉取失败,跳过..' % f)
print('成功拉取:%s个文件' % finished)
def list(args):
def list_box(box):
file_filter = NoteTypes.NoteFilter()
file_filter.notebookGuid = box.guid
files = get_box_store().findNotes(dev_token, file_filter, 0, 100).notes
if not files:
print '仓库没有任何文本'
else:
print ('| 文本id | 文本名称 | 创建时间')
for f in reversed(files):
print "%s %s %s" %(f.guid, f.title, datetime_format(f.created))
# 没有指定仓库,则列出所有仓库
if args.box is None:
boxes = get_boxes()
print ('| 仓库id | 仓库名称 | 创建时间')
for box in boxes:
print box.guid, box.name, datetime_format(box.serviceCreated)
else:
box = get_box(args.box)
list_box(box)
def push(args):
box = get_box(args.box)
abs_path = os.path.abspath(os.path.normpath(args.file))
if not os.path.exists(abs_path):
print ('文本路径不存在')
return
try:
with open(abs_path, 'rb') as fp:
title = os.path.basename(abs_path)
if args.box:
box = get_box(args.box)
file = create_file(title, fp.read(), box.guid, '')
else:
print ('无指定仓库,将使用默认仓库')
file = create_file(title, base64.b64encode(fp.read()), None, '')
push_to_box(file)
print ('%s 上传成功' % abs_path)
except Exception, e:
print e
print ('上传%s时,发生异常' % abs_path)
def pushall(args):
files = args.files
to_add = []
for f in files:
abs_path = os.path.abspath(os.path.normpath(f))
if not os.path.exists(abs_path):
print ('%s:文本路径不存在,跳过' % f)
continue
if os.path.getsize(abs_path) > 1000 * 1000 * 10: # 10M
print ('%s: 文本体积大于10M,跳过' % f)
continue
to_add.append(os.path.abspath(os.path.normpath(f)))
else:
total = len(to_add)
finished = 0
for f in to_add:
try:
with open(f, 'rb') as fp:
title = os.path.basename(f)
if args.box:
box = get_box(args.box)
file = create_file(title, fp.read(), box.guid, '')
else:
print ('无指定仓库,将使用默认仓库')
file = create_file(title, fp.read(), None, '')
push_to_box(file)
finished += 1
sys.stdout.write('已上传(%s/%s)个文本\r' % (finished, total))
sys.stdout.flush()
except Exception, e:
print ('上传%s时,发生异常' % f)
print('已上传(%s/%s)个文本' % (finished, total))
def remove(args):
guid = args.guid
try:
get_box_store().deleteNote(dev_token, guid)
print ('删除成功')
except:
print ('删除失败')
def drop(args):
box = get_box(args.box)
try:
get_box_store().expungeNotebook(dev_token, box.guid)
print ('删除成功')
except:
print ('删除失败')
def drag(args):
dest_dir = get_abs_dir(args.directory)
file = load_file(args.guid)
if not file:
print ('文件拉取失败')
output_file = os.path.join(os.path.join(dest_dir, file['title']));
if os.path.exists(output_file):
if not get_yn_input('文件 %s 已存在,是否覆盖' % output_file):
return
with open(output_file, 'w') as fp:
fp.write(file['content'])
print ('拉取完成')
remove(args)
def log(args):
file = args.file
abs_path = os.path.abspath(os.path.normpath(file))
if not os.path.exists(abs_path):
print ('文件不存在!')
return
file_name = os.path.basename(abs_path)
file_filter = NoteTypes.NoteFilter()
file_filter.title = file_name
files = get_box_store().findNotes(dev_token, file_filter, 0, 100).notes
if not files:
print ('仓库中不存在该文件的记录')
return
print ('| 文本id | 文本名称 | 仓库\t | 创建时间')
for f in files:
print '%s %s %s %s' %(f.guid, f.title, get_box_by_id(f.notebookGuid).name, datetime_format(f.created))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='文本备份助手.')
subparsers = parser.add_subparsers(title='操作命令')
init_cmd = subparsers.add_parser('init', help='新建一个仓库', description='新建一个仓库')
init_cmd.add_argument('box', help='仓库名字')
init_cmd.set_defaults(func=init)
push_cmd = subparsers.add_parser('push', help='添加文本到仓库', description='添加文本到仓库')
push_cmd.add_argument('-b', '--box', help='仓库id或仓库名字')
push_cmd.add_argument('file')
push_cmd.set_defaults(func=push)
push_all_cmd = subparsers.add_parser('pushall', help='添加批量文本到仓库', description='添加批量文本到仓库')
push_all_cmd.add_argument('-b', '--box', help='仓库id或仓库名字')
push_all_cmd.add_argument('files', nargs='*', help='文本路径,多个以空格间隔')
push_all_cmd.set_defaults(func=pushall)
list_cmd = subparsers.add_parser('list', help='列出仓库或文本', description='列出仓库或文本')
list_cmd.add_argument('box', nargs='?', help='仓库id或仓库名字')
list_cmd.set_defaults(func=list)
drop_cmd = subparsers.add_parser('drop', help='删除一个仓库', description='删除一个仓库')
drop_cmd.add_argument('box', help='仓库id或仓库名字')
drop_cmd.set_defaults(func=drop)
drag_cmd = subparsers.add_parser('drag', help='从远程拉取一个文件同时删除记录', description='从远程拉取一个文件同时删除记录')
drag_cmd.add_argument('guid', help='文本guid')
drag_cmd.add_argument('directory', type=str, help='拉取目录')
drag_cmd.set_defaults(func=drag)
remove_cmd = subparsers.add_parser('remove', help='从仓库删除指定id的文本', description='从仓库删除指定id的文本')
remove_cmd.add_argument('guid', help='文本guid')
remove_cmd.set_defaults(func=remove)
pull_cmd = subparsers.add_parser('pull', help='从仓库拉取文本', description='从仓库拉取文本')
pull_cmd.add_argument('-b', '--box', help='仓库id或仓库名字')
pull_cmd.add_argument('-y', '--yes', action='store_true', help='忽略覆盖提示')
pull_cmd.add_argument('files', nargs='*', help='文本guid或名称(若用名称则取最新的同名),多个以空格间隔')
pull_cmd.add_argument('directory', type=str, help='拉取目录')
pull_cmd.set_defaults(func=pull)
log_cmd = subparsers.add_parser('log', help='查看文本记录信息', description='查看文本记录信息')
log_cmd.add_argument('file', help='文本名称')
log_cmd.set_defaults(func=log)
args = parser.parse_args()
args.func(args)
04. 12306余票查询工具
前言
把图像转为ascii字符。
用法
python ascii-image.py [图像路径]
输出

Paste_Image.png
源码
#-*- coding:utf-8 -*-
from PIL import Image
import sys
import os
# 加上颜色
class BColor:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def _main():
try:
pic = os.path.abspath(sys.argv[1])
except:
print('指定图片路径')
img = Image.open(pic)
width = int(img.size[0])
height = int(img.size[1])
gray_img = img.convert('L')
scale = width // 100
char_lst = ' .:-=+*#%@'
char_len = len(char_lst)
arr = []
for y in range(0, height, scale):
for x in range(0, width, scale):
brightness = 0
r = g = b = 0
count = 0
for ix in range(scale):
for iy in range(scale):
if (x + ix) == width or (y + iy) == height:
break
count += 1
b = 255 - gray_img.getpixel((x+ix, y+iy))
brightness += b
choice = int(char_len * (brightness // count / 255) ** 1.2)
if choice >= char_len:
choice = char_len
sys.stdout.write(char_lst[choice])
sys.stdout.write('\n')
sys.stdout.flush()
if __name__ == '__main__':
_main()
05. html生成器
前言
用python生成html
用法
from htmlgen import h
h('html') # <html></html>
更深入的例子
datas = [
['1 + 1', 2],
['1 + 2', 3],
['2 + 2', 4]
]
参数语法
build(h('html', c=[
h('head', c=[
h('title', 'My Title'),
h('meta', charset='utf-8'),
hc('This is comment'),
hcss('test.css')
]),
h('body#main.class1 class2', c=[
h('h1', 'HtmlGen') * 2,
h('ul', extra=1, c=[
hmap('li', 'I am {?}!', [1,2,3,4,5])
]),
h('ul', c=[
hfor(5, lambda i: h('li', '{}').format(chr(ord(str(i)) + 17)))
]),
h('table', c=[
h('tr', c=[
h('td'),
heach(dict(name='result'), lambda k, v: h('td', v))
]),
heach(datas, lambda v:
h('tr', c=[
heach(v, lambda v: h('td', v, style='border:1px solid black;')),
])
)
]),
~ hjs('test.js')
]),
]), 'test.html')
注入语法
build(h('html') <= [
h('head') <= [
h('title', 'My Title'),
h('meta', charset='utf-8'),
hc('This is comment'),
hcss('test.css')
],
h('body#main.class1 class2') <= [
h('h1', 'HtmlGen') * 2,
h('ul', extra=1) <= [
hmap('li', 'I am {?}!', [1,2,3,4,5])
],
h('ul') <= [
hfor(5, lambda i: h('li', '{}').format(chr(ord(str(i)) + 17)))
],
h('table') <= [
h('tr') <= [
h('td'),
heach({'name': 'result'}, lambda k, v: h('td', v))
],
heach(datas, lambda v:
h('tr') <= [
heach(v, lambda v: h('td', v, style='border:1px solid black;')),
]
)
],
~ hjs('test.js') # comment,too
],
], 'test.html')
以上代码会生成如下html:
<html>
<head>
<title>My Title</title>
<meta charset="utf-8"></meta>
<!-- This is comment -->
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body id="main" class="class1 class2">
<h1>HtmlGen</h1>
<h1>HtmlGen</h1>
<ul extra="1">
<li>I am 1!</li>
<li>I am 2!</li>
<li>I am 3!</li>
<li>I am 4!</li>
<li>I am 5!</li>
</ul>
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
</ul>
<table>
<tr>
<td></td>
<td>result</td>
</tr>
<tr>
<td style="border:1px solid black;">1 + 1</td>
<td style="border:1px solid black;">2</td>
</tr>
<tr>
<td style="border:1px solid black;">1 + 2</td>
<td style="border:1px solid black;">3</td>
</tr>
<tr>
<td style="border:1px solid black;">2 + 2</td>
<td style="border:1px solid black;">4</td>
</tr>
</table>
<!-- <script type="text/javascript" src="test.js"></script> -->
</body>
</html>
源码
#-*- coding:utf-8 -*-
import re
class Piece(object):
_html = '<{tag}{attrs}>{content}{childs}</{tag}>'
def __init__(self, tag='', attrs={}, content='', childs='', raw=None):
self.raw = raw
self.tag = tag
self.attrs = attrs
self.content = content
self.childs = childs
def resolve(self):
if self.raw:
return self.raw
attrs = make_attrs(self.attrs)
self.html = self._html.format(tag=self.tag, attrs=attrs, content=self.content, childs=self.childs)
return self.html
def format(self, *args, **kws):
return self.resolve().format(*args, **kws)
def __str__(self):
return self.resolve()
def __invert__(self):
return '<!-- {} -->'.format(self.resolve())
# child's inject
def __le__(self, childs):
self.childs = ''.join([str(o) for o in childs])
return self
def __mul__(self, num):
return str(self) * 2
def __getattr__(self, attr):
return self.attrs.get(attr, None)
__repr__ = __str__
def make_attrs(attrs):
attrs = '' + ' '.join(['{}="{}"'.format(k, v) for k, v in attrs.items()])
attrs = ' {}'.format(attrs) if attrs else ''
return attrs
tag_flg = re.compile(r'(?P<tag>[\w]+)(\#(?P<id>[\w]+))?(\.(?P<class>[\w\s]+))?')
def h(tag, content='', **attrs):
if tag:
m = re.match(tag_flg, tag)
if m:
groups = m.groupdict()
if groups['id'] is not None:
attrs['id'] = groups['id']
if groups['class'] is not None:
attrs['class'] = groups['class']
if groups['tag'] is not None:
tag = groups['tag']
childs = attrs.pop('c', [])
child_html = ''.join([str(c) for c in childs])
return Piece(tag, attrs, content, child_html)
def hmap(tag, content='', datas=None, **attrs):
if datas is None:
datas = []
htmls = []
for data in datas:
html = Piece._html.format(
tag=tag,
attrs=make_attrs(attrs),
content=content.replace('{?}', str(data)),
childs=''
)
htmls.append(html)
return ''.join(htmls)
def hcss(path):
return Piece(raw='<link rel="stylesheet" type="text/css" href="{}">'.format(path))
def hjs(path):
return Piece(raw='<script type="text/javascript" src="{}"></script>'.format(path))
def hc(comment):
return '<!-- {} -->'.format(comment)
def heach(iterable, func):
res = []
if isinstance(iterable, list):
for value in iterable:
res.append(str(func(value)))
elif isinstance(iterable, dict):
for key, value in iterable.items():
res.append(str(func(key, value)))
return ''.join(res)
def hfor(times, func, **injects):
res = []
for i in range(times):
res.append(func(i))
return ''.join(res)
def _main():
def build(piece, path):
buf = piece.resolve()
with open(path, 'w') as fp:
fp.write(buf)
datas = [
['1 + 1', 2],
['1 + 2', 3],
['2 + 2', 4]
]
build(h('html') <= [
h('head') <= [
h('title', 'My Title'),
h('meta', charset='utf-8'),
hc('This is comment'),
hcss('test.css')
],
h('body#main.class1 class2') <= [
h('h1', 'HtmlGen') * 2,
h('ul', extra=1) <= [
hmap('li', 'I am {?}!', [1,2,3,4,5])
],
h('ul') <= [
hfor(5, lambda i: h('li', '{}').format(chr(ord(str(i)) + 17)))
],
h('table') <= [
h('tr') <= [
h('td'),
heach({'name': 'result'}, lambda k, v: h('td', v))
],
heach(datas, lambda v:
h('tr') <= [
heach(v, lambda v: h('td', v, style='border:1px solid black;')),
]
)
],
~ hjs('test.js') # comment,too
],
], 'test.html')
if __name__ == '__main__':
_main()