kindle manager

This commit is contained in:
gavin
2020-06-26 11:26:19 +08:00
parent d7672d9380
commit 6eac75b852
28 changed files with 4820 additions and 26731 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,80 @@
# Change Log
## 1.0.0 (20200526)
### create
- unittest
- abstract note/bookmark/highlight from kindle clipping
- formatter output to special file
## 1.1.1 (20200626)
### feature
- remove pandas because it will make the package too large,
I write mTable calss to operate table data structure
- add sort function in nTableModel class which
make table can be sort by clicking table header!! cheer!!
- make installer package by pyistaller, but run "open kmanapp.app",<br>
always show:<br>
"LSOpenURLsWithRole() failed with error -10810 for the file /Users/mark/penv/kman/Release/kmanapp.app."<br>
which spend me a great mount of time,<br>
and I baidu/bing/google all about discuss try to solve it,<br>
but problem still here! :cry: :cry: :cry:<br>
- modify some bugs let application more stabler
### learn lesson
- how to write a iterable object
## 1.1.0 (20200617)
### feature
- book information grabbed from douban / amazon
## 1.0.5 (20200613)
### feature
- backup clips after kman closed, check and read backup file when kman started
- import words from kindle, export filtered words to MD/CSV
### learn lesson
- create virtual envirement
```
> mkvirtualenv kmanenv
> workon kmanenv
virtual env lib in ~/.virtualenvs/kmanenv/
```
## 1.0.4 (20200604)
### feature
- refactoring kman.py
- creat constant class for all constant
- creat config dialog to do some constant configuration
- creat xman class for all clipping manipulation
- design config dialog & statusbar information
### learn lesson
- generate ui source(.py) use pyqtuic will 'import kmanapp\_rc',
so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o kmanapp\_rc.py
- use QTime to check kindle kindle is connected or not
- implement table model which inherited from QAbstractTableModel, so
we can use **panda data structure**
## 1.0.3 (20200603)
### feature
- design GUI with qdesigner
- define toolbar action's slots
## 1.0.2 (20200530)
### feature
- remove duplication
- refactor some code
- optimize search feature
## learn lesson
- change dict keys between iteration, will throw RuntimeError:
dictionary changed size during iteration
- reference - http://www.cocoachina.com/articles/89748
- pyQt5 reference - https://www.learnpyqt.com/
## 1.0.1 (20200528)
### feature
- add search clip and some functions
## learn lesson
### learn lesson
- assign value to a not exist key, will throw KeyError, too inflexible!!! perl much better
- use defaultdict to solve obove problem, note, defaultdict only create two layer key auto
```
@@ -40,52 +103,12 @@ b['1']['2'] = {'3':1} # OK
- unittest error line number is not collect, **unitest bug**?
- **unitest testcase execution sequence is disorder, so must create test data for each case**
## 1.0.2 (20200530)
### feature
- remove duplication
- refactor some code
- optimize search feature
## learn lesson
- change dict keys between iteration, will throw RuntimeError:
dictionary changed size during iteration
- reference - http://www.cocoachina.com/articles/89748
- pyQt5 reference - https://www.learnpyqt.com/
## 1.0.3 (20200603)
### feature
- design GUI with qdesigner
- define toolbar action's slots
## 1.0.4 (20200604)
### feature
- refactoring kman.py
- creat constant class for all constant
- creat config dialog to do some constant configuration
- creat xman class for all clipping manipulation
- design config dialog & statusbar information
## learn lesson
- generate ui source(.py) use pyqtuic will 'import kmanapp\_rc',
so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o kmanapp\_rc.py
- use QTime to check kindle kindle is connected or not
- implement table model which inherited from QAbstractTableModel, so
we can use **panda data structure**
## 1.0.5 (20200613)
### feature
- backup clips after kman closed, check and read backup file when kman started
- import words from kindle, export filtered words to MD/CSV
## learn lesson
## 1.1.0 (20200617)
### feature
- book information grabbed from douban / amazon
## 1.0.0 (20200526)
### create
- unittest
- abstract note/bookmark/highlight from kindle clipping
- formatter output to special file
---

2
cui
View File

@@ -1,2 +1,4 @@
pyuic mainwindow.ui -o mainwindow.py --no-protection
pyside2-rcc -binary kmanapp.qrc -o kmanapp_rc.py
cp -fr *py ~/penv/kman/

3171
debug Normal file

File diff suppressed because it is too large Load Diff

35
kman.py
View File

@@ -8,6 +8,7 @@
#########################################################
import re
import sys
import os
import io
import json
@@ -65,15 +66,16 @@ LASTLINE = '=========='
NTPREF = '--CG注:'
CLIPFN = 'My Clippings.txt'
WORDFN = 'vocab.db'
CLIPPATH = './'
OUTPREF = './clip'
CURRPATH = os.path.dirname(os.path.realpath(sys.argv[0]))
OUTPREF = os.path.join(CURRPATH,'CLIP')
DEBUG = 1 # 0 - INFO; 1 - DEBUG
LOG2FILE = 1 # 0 - to stdio; 1 - to file
LOGFILE = 'log'
DELIMITER= '|'
BACKUPNOTEFN = './backup/bk.note.data' # kindle notes
BACKUPWORDFN = './backup/bk.word.data' # kindle words
BACKUPINFOFN = './backup/bk.info.data' # book information from douban/amazon
BACKUPFOLDER = os.path.join(CURRPATH,'backup')
BACKUPNOTEFN = os.path.join(BACKUPFOLDER,'bk.note.data') # kindle notes
BACKUPWORDFN = os.path.join(BACKUPFOLDER,'bk.word.data') # kindle words
BACKUPINFOFN = os.path.join(BACKUPFOLDER,'bk.info.data') # book information from douban/amazon
#HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'}
# log info
@@ -119,6 +121,7 @@ r'''
class kMan:
def __init__(self, parent=None):
self.hlnum = 0
self.ntnum = 0
self.refleshtime = '2020/10/10 10:00:00'
@@ -130,7 +133,7 @@ class kMan:
kp = self.get_kindle_path()
if not kp:
s2 = u'Disconnected ({})'.format(CLIPPATH+CLIPFN)
s2 = u'Disconnected ({})'.format(os.path.join(CURRPATH,CLIPFN))
else:
with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f:
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
@@ -597,7 +600,7 @@ class kMan:
return False
def import_clips(self, fp=(CLIPPATH+CLIPFN)):
def import_clips(self, fp=os.path.join(CURRPATH, CLIPFN)):
"""import clips from local file or kindle
4 lines for each section seperated with '======='
so read 4 lines before '======='
@@ -616,6 +619,8 @@ class kMan:
path = fn
"""
if not os.path.exists(fp): return {}
# loop to fill books dict
with open(fp, 'r', encoding='utf8', errors='ignore') as f:
bks = defaultdict(dict)
@@ -670,7 +675,7 @@ class kMan:
return self.drop_duplicate(bks)
# words operations
def import_words(self, fp=(CLIPPATH+WORDFN)):
def import_words(self, fp=(os.path.join(CURRPATH,WORDFN))):
"""import words from local file or kindle
vocab.db -> tables: BOOK_INFO, LOOKUPS, WORDS
@@ -784,6 +789,19 @@ class kMan:
return [nu, bk_wd_num]
class Util:
def __init__(self, parent=None):
pass
@staticmethod
def get_app_path():
"""Returns the base application path."""
if hasattr(sys, 'frozen'):
# Handles PyInstaller
return os.path.dirname(sys.executable) # 使用pyinstaller打包后的exe目录
return os.path.dirname(__file__) # 没打包前的py目录
if __name__=='__main__':
#books = defaultdict(dict)
km = kMan()
@@ -815,4 +833,3 @@ if __name__=='__main__':
# print data with json format
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))

BIN
kmanapp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -9,11 +9,17 @@
import sys
import os
import operator
from time import sleep
import pandas as pd
#import pandas as pd
from mtable import mTable
import threading
from PySide2.QtWidgets import (QMainWindow, QApplication, QLabel, QAbstractItemView, QHeaderView)
#if hasattr(sys, 'frozen'):
# os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
from PySide2.QtWidgets import (QMainWindow, QApplication, QMessageBox,
QFileDialog, QLabel, QAbstractItemView, QHeaderView)
from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt)
from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon)
@@ -81,6 +87,10 @@ class kmanWindow(QMainWindow):
self.km = kMan()
self.spide = bookInfoSpide()
### in order to smaller the package,
### substitute pandas table with mTable
self.mt = mTable()
# initial check order:
# 1. backup file bk.data ->
# 2. kindle(My Clippings.txt) ->
@@ -89,9 +99,8 @@ class kmanWindow(QMainWindow):
if os.path.exists(BACKUPNOTEFN) and os.path.exists(BACKUPWORDFN):
self.books_data = self.km.json2dict(BACKUPNOTEFN)
self.words_data = self.km.json2dict(BACKUPWORDFN)
if (len(self.books_data)*len(self.words_data[0]))>=1:
self.books_data = self.km.json2dict(BACKUPNOTEFN)
self.words_data = self.km.json2dict(BACKUPWORDFN)
if self.books_data and len(self.books_data) >=1 and \
self.words_data and len(self.words_data[0])>=1:
flg = 1
if not flg:
@@ -163,6 +172,7 @@ class kmanWindow(QMainWindow):
ui.tablemodel = nTableModel(data)
ui.tableView.verticalHeader().hide()
ui.tableView.setModel(self.ui.tablemodel)
ui.tableView.setSortingEnabled(True)
self.ui.textEdit.installEventFilter(self)
@@ -319,12 +329,17 @@ class kmanWindow(QMainWindow):
def convert_to_panda(self, mlist, tp=0):
if tp==0:
pdframe = pd.DataFrame(mlist, \
#### XXX remove pandas
#pdframe = pd.DataFrame(mlist, \
# columns = ['Type','Bookname','Author','Position','Date','Content'])
self.mt.dataframe(mlist, \
columns = ['Type','Bookname','Author','Position','Date','Content'])
else:
pdframe = pd.DataFrame(mlist, \
#pdframe = pd.DataFrame(mlist, \
# columns = ['Word','Bookname','Author','Category'])
self.mt.dataframe(mlist, \
columns = ['Word','Bookname','Author','Category'])
return pdframe
return self.mt
def tabledata_update_slot(self, s):
print('call tabledata_update_slot() {}'.format(s))
@@ -471,8 +486,8 @@ class kmanWindow(QMainWindow):
self.messagebox(ico=2, info='\n\n kindle is not connected')
return 0
self.books_data = self.km.import_clips(fp+'documents/'+CLIPFN)
self.words_data = self.km.import_words(fp+'system/vocabulary/'+WORDFN)
self.books_data = self.km.import_clips(os.path.join(fp,'documents',CLIPFN))
self.words_data = self.km.import_words(os.path.join(fp,'system','vocabulary',WORDFN))
[self.filter_books, self.filter_list] = self.km.filter_clips(self.books_data)
self.filter_wordlist = self.km.filter_words(self.words_data)
@@ -676,6 +691,8 @@ class kmanWindow(QMainWindow):
# backup file when kman closed
# so we can read backup file when kman start
def closeEvent(self, e):
if not os.path.exists(BACKUPFOLDER): os.mkdir(BACKUPFOLDER)
with open(BACKUPNOTEFN, 'w', encoding='utf8', errors='ignore') as fw:
fw.write(self.km.dict2json(self.books_data))
with open(BACKUPWORDFN, 'w', encoding='utf8', errors='ignore') as fw:
@@ -711,27 +728,60 @@ class nTableModel(QAbstractTableModel):
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
#### XXX remove pandas
#value = self._data.iloc[index.row(), index.column()]
value = self._data.get_iat(index.row(), index.column())
self.tabledata_update[str].emit('{} {}'.format(index.row(),index.column()))
return str(value)
def rowCount(self, index):
return self._data.shape[0]
#### XXX remove pandas
#return self._data.shape[1]
return self._data.get_num_rows()
def columnCount(self, index):
return self._data.shape[1]
#### XXX remove pandas
#return self._data.shape[1]
return self._data.get_num_columns()
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
#### XXX remove pandas
#return str(self._data.columns[section])
return str(self._data.get_columns()[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
#### XXX remove pandas
#return str(self._data.index[section])
return str(self._data.get_index()[section])
# cheer!!!
def sort(self, column, order):
"""Sort table by given column number.
"""
self.layoutAboutToBeChanged.emit()
self._data.set_data(sorted(self._data.get_data(), key=operator.itemgetter(column)))
if order == Qt.DescendingOrder:
self._data.reverse()
self.layoutChanged.emit()
if __name__ == "__main__":
print('sys.path[0]', sys.path[0])
print('sys.argv[0]', sys.argv[0])
print('os.path.realpath(sys.executable)', os.path.realpath(sys.executable))
print('os.path.realpath(sys.argv[0]))', os.path.realpath(sys.argv[0]))
print('os.path.dirname(os.path.realpath(sys.executable))',
os.path.dirname(os.path.realpath(sys.executable)))
print('os.path.dirname(os.path.realpath(sys.argv[0]))',
os.path.dirname(os.path.realpath(sys.argv[0])))
util = Util()
print('get_app_path',util.get_app_path())
app = QApplication(sys.argv)
kmw = kmanWindow()
icon = QIcon()

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.2, 2020-06-20T21:18:52. -->
<!-- Written by QtCreator 4.11.2, 2020-06-25T21:57:36. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@@ -55,5 +55,6 @@
<file>icons/amazon.png</file>
<file>icons/flower.png</file>
<file>kmanapp.ico</file>
<file>kmanapp.png</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

31
makepkg.md Normal file
View File

@@ -0,0 +1,31 @@
# 打包问题
1. 'ValueError: too many values to unpack (expected 2)
A: [资源文件打包配置](https://blog.csdn.net/weixin_42052836/article/details/82315118)
资源文件包括打包的python项目使用的相关文件如图标文件文本文件等。对于此类资源文件的打包需要设置Analysis的datas如例子所示datas接收元组datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')]。元组的组成为(原项目中资源文件路径,打包后路径),例子中的(SETUP_DIR+'lib\\icon','lib\\icon')表示从D:\\install_test\\FASTPLOT\\lib\\icon下的图标文件打包后放入打包结果路径下的lib\\icon目录。
1. (kmanenv) [gavin@gavin-2 tkman]$open Release/kmanapp.app/
FAIL - LSOpenURLsWithRole() failed with error -10810 for the file /Users/mark/penv/tkman/Release/kmanapp.app.
(kmanenv) [gavin@gavin-2 tkman]$Release/kmanapp.app/Contents/MacOS/kmanapp
OK
A:
1. 到其他路径区执行(kmanenv) [gavin@gavin-2 penv]$py kman/kmanapp.py看有没有问题
2. 所有路径合并,不用+, 用os.path.join()
3.
1. (kmanenv) [gavin@gavin-2 tkman]$pyinstaller --distpath Release -w -c -i kmanapp.ico kmanapp.spec
21750 ERROR: Can not find path ./libshiboken2.abi3.5.15.dylib (needed by /Users/mark/.virtualenvs/kmanenv/lib/python3.7/site-packages/PySide2/QtGui.abi3.so)
A: rm -fr __pycache__, 重新打包
1. (kmanenv) [gavin@gavin-2 kman]$Release/kmanapp.app/Contents/MacOS/kmanapp
NameError: name 'kMan' is not defined
A: pathex=['/Users/mark/penv/kman',]
# reference
1. [ModuleNotFoundError: No module named 'pkg_resources.py2_warn' ](https://blog.csdn.net/qq_40608730/article/details/104864943)
https://www.cnblogs.com/yemeng/p/6253097.html
https://blog.csdn.net/SCDS_Zyx/article/details/82052396

128
mtable.py Normal file
View File

@@ -0,0 +1,128 @@
#########################################################
## @file : mtable.py
## @desc : pandas is to large,
## so i implement a simple table data structure
## by my self
## @create : 2020/06/25
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
class mTable():
def __init__(self, data=[], index=[], columns=[]):
self._data = data
self._index = index
self._columns = columns
self._width = 10
def dataframe(self, data=[], index=[], columns=[]):
self._data = data
self._index = index
self._columns = columns
def get_iat(self, row: int, column: int):
""" just like pandas iat"""
return self._data[row][column] if len(self._data)>0 else None
def get_num_rows(self):
return len(self._data) if len(self._data)>0 else 0
def get_num_columns(self):
return len(self._columns) if len(self._columns)>0 else 0
def reverse(self):
self._data.reverse()
def set_data(self, data=[]):
""" get table content"""
self._data = data
def get_data(self):
""" get table content"""
return self._data
def get_index(self):
""" just like pandas index"""
return self._index
def get_columns(self):
""" just like pandas columns"""
return self._columns
def set_repr_width(self, width):
self._width = width
def __repr__(self):
""" print table """
w = self._width
s = ''.ljust(w-1,' ')
for c in self._columns: s += c.ljust(w,' ')
s += '\n'
len_index = len(self._index)
for i, r in enumerate(self._data):
s += self._index[i].ljust(w,' ') if i<len_index else str(i).ljust(w,' ')
for c in r:
s += str(c).ljust(w,' ')
s += '\n'
return s
def __next__(self):
# self.a就是当前要迭代的值
result = self.a
# 计算斐波那契数列的下一个值并将a变成原来的b将b变成下一个值
self.a, self.b = self.b, self.a + self.b
# 返回当前迭代的值
return result
def __iter__(self):
return self
if __name__=='__main__':
print('\n---------------------')
data = [['Ohiox','Ohio','Ohio','Nevada','Nevada'],
[2000,2001,2002,2001,2002],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
]
mt = mTable(data,
index = ['one', 'two','three','four','five'],
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[0, 0]\n', mt.get_iat(0, 0))
print('== frame.iat[1, 1]\n', mt.get_iat(1, 1))
print('== frame.iat[2, 2]\n', mt.get_iat(2, 2))
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data,
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data)
mt.set_repr_width(20)
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable()
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())

View File

@@ -1,3 +1,2 @@
PySide2==5.15.0
pandas==1.0.3
requests==2.23.0

View File

@@ -1,83 +0,0 @@
# CG test pandas
import pandas as pd
from kman import *
data = {
'state':['Ohio','Ohio','Ohio','Nevada','Nevada'],
'year':[2000,2001,2002,2001,2002],
'pop':[1.5,1.7,3.6,2.4,2.9],
'xx':[1.5,1.7,3.6,2.4,2.9]
}
"""
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
"""
frame = pd.DataFrame(data,
index = ['one', 'two','three','four','five'],
columns = ['year','state','pop','debt','xx'])
print(frame.iat[0, 0])
print(frame.iat[1, 1])
print(frame.iat[2, 2])
print(frame.iat[2, 1])
'''
print('\n---------------------')
print(frame)
print('\n---------------------')
frame['debt']=16.5
print(frame)
print('\n---------------------')
print(frame.iloc[2]) #line 2
print('\n---------------------')
print(frame.iloc[[2,3],0:2])
print('\n---------------------')
print(frame.iloc[[2,3],0:2])
print('\n---------------------')
print(frame.iat[1, 1])
print('\n---------------------')
for index, row in frame.iterrows():
print(index)
print(row)
print('\n---------------------')
for row in frame.iterrows():
print(row)
#print(row[0], row[1])
print('\n---------------------')
print(frame.head(2))
print('\n---------------------')
print(frame.columns)
print('\n---------------------')
print(frame.index)
print('\n---------------------')
print(frame.to_dict())
print('\n---------------------')
'''
km = kMan()
books = km.import_clips()
print(books)
print('\n---------------------')
frame = pd.DataFrame(books)
print(frame)
print('\n---------------------')
data = [['Ohio','Ohio','Ohio','Nevada','Nevada'],
[2000,2001,2002,2001,2002],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
]
frame = pd.DataFrame(data,
columns = ['year','state','pop','debt','xx'])
print(frame)

View File

@@ -1,39 +0,0 @@
#########################################################
## @file : tdict.py
## @desc : test dict
## @create : 2020/05/26
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import re
from collections import defaultdict
t = {'a':{'b':{'c':1}}}
t['a']['b']['c']=2
t['a']=2
print(t)
t1 = {'a':{'b':{'c':'3'}}}
t2 = t1
print(t2)
t3 = {'b':{'c':'4'}}
t4 = defaultdict(dict)
t4['a'] = t3
print(t4)
t4['a1']['b2'] = {'c3':'5'}
print(t4)
t4['a1']['d2'] = {'c3':'6'}
print(t4)
t4['a1']['e2'] = {'c3':'7'}
print(t4)
t4['b1']['b2'] = {'b5':1}
print(t4)
t4['b1']['b2']['b3'] = {'b5':1}
print(t4)

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +0,0 @@
# CG test logging
import logging
import os.path
import time
logger = logging.getLogger()
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.INFO)
logger.setLevel(logging.DEBUG)
tt = 'xxx'
tt1 = 'yyy'
logger.debug('this is a logger debug message {} {}'.format(tt,tt1))
logger.info('this is a logger info message')
logger.warning('this is a logger warning message')
logger.error('this is a logger error message')
logger.critical('this is a logger critical message')

View File

@@ -1,50 +0,0 @@
#########################################################
## @file : tparseamazon.py
## @desc : test parse amazon response
## @create : 2020/05/26
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import re
import json
s =['''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">丹·琼斯(Dan Jones)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">杰弗里·瓦夫罗(Geoffrey Wawro)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">克里斯托弗·希伯特(Christopher Hibbert)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">罗斯·金(Ross King)</span><span class="a-size-base" dir="auto">等等。</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">马克·哈里斯</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">黎绮妮</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">马克·哈里斯</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">黎绮妮</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[美]威廉·厄本(William Urban)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">陆大鹏</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">刘晓晖</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[英]安德鲁·罗伯茨(Andrew Roberts)</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">苏然</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[英]安德鲁·罗伯茨(Andrew Roberts)</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">苏然</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">堀田江理(Eri Hotta)</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">景跃进</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">张小劲</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">余逊达</span></div>''']
tre = ['''<div data-asin="B07S46LMCK" data-index="3" data-uuid="ef76dd52-2ab2-4372-8597-2c3258698b6e" data-component-type="s-search-result" class="sg-col-20-of-24 s-result-item s-asin sg-col-0-of-12 sg-col-28-of-32 sg-col-16-of-20 sg-col sg-col-32-of-36 sg-col-12-of-16 sg-col-24-of-28"><div class="sg-col-inner">''',
'''<img src="https://images-cn.ssl-images-amazon.com/images/I/41JFUwuJPCL._AC_UY218_.jpg"''',
'''alt="小窗幽记(国学大书院)(为人处世的智慧之果 修身齐家的行动指南)"''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">野田洋次郎</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">蒋青青</span></div>''',
'''<span aria-label="4.7 颗星,最多 5 颗星">''',
'''<span class="a-letter-space"></span></div></div>''']
for t in s:
ret = re.split('<span.+?auto\">|<\/span',t)
fret = ret[3::4]
#print(json.dumps(re.split('<span.+?auto\">|<\/span',t), indent=2, ensure_ascii=False))
print(','.join(fret))
re_asin = re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img = re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn = re.compile(r'''^alt=\"(.+?)\"$''')
re_author = re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate = re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end = re.compile(r'''<\/body><\/html>''')
re_end = re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
print(re.search(re_asin, tre[0]).group(1))
print(re.search(re_img , tre[1]).group(1))
print(re.search(re_bn , tre[2]).group(1))
print(re.search(re_author,tre[3]).group(0))
print(re.search(re_rate, tre[4]).group(1))
print(re.search(re_end , tre[5]).group(0))

View File

@@ -1,341 +0,0 @@
#########################################################
## @file : tparsedbcomments.py
## @desc : parse douban comments & reviews
## @create : 2020/06/15
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
#comments - https://book.douban.com/subject/26591910/comments/hot?p=1
# https://book.douban.com/subject/26591910/comments/
#reviews - https://book.douban.com/subject/26591910/reviews/
# https://book.douban.com/subject/26591910/reviews?start=0
import requests
import json
import re
import os
import subprocess
import logging
from collections import defaultdict
# log info
logger=logging.getLogger()
logger.addHandler(logging.FileHandler('log'))
logger.setLevel(logging.DEBUG)
FROMFILE=1
ISDOUBAN=1
IMGPATH = './downimg'
LINKPREF = 'https://book.douban.com/subject/' \
if ISDOUBAN else 'https://www.amazon.cn/s?k='
mheaders = {
'Host': 'www.douban.com',
'Referer': 'http://www.douban.com',
'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
#"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
mparams = {}
murl = ""
if ISDOUBAN==1:
mparams['Host']='www.douban.com',
mparams['search_text'] = 'bkname_xxx'
mparams['cat']='1001'
mparams['k']='bookname_xxx'
murl = "https://search.douban.com/book/subject_search"
else:
mheaders['Host']='www.amazon.cn'
mheaders['Referer']='http:/www.amazon.cn'
#https://www.amazon.cn/s?k={bookname}&i=stripbooks&__mk_zh_CN=亚马逊网站&ref=nb_sb_noss
mparams['Host']='www.amazon.cn'
mparams['k']='bkname_xxx'
mparams['i']='stripbooks'
mparams['__mk_zh_CN=']='亚马逊网站'
mparams['reg']='nb_sb_noss'
murl = 'https://www.amazon.cn/s'
testbooks=['24堂财富课',
'甲骨文',
'庆余年(精校版)',
'商君书-中华经典名著全本全注全译丛书',
'苏世民:我的经验与教训2018读桥水达利欧的原则2020看黑石苏世民的经验!一本书读懂从白手起家到华尔街新国王的传奇人生)',
'杨伯峻_论语译注',
'小窗幽记',
'少年凯歌',
'投资要义',
'白鱼解字',
'历史的巨镜',
'货币的教训',
'钱从哪里来',
'中国古代简史',
'罗马人的故事(套装共15册)',
'改变心理学的40项研究',
'如何假装懂音乐',
'管子(上下册)--中华经典名著全本全注全译(精)',
'投资中最简单的事',
'薛兆丰经济学讲义',
'枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)',
'中央帝国的哲学密码',
'新编说文解字大全集(超值白金版)',
'市场的逻辑(增订本)',
'金融的本质:伯南克四讲美联储(看一个风云人物的金融思考)',
'从零开始学写作:个人增值的有效方法',
'中国国家治理的制度逻辑:一个组织学研究',
'中国为什么有前途对外经济关系的战略潜能第3版',
'日本的世界观(《剑桥日本史》主编凝练之作三个人物故事串起日本两百年变局了解近代日本转向的必读之书理想国出品))']
class bookInfoSpide():
[re_bn,re_bn,re_score,re_star,re_author,re_end]=[None,None,None,None,None,None]
if ISDOUBAN==1:
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\".+?rating_nums\">(.+?)<''', re.S)
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\"''')
re_star=re.compile(r'''^<span class=\"allstar(\d+)\"></span>''')
re_score=re.compile(r'''class=\"rating_nums\">(.+?)<''')
re_ratenum=re.compile(r'''^<span>\((\d+)人评价\)</span>''')
re_author=re.compile(r'''class=\"subject-cast\">(.+?)<''')
else:
re_asin=re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img=re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn=re.compile(r'''^alt=\"(.+?)\"$''')
re_author=re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate=re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end=re.compile(r'''<\/body><\/html>''')
re_end=re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
def __init__(self):
pass
def grab_book_info(self, mbkn: str):
"""mbkn - bookname to be spided
return: {
"25853071": { # sid
"link":"https://....xxxxx"
"bookname": "庆余年",
"img": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2575362797.jpg",
"score": "8.0",
"ratenum": "1000",
"author": "猫腻"
"publisher": "中华书局"
"publishing": "2015"
},...}
"""
bkinfo=defaultdict(dict)
sid=None
stat=None
if FROMFILE:
with open('./tdouban.data', 'r', encoding='utf8', errors='ignore') as f:
resp=f.read()
else:
if ISDOUBAN==1: #douban
mparams['search_text'] = mbkn
else: #amazon
mparams['k'] = mbkn
try:
session = requests.Session()
session.header = mheaders
session.params = mparams
r = session.get( url=murl, headers=mheaders, params=mparams)
#r = requests.get( url=murl, headers=mheaders, params=mparams)
except requests.exceptions.ConnectionError:
print('ConnectionError -- please wait 3 seconds')
time.sleep(3)
except requests.exceptions.ChunkedEncodingError:
print('ChunkedEncodingError -- please wait 3 seconds')
time.sleep(3)
except:
print('Unfortunitely -- An Unknow Error Happened, Please wait 3 seconds')
time.sleep(3)
if r.status_code != 200:
print('grab book {} info from webside failure'.format(mbkn))
if ISDOUBAN==1:
stat='SID'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='SID':
ret=re.search(self.re_bn, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,sid)
bkinfo[sid]['bookname']=ret.group(2)
bkinfo[sid]['img']=ret.group(3)
stat='STAR'
continue
elif stat=='STAR':
ret=re.search(self.re_star, line)
if ret:
star = ret.group(1)
if star=='00':
stat='AUTHOR'
elif int(star) > 0:
stat='SCORE'
elif stat=='SCORE':
ret=re.search(self.re_score, line)
if ret:
bkinfo[sid]['score']=ret.group(1)
stat='RATENUM'
continue
elif stat=='RATENUM':
ret=re.search(self.re_ratenum, line)
if ret:
bkinfo[sid]['ratenum']=ret.group(1)
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
tt=ret.group(1).split(' / ')
if len(tt)>=3:
*author, bkinfo[sid]['publisher'], bkinfo[sid]['publishing']=tt
bkinfo[sid]['author']='/'.join(author)
else:
bkinfo[sid]['author']=ret[0]
stat='SID'
else: continue
else:
stat='ASIN'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='ASIN':
ret=re.search(self.re_asin, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,ret.group(1))
stat='IMG'
continue
elif stat=='IMG':
ret=re.search(self.re_img, line)
if ret:
bkinfo[sid]['img']=ret.group(1)
stat='BOOKNAME'
continue
elif stat=='BOOKNAME':
ret=re.search(self.re_bn, line)
if ret:
bkname=re.split(r'[(\s]',ret.group(1).strip())[0]
bkinfo[sid]['bookname']=bkname
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
author=','.join(re.split('<span.+?auto\">|<\/span', ret.group(0))[3::4])
bkinfo[sid]['author']=author
stat='RATE'
continue
elif stat=='RATE':
ret=re.search(self.re_rate, line)
if ret:
bkinfo[sid]['rate']=ret.group(1).split(' ')[0]
stat='AUTHOR'
continue
else: continue
if re.search(self.re_end, line):
stat=='ASIN'
continue
return [mbkn, bkinfo]
def filter_spide_book(self, mbkinfo):
"""
mbkinfo:
douban
"10530219": {
"link": "https://book.douban.com/subject/10530219",
"bookname": "市场的逻辑",
"img": "https://img3.doubanio.com/view/subject/s/public/s8912552.jpg",
"score": "8.3",
"ratenum": "218",
"publisher": "世纪文景 上海人民出版社",
"publishing": "2012",
"author": "张维迎"
},...}
amazon
"孟子": {
"link": "https://....B07RN73425",
"bookname": "古典名著普及文库:孟子",
"img": "https://images-cn.ssl-images-amazon.com/images/I/511vbVrhIBL._AC_UY218_.jpg",
"rate": "3.9"
"author": "孙钦善",
}
"""
#booklink - https://book.douban.com/subject/{sid}
# f1/d1: mbkn include in bookname
# f2/d2: bookname include mbkn
# f3/d3: mbkn and bookname different
[f1,f2,f3]=[0,0,0]
[d1,d2,d3] =[{},{},{}]
mbkn=mbkinfo[0]
for k,v in mbkinfo[1].items():
bkn=v['bookname']
if len(v)==8:
if (not f1) and (mbkn in bkn):
f1=1
d1={mbkn:v}
elif (not f1) and (not f2) and (bkn in mbkn):
f2=1
d2={mbkn:v}
elif (not f3):
f3=1
d3={mbkn:v}
else: continue
else:
continue
if f1:
return d1
elif f2:
return d2
elif f3:
return d3
return None
def down_book_img(self, mbkinfo):
import os
import socket
from urllib.request import urlretrieve
headers={'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
for k,v in mbkinfo.items():
link=v['img']
if not os.path.exists(IMGPATH): os.mkdir(IMGPATH)
p=os.path.join(IMGPATH,link.split('/')[-1])
try:
img=requests.get(link, headers=headers)
if img.status_code==200:
with open(p, 'wb') as fp:
fp.write(img.content)
except Exception as e:
print(e)
if __name__=='__main__':
spide=bookInfoSpide()
for bkname in testbooks:
bkname=re.split(r'[\(\-\:_\s]',bkname.strip())[0]
bkinfo=spide.grab_book_info(bkname)
filter_bkinfo=spide.filter_spide_book(bkinfo)
if filter_bkinfo: spide.down_book_img(filter_bkinfo)
#logger.debug('================ {} ================'.format(bkname))
#logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))

View File

@@ -1,345 +0,0 @@
#########################################################
## @file : tparsedbinfo.py
## @desc : kindle note managerment tool
## @create : 2020/06/15
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import requests
import json
import re
import os
import subprocess
import logging
from collections import defaultdict
# log info
logger=logging.getLogger()
logger.addHandler(logging.FileHandler('log'))
logger.setLevel(logging.DEBUG)
FROMFILE=1
ISDOUBAN=1
IMGPATH = './downimg'
LINKPREF = 'https://book.douban.com/subject/' \
if ISDOUBAN else 'https://www.amazon.cn/s?k='
mheaders = {
'Host': 'www.douban.com',
'Referer': 'http://www.douban.com',
'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
#"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
mparams = {}
murl = ""
if ISDOUBAN==1:
mparams['Host']='www.douban.com',
mparams['search_text'] = 'bkname_xxx'
mparams['cat']='1001'
mparams['k']='bookname_xxx'
murl = "https://search.douban.com/book/subject_search"
else:
mheaders['Host']='www.amazon.cn'
mheaders['Referer']='http:/www.amazon.cn'
#https://www.amazon.cn/s?k={bookname}&i=stripbooks&__mk_zh_CN=亚马逊网站&ref=nb_sb_noss
mparams['Host']='www.amazon.cn'
mparams['k']='bkname_xxx'
mparams['i']='stripbooks'
mparams['__mk_zh_CN=']='亚马逊网站'
mparams['reg']='nb_sb_noss'
murl = 'https://www.amazon.cn/s'
testbooks=['24堂财富课',
'甲骨文',
'庆余年(精校版)',
'商君书-中华经典名著全本全注全译丛书',
'苏世民:我的经验与教训2018读桥水达利欧的原则2020看黑石苏世民的经验!一本书读懂从白手起家到华尔街新国王的传奇人生)',
'杨伯峻_论语译注',
'小窗幽记',
'少年凯歌',
'投资要义',
'白鱼解字',
'历史的巨镜',
'货币的教训',
'钱从哪里来',
'中国古代简史',
'罗马人的故事(套装共15册)',
'改变心理学的40项研究',
'如何假装懂音乐',
'管子(上下册)--中华经典名著全本全注全译(精)',
'投资中最简单的事',
'薛兆丰经济学讲义',
'枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)',
'中央帝国的哲学密码',
'新编说文解字大全集(超值白金版)',
'市场的逻辑(增订本)',
'金融的本质:伯南克四讲美联储(看一个风云人物的金融思考)',
'从零开始学写作:个人增值的有效方法',
'中国国家治理的制度逻辑:一个组织学研究',
'中国为什么有前途对外经济关系的战略潜能第3版',
'日本的世界观(《剑桥日本史》主编凝练之作三个人物故事串起日本两百年变局了解近代日本转向的必读之书理想国出品))']
class bookInfoSpide():
[re_bn,re_bn,re_score,re_star,re_author,re_description,re_end]=[None,None,None,None,None,None,None]
if ISDOUBAN==1:
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\".+?rating_nums\">(.+?)<''', re.S)
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\"''')
re_star=re.compile(r'''^<span class=\"allstar(\d+)\"></span>''')
re_score=re.compile(r'''class=\"rating_nums\">(.+?)<''')
re_ratenum=re.compile(r'''^<span>\((\d+)人评价\)</span>''')
re_author=re.compile(r'''class=\"subject-cast\">(.+?)<''')
re_description=re.compile(r'''^<p>(.+?)(<\/p>){0,1}$''')
else:
re_asin=re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img=re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn=re.compile(r'''^alt=\"(.+?)\"$''')
re_author=re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate=re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end=re.compile(r'''<\/body><\/html>''')
re_end=re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
def __init__(self):
pass
def grab_book_info(self, mbkn: str):
"""mbkn - bookname to be spided
return: {
"25853071": { # sid
"link":"https://....xxxxx"
"bookname": "庆余年",
"img": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2575362797.jpg",
"score": "8.0",
"ratenum": "1000",
"author": "猫腻"
"publisher": "中华书局"
"publishing": "2015"
},...}
"""
bkinfo=defaultdict(dict)
sid=None
stat=None
if FROMFILE:
with open('./tdouban.data', 'r', encoding='utf8', errors='ignore') as f:
resp=f.read()
else:
if ISDOUBAN==1: #douban
mparams['search_text'] = mbkn
else: #amazon
mparams['k'] = mbkn
try:
session = requests.Session()
session.header = mheaders
session.params = mparams
r = session.get( url=murl, headers=mheaders, params=mparams)
#r = requests.get( url=murl, headers=mheaders, params=mparams)
except requests.exceptions.ConnectionError:
print('ConnectionError -- please wait 3 seconds')
time.sleep(3)
except requests.exceptions.ChunkedEncodingError:
print('ChunkedEncodingError -- please wait 3 seconds')
time.sleep(3)
except:
print('Unfortunitely -- An Unknow Error Happened, Please wait 3 seconds')
time.sleep(3)
if r.status_code != 200:
print('grab book {} info from webside failure'.format(mbkn))
if ISDOUBAN==1:
stat='SID'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='SID':
ret=re.search(self.re_bn, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,sid)
bkinfo[sid]['bookname']=ret.group(2)
bkinfo[sid]['img']=ret.group(3)
stat='STAR'
continue
elif stat=='STAR':
ret=re.search(self.re_star, line)
if ret:
star = ret.group(1)
if star=='00':
stat='AUTHOR'
elif int(star) > 0:
stat='SCORE'
elif stat=='SCORE':
ret=re.search(self.re_score, line)
if ret:
bkinfo[sid]['score']=ret.group(1)
stat='RATENUM'
continue
elif stat=='RATENUM':
ret=re.search(self.re_ratenum, line)
if ret:
bkinfo[sid]['ratenum']=ret.group(1)
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
tt=ret.group(1).split(' / ')
if len(tt)>=3:
*author, bkinfo[sid]['publisher'], bkinfo[sid]['publishing']=tt
bkinfo[sid]['author']='/'.join(author)
else:
bkinfo[sid]['author']=ret[0]
stat='DESCRIPTION'
continue
elif stat=='DESCRIPTION':
ret=re.search(self.re_description, line)
if ret:
bkinfo[sid]['description']=ret.group(1)
stat='SID'
continue
else: continue
else:
stat='ASIN'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='ASIN':
ret=re.search(self.re_asin, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,ret.group(1))
stat='IMG'
continue
elif stat=='IMG':
ret=re.search(self.re_img, line)
if ret:
bkinfo[sid]['img']=ret.group(1)
stat='BOOKNAME'
continue
elif stat=='BOOKNAME':
ret=re.search(self.re_bn, line)
if ret:
bkname=re.split(r'[(\s]',ret.group(1).strip())[0]
bkinfo[sid]['bookname']=bkname
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
author=','.join(re.split('<span.+?auto\">|<\/span', ret.group(0))[3::4])
bkinfo[sid]['author']=author
stat='RATE'
continue
elif stat=='RATE':
ret=re.search(self.re_rate, line)
if ret:
bkinfo[sid]['rate']=ret.group(1).split(' ')[0]
stat='AUTHOR'
continue
else: continue
if re.search(self.re_end, line):
stat=='ASIN'
continue
return [mbkn, bkinfo]
def filter_spide_book(self, mbkinfo):
"""
mbkinfo:
douban
"10530219": {
"link": "https://book.douban.com/subject/10530219",
"bookname": "市场的逻辑",
"img": "https://img3.doubanio.com/view/subject/s/public/s8912552.jpg",
"score": "8.3",
"ratenum": "218",
"publisher": "世纪文景 上海人民出版社",
"publishing": "2012",
"author": "张维迎"
},...}
amazon
"孟子": {
"link": "https://....B07RN73425",
"bookname": "古典名著普及文库:孟子",
"img": "https://images-cn.ssl-images-amazon.com/images/I/511vbVrhIBL._AC_UY218_.jpg",
"rate": "3.9"
"author": "孙钦善",
}
"""
#booklink - https://book.douban.com/subject/{sid}
# f1/d1: mbkn include in bookname
# f2/d2: bookname include mbkn
# f3/d3: mbkn and bookname different
[f1,f2,f3]=[0,0,0]
[d1,d2,d3] =[{},{},{}]
mbkn=mbkinfo[0]
for k,v in mbkinfo[1].items():
bkn=v['bookname']
if len(v)==9:
if (not f1) and (mbkn in bkn):
f1=1
d1={mbkn:v}
elif (not f1) and (not f2) and (bkn in mbkn):
f2=1
d2={mbkn:v}
elif (not f3):
f3=1
d3={mbkn:v}
else: continue
else:
continue
if f1:
return d1
elif f2:
return d2
elif f3:
return d3
return None
def down_book_img(self, mbkinfo):
import os
import socket
from urllib.request import urlretrieve
headers={'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
for k,v in mbkinfo.items():
link=v['img']
if not os.path.exists(IMGPATH): os.mkdir(IMGPATH)
p=os.path.join(IMGPATH,link.split('/')[-1])
try:
img=requests.get(link, headers=headers)
if img.status_code==200:
with open(p, 'wb') as fp:
fp.write(img.content)
except Exception as e:
print(e)
if __name__=='__main__':
spide=bookInfoSpide()
for bkname in testbooks:
bkname=re.split(r'[\(\-\:_\s]',bkname.strip())[0]
bkinfo=spide.grab_book_info(bkname)
filter_bkinfo=spide.filter_spide_book(bkinfo)
if filter_bkinfo: spide.down_book_img(filter_bkinfo)
#logger.debug('================ {} ================'.format(bkname))
#logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))

View File

@@ -1,88 +0,0 @@
# -*- coding: utf-8 -*-
"""
@author: Taar
"""
#conversion of https://github.com/openwebos/qt/tree/master/examples/tutorials/modelview/5_edit
#I work with ints for values instead of strings (less cumbersome to add)
import sys
import pandas as pd
from PySide2.QtWidgets import *
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
QPixmap, QRadialGradient, QStandardItem, QStandardItemModel)
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
QAbstractTableModel, QObject, QPoint, QRect, QSize, QTime,
QUrl, Qt, QThread, Signal, QTimer)
class TableModel(QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = QTableView()
data = pd.DataFrame([
[1, 9, 2],
[1, 0, -1],
[3, 5, 2],
[3, 3, 2],
[5, 8, 9],
],
columns = ['A', 'B', 'C'],
index=['Row 1', 'Row 2', 'Row 3', 'Row 4', 'Row 5'])
self.model = TableModel(data)
self.table.setModel(self.model)
data = pd.DataFrame([
[1, 5, 5, 9],
[1, 5, 5, 9],
[1, 2, 1, 3],
[1, 1, 0, -1],
[1, 5, 5, 2],
[1, 5, 5, 8],
[1, 5, 5, 9],
],
columns = ['Aa', 'Bb', 'Cc', 'dd'],
index=['Row 1', 'Row 3', 'Row 4', 'Row 4', 'Row 5', '6', '7'])
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
if __name__ == '__main__':
app=QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()

View File

@@ -1,37 +0,0 @@
#########################################################
## @file : tsqlite.py
## @desc : test sqlite3
## @create : 2020/06/11
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import sqlite3
dbname = 'vocab.db'
conn = sqlite3.connect(dbname)
try:
cursor = conn.cursor()
sql1 = '''select * from words;'''
sql2 = '''select * from lookups;'''
# fetch one record
cursor.execute(sql2)
one=cursor.fetchone()
print(one)
# fetch all records
for row in cursor.fetchall():
print(row)
# fetch table structure
except Exception as e:
print('sql failure {}'.format(e))
pass
finally:
conn.close()

View File

@@ -1,6 +0,0 @@
import platform
print(platform.system(),platform.machine(),platform.platform(aliased=0, terse=0) )

View File

@@ -1,106 +0,0 @@
import sys
import pyqtgraph as pg
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
import numpy as np
class PlotSin(QThread):
# 定义信号self.y 是 numpy.array所以信号数据类型为 object
signal = pyqtSignal(object)
def __init__(self, parent=None):
super().__init__()
self.y = None
self.phase = 0
def sin(self):
self.x = np.arange(0, 3.0, 0.01)
self.y = np.sin(2 * np.pi * self.x + self.phase)
self.phase += 0.1
QThread.msleep(200) # 等待200毫秒
def run(self):
for _ in range(300):
self.sin()
self.signal.emit(self.y) # 向连接槽发射信号 self.y
class PlotSin_MainWindow(QDialog):
def __init__(self):
super().__init__()
self.initUI()
self.clock_time = 0
self.timer = QTimer(self) # 生成定时器
self.timer.timeout.connect(self.clock) # 绑定计时函数 self.clock
def initUI(self):
self.creatContorls("时间显示:")
self.creatResult("函数绘制:")
layout = QHBoxLayout()
layout.addWidget(self.controlsGroup)
layout.addWidget(self.resultGroup)
self.setLayout(layout)
self.beginButton.clicked.connect(self.clock_begin)
self.setGeometry(300, 300, 600, 300)
self.setWindowTitle('Plot Sine')
self.show()
def creatContorls(self,title):
self.controlsGroup = QGroupBox(title)
self.beginButton = QPushButton("开始")
numberLabel = QLabel("运行时间:")
self.clockLabel = QLabel("")
controlsLayout = QGridLayout()
controlsLayout.addWidget(numberLabel, 0, 0)
controlsLayout.addWidget(self.clockLabel, 0, 1)
controlsLayout.addWidget(self.beginButton, 3, 0)
self.controlsGroup.setLayout(controlsLayout)
def creatResult(self,title):
self.resultGroup = QGroupBox(title)
self.guiplot = pg.PlotWidget()
gridLayout = QGridLayout()
gridLayout.addWidget(self.guiplot,0,2,2,3)
self.resultGroup.setLayout(gridLayout)
def clock_begin(self):
if not self.timer.isActive():
self.recorder_thread = PlotSin()
self.recorder_thread.signal.connect(self.displaySin) # 绑定信号槽函数
self.recorder_thread.start() # 线程执行
self.clock()
self.timer.start(1000)
else:
self.beginButton.setText("开始")
self.clockLabel.setText("")
self.recorder_thread.terminate() # 终止线程
self.timer.stop() # 终止定时器
self.clock_time = 0
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
def clock(self):
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
if self.clock_time == 0:
self.beginButton.setText("结束")
self.clock_time += 1
def plotSin(self, y):
self.guiplot.clear()
self.guiplot.plot(y)
def displaySin(self, y):
self.plotSin(y)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PlotSin_MainWindow()
window.show()
sys.exit(app.exec_())

View File

@@ -1,88 +0,0 @@
# from webside not test, only for reference XXX
#coding=utf8
import random
import requests
import hashlib
appid = 'xxxxxx'
secretKey = 'xxxxx'
def get_md5(string):#返回字符串md5加密
hl = hashlib.md5()
hl.update(string.encode('utf-8'))
return hl.hexdigest()
def en_to_zh(en_str):#英语翻译成中文
api_url = 'http://api.fanyi.baidu.com/api/trans/vip/translate'
salt = random.randint(32768,65536)
sign = get_md5(appid + en_str + str(salt) + secretKey)
api_data = {
'q':en_str,
'from':'en',
'to':'zh',
'appid':appid,
'salt':salt,
'sign':sign
}
req_get = requests.get(api_url,api_data)
result = req_get.json()
print(result)
return result['trans_result']
print(en_to_zh('test'))
'''
import urllib.request
import urllib.parse
import json
from tkinter import *
root=Tk()
root.title("翻译小程序")
sw = root.winfo_screenwidth()
#得到屏幕宽度
sh = root.winfo_screenheight()
#得到屏幕高度
ww = 500
wh = 300
x = (sw-ww) / 2
y = (sh-wh) / 2-50
root.geometry("%dx%d+%d+%d" %(ww,wh,x,y))
lb2=Label(root,text="输入英文翻译中文,或者输入中文翻译英文,按回车键翻译。--版权所有,翻录必究。")
lb2.place(relx=0, rely=0.05)
txt = Text(root,font=("宋体",20))
txt.place(rely=0.6, relheight=0.4,relwidth=1)
inp1 = Entry(root,font=("",20))
inp1.place(relx=0, rely=0.2, relwidth=1, relheight=0.25)
def run2(event):
txt.delete("0.0",END)
a = (inp1.get())
content = a
url='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
data={}
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15812376682056'
data['sign'] = 'a1246b257926af8432be022564ff79f5'
data['ts'] = '1581237668205'
data['bv'] = '656f750600466990f874a839d9f5ad23'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CLICKBUTTION'
data = urllib.parse.urlencode(data).encode('utf-8')
response = urllib.request.urlopen(url,data)
html = response.read().decode('utf-8')
target = json.loads(html)
s=("%s"%(target['translateResult'][0][0]['tgt'])+"\n")
print(s)
txt.insert(END, s)
def button1(event):
btn1 = Button(root, text='结果如下', font=("",12))
btn1.place(relx=0.35, rely=0.45, relwidth=0.2, relheight=0.15)
inp1.bind("<Return>",run2)
button1(1)
root.mainloop()
'''

View File

@@ -31,6 +31,7 @@ class TestKman(unittest.TestCase):
DELIMITER= '|'
self.km = kMan()
self.util = util()
global t_bm_sec
global t_hl_sec
@@ -232,6 +233,9 @@ class TestKman(unittest.TestCase):
logger.debug(json.dumps(bookinfo, indent=2, ensure_ascii=False))
logger.debug('========== 5 ==========\n')
logger.debug(json.dumps(lookups, indent=2, ensure_ascii=False))
logger.debug('========== 6 ==========\n')
logger.debug(json.dumps(words, indent=2, ensure_ascii=False))
# test filter_words()
logger.debug('========== 7 ==========\n')
self.km.filter_words(lookups, '中国历史风云录 (陈舜臣作品)', tp=0)

62
x
View File

@@ -1,62 +0,0 @@
《解读基金——我的投资观和实践》e2780
薛兆丰经济学讲义
xxxx bkn 解读基金——我的投资观与实践(修订版) {'link': 'https://book.douban.com/subject/30784282', 'bookname': '解读基金——我的投资观与实践(修订版)', 'img': 'https://img3.doubanio.com/view/subject/s/public/s32292360.jpg', 'score': '8.1', 'ratenum': '469', 'publisher': '中国经济出版社', 'publishing': '2020', 'author': '季凯帆', 'description': '季凯帆的网名LaoK恐怕比他的真名还要响亮因为他的系列科普帖《解读基金——我的投资观和实践》和博客曾经传遍了中国基金类网站的各个论坛成为基金投资者入门和提高...'} len 9
xxxx bkn 解读基金 {'link': 'https://book.douban.com/subject/2051332', 'bookname': '解读基金', 'img': 'https://img3.doubanio.com/view/subject/s/public/s4561451.jpg', 'score': '8.3', 'ratenum': '3268', 'publisher': '中国经济出版社', 'publishing': '2007', 'author': '季凯帆', 'description': '作者季凯帆的网名-LaoK恐怕要比他的真名还要响亮因为他的作品《我的投资观和实践》和他的博客几乎传遍了中国基金类网站的各个论坛成为基金投资者入门和提高的必读文...'} len 9
金融街
xxxx bkn 金融街购物中心 {'link': 'https://book.douban.com/subject/162518', 'bookname': '金融街购物中心', 'img': 'https://img3.doubanio.com/icon/g162518-1.jpg', 'score': '7.6', 'ratenum': '725', 'publisher': '中信出版集团', 'publishing': '2017', 'author': '梁成', 'description': '本书以一家名为“鑫城财富”的公司为背景,以主人公杨晓波的视角,讲述了这家公司在金融街上的繁荣与衰落,也解答了人们对金融街及金融行业的种种疑问。这或许只是金融...'} len 9
xxxx bkn 金融街 {'link': 'https://book.douban.com/subject/1036614', 'bookname': '金融街', 'img': 'https://img1.doubanio.com/view/subject/s/public/s1026488.jpg', 'score': '6.5', 'ratenum': '49', 'publisher': '春风文艺出版社', 'publishing': '2004', 'author': '陈一夫', 'description': '具有黑社会性质的犯罪团伙把持着拥有上市子公司的怒潮集团,他们虽已取得了数十亿巨额贷款,但却依然资不抵债,靠骗取银行贷款、圈股市资金度日。瘸子处长识破了怒潮集...'} len 9
xxxx bkn 金融街 {'link': 'https://book.douban.com/subject/30874491', 'bookname': '金融街', 'img': 'https://img3.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif', 'author': 'class="subject-cast">陈一夫 / 九州出版社<', 'description': '欢迎在北京金融街一带工作、生活的朋友们加入这个小组,讨论共同感兴趣的话题,分享收获与快乐!'} len 5
xxxx bkn 占领金融街 {'link': 'https://book.douban.com/subject/370556', 'bookname': '占领金融街', 'img': 'https://img3.doubanio.com/icon/g370556-1.jpg', 'author': 'class="subject-cast">鈴置洋孝<', 'description': '鈴置洋孝(鷲崎\u3000勲×緑川光五十嵐邦彦'} len 5
xxxx bkn The Bank Job {'link': 'https://book.douban.com/subject/1889152', 'bookname': 'The Bank Job', 'img': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p1521131641.jpg', 'score': '7.5', 'ratenum': '46711', 'publisher': '杰森·斯坦森', 'publishing': '2008', 'author': '原名:The Bank Job/罗杰·唐纳森', 'description': '本片根据真实历史事件改编。'} len 9
xxxx bkn 求金融街的同事中午吃饭 {'link': 'https://book.douban.com/subject/344904', 'bookname': '求金融街的同事中午吃饭', 'img': 'https://img9.doubanio.com/icon/user.jpg', 'score': '6.7', 'ratenum': '332', 'publisher': '中国友谊出版公司', 'publishing': '2018', 'author': '梁成', 'description': '故事发生在浮华光鲜的金融街,在这个看似高大上,实则暗水流深的商业世界里,交织着一场对“西南第一城”项目控股权的争夺。'} len 9
xxxx bkn 金融街没有爱情(Ⅰ、Ⅱ) {'link': 'https://book.douban.com/subject/34482165', 'bookname': '金融街没有爱情(Ⅰ、Ⅱ)', 'img': 'https://img3.doubanio.com/view/subject/s/public/s33325310.jpg', 'score': '7.3', 'ratenum': '68', 'publisher': '广西师范大学出版社', 'publishing': '2019', 'author': '刘玥', 'description': '◉内容简介'} len 9
xxxx bkn 金融街 {'link': 'https://book.douban.com/subject/1059122', 'bookname': '金融街', 'img': 'https://img3.doubanio.com/view/subject/s/public/s10284623.jpg', 'score': '6.7', 'ratenum': '19', 'publisher': '山东文艺出版社', 'publishing': '2003', 'author': '矫健', 'description': '金融街上故事多。咖啡之战展现早期期货界风浪迭起、硝烟弥漫的情景。红星风暴揭露任家炒股内幕疑云重重陷阱密布大豆激斗描绘中国加入WTO之后期货界最新状况...'} len 9
xxxx bkn 金融街:危险交易 {'link': 'https://book.douban.com/subject/34636557', 'bookname': '金融街:危险交易', 'img': 'https://img9.doubanio.com/view/subject/s/public/s33445355.jpg', 'score': '7.4', 'ratenum': '74', 'publisher': '中国友谊出版公司', 'publishing': '2019', 'author': '梁成', 'description': '终极之战一触即发私募基金、上市公司、金融大鳄……多方势力齐聚开启资本市场ZUI真实的博弈金钱世界ZUI残酷的厮杀'} len 9
xxxx bkn 老天津金融街 {'link': 'https://book.douban.com/subject/11606234', 'bookname': '老天津金融街', 'img': 'https://img9.doubanio.com/view/subject/s/public/s26096804.jpg', 'publisher': '天津人民出版社', 'publishing': '2013', 'author': '档案馆', 'description': '老天津金融街平装ISBN9787201067698作者'} len 7
xxxx bkn 愛と欲望の金融街 {'link': 'https://book.douban.com/subject/4175765', 'bookname': '愛と欲望の金融街', 'img': 'https://img9.doubanio.com/view/subject/s/public/s4083115.jpg', 'publisher': '講談社', 'publishing': '2003', 'author': '井村仁美', 'description': '出版社/著者からの内容紹介'} len 7
xxxx bkn 金融芝麻街 {'link': 'https://book.douban.com/subject/24526755', 'bookname': '金融芝麻街', 'img': 'https://img3.doubanio.com/view/subject/s/public/s26386853.jpg', 'publisher': '上書局', 'publishing': '2011', 'author': '蔡東豪', 'description': '金融和財經一直都是香港由政府到市民都最為關注的問題,蔡東豪繼《金錢之王》、《金融海嘯.我所學到的十件事》和《金融是文化》之後,又再推出新作,以金融和財經為切...'} len 7
大教堂与集市
xxxx bkn Rebuilding Notre Dame: Inside the Great Cathedral Rescue {'link': 'https://book.douban.com/subject/35034029', 'bookname': 'Rebuilding Notre Dame: Inside the Great Cathedral Rescue', 'img': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2599296429.jpg', 'score': '8.7', 'ratenum': '735', 'publisher': '', 'publishing': '2020', 'author': '原名:Rebuilding Notre Dame: Inside the Great Cathedral Rescue', 'description': '法国最著名哥特式建筑、法兰西最重要的文化遗产之一巴黎圣母院于2019年4月遭遇了百年难得一遇之大火整个教堂顶几乎被大火摧毁随后法国政府承诺将在五年内完成巴黎圣...'} len 9
xxxx bkn Andrea Bocelli : Music for Hope - Live from Duomo di Milano {'link': 'https://book.douban.com/subject/35034134', 'bookname': 'Andrea Bocelli : Music for Hope - Live from Duomo di Milano', 'img': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2595998004.jpg', 'score': '9.5', 'ratenum': '244', 'publisher': '安德烈·波切利', 'publishing': '2020', 'author': '原名:Andrea Bocelli : Music for Hope - Live from Duomo di Milano', 'description': 'On Easter Sunday (April 12, 2020), by invitation of the City and of the Duomo cathedral of Milan, Italian global music icon Andrea Bocelli gave a solo perfor...'} len 9
xxxx bkn Sobor {'link': 'https://book.douban.com/subject/35064531', 'bookname': 'Sobor', 'img': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2604453204.jpg', 'publisher': '谢尔盖·马林', 'publishing': '2020', 'author': '原名:Sobor/谢尔盖·金斯堡', 'description': '全球3000萬樂迷深情呼喚'} len 7
xxxx bkn La Catedral del mar {'link': 'https://book.douban.com/subject/30228956', 'bookname': 'La Catedral del mar', 'img': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2523139197.jpg', 'score': '8.2', 'ratenum': '152', 'publisher': '埃托尔·卢纳', 'publishing': '2018', 'author': '原名:La Catedral del mar/乔迪·弗拉德斯', 'description': '在14世纪的巴塞罗那一名农奴决心要获得财富和自由但却遭到了贵族阶层的鄙夷和宗教法庭的怀疑。'} len 9
xxxx bkn Katedra {'link': 'https://book.douban.com/subject/1417236', 'bookname': 'Katedra', 'img': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2494920420.jpg', 'score': '7.7', 'ratenum': '4932', 'publisher': '托默克·巴金斯基', 'publishing': '2002', 'author': '原名:Katedra', 'description': '朝圣者在日食之时来到神秘的大教堂门外。'} len 9
xxxx bkn Ancient Megastructures: St. Paul&#39;s Cathedral {'link': 'https://book.douban.com/subject/5265489', 'bookname': 'Ancient Megastructures: St. Paul&#39;s Cathedral', 'img': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2500960684.jpg', 'score': '8.6', 'ratenum': '175', 'publisher': '', 'publishing': '2009', 'author': '原名:Ancient Megastructures: St. Paul&#39;s Cathedral', 'description': '当代软件技术领域最重要的著作,中文版首次出版!'} len 9
xxxx bkn Kathedralen der Kultur {'link': 'https://book.douban.com/subject/25813839', 'bookname': 'Kathedralen der Kultur', 'img': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2215497480.jpg', 'score': '7.5', 'ratenum': '48', 'publisher': '维姆·文德斯', 'publishing': '2014', 'author': '原名:Kathedralen der Kultur', 'description': '&#39;If buildings could talk, what would they say about us?&#39; Cathedrals of Culture offers six startling responses to this question. This 3D film project about th...'} len 9
xxxx bkn Murder in the Cathedral {'link': 'https://book.douban.com/subject/3081315', 'bookname': 'Murder in the Cathedral', 'img': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2334286071.jpg', 'publisher': 'Clement McCallin', 'publishing': '1952', 'author': '原名:Murder in the Cathedral/乔治·赫勒林', 'description': '欧洲大教堂'} len 7
xxxx bkn 大数据译丛译者与读者互动小组 {'link': 'https://book.douban.com/subject/508070', 'bookname': '大数据译丛译者与读者互动小组', 'img': 'https://img1.doubanio.com/view/group/sqxs/public/ff4adc4140a6107.jpg', 'publisher': '约亨·希克', 'publishing': '1992', 'author': '原名:Willkommen im Dom', 'description': '德国行动起来艾滋病联盟释放权力运动的罕见记录。在1991年9月26日德国主教会议的闭幕式祈祷中法兰克福行动小组聚集在一起举行了一场壮观的抗议活动抗议天主教...'} len 7
xxxx bkn Phineas and Ferb the Movie: Across the 2nd Dimension {'link': 'https://book.douban.com/subject/6890326', 'bookname': 'Phineas and Ferb the Movie: Across the 2nd Dimension', 'img': 'https://img3.doubanio.com/view/photo/s_ratio_poster/public/p2176180893.jpg', 'score': '8.3', 'ratenum': '1179', 'publisher': '艾莉森·斯通勒', 'publishing': '2011', 'author': '原名:Phineas and Ferb the Movie: Across the 2nd Dimension/丹·波文迈尔', 'description': '故事叙述一个巧妙的契机,在泰瑞进入飞哥小佛家五周年纪念日当天,飞哥做了一个用大型鸭嘴兽羽毛球机器,而且两人就坐在里头,在阴错阳差之下撞上了背着两人驾着火箭车...'} len 9
xxxx bkn Building the Great Cathedrals {'link': 'https://book.douban.com/subject/25766508', 'bookname': 'Building the Great Cathedrals', 'img': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2158147438.jpg', 'publisher': '杰伊·桑德斯', 'publishing': '2010', 'author': '原名:Building the Great Cathedrals', 'description': '本片讲述了“八卦掌”传人女侠易水寒与中医小郎中钱成用正义战胜邪恶、用真诚孕育爱情、用美德诠释和谐的感人故事。'} len 7
xxxx bkn Salida de misa de doce del Pilar de Zaragoza {'link': 'https://book.douban.com/subject/6967938', 'bookname': 'Salida de misa de doce del Pilar de Zaragoza', 'img': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2550701347.jpg', 'publisher': '', 'publishing': '1896', 'author': '原名:Salida de misa de doce del Pilar de Zaragoza', 'description': '一群人走出萨拉戈萨的皮拉尔大教堂。'} len 7
xxxx bkn La inviolabilidad del domicilio se basa en el hombre que apa {'link': 'https://book.douban.com/subject/6124027', 'bookname': 'La inviolabilidad del domicilio se basa en el hombre que apa', 'img': 'https://img1.doubanio.com/view/subject/s/public/s4718927.jpg', 'publisher': '艾力克斯·皮培诺', 'publishing': '2011', 'author': '原名:La inviolabilidad del domicilio se basa en el hombre que apa', 'description': 'In the garden of a provincial summer house, a series of operations is carried out, involving a man, a woman and a group of individuals with certain convictions.'} len 7
xxxx bkn 大军阀与大老千 {'link': 'https://book.douban.com/subject/26862219', 'bookname': '大军阀与大老千', 'img': 'https://img3.doubanio.com/f/movie/b6dc761f5e4cf04032faa969826986efbecd54bb/pics/movie/movie_default_small.png', 'publisher': '恬妮', 'publishing': '1970', 'author': '原名:大军阀与大老千/谢君仪', 'description': '……'} len 7
中国历史风云录
xxxx bkn 中国历史风云录 {'link': 'https://book.douban.com/subject/3335643', 'bookname': '中国历史风云录', 'img': 'https://img3.doubanio.com/view/subject/s/public/s29347220.jpg', 'score': '8.0', 'ratenum': '788', 'publisher': '理想国 | 广西师范大学出版社', 'publishing': '2016', 'author': '陈舜臣/陈亚坤', 'description': '本书原名《中国五千年》,是陈舜臣的代表作。五千年华夏历史的风云变幻、世事沉浮,从远古到近代,从荣光到黯然,从分裂到统一,一个个大的时代的起落,一个个王朝的兴...'} len 9
xxxx bkn 共和国战争--新中国战争风云录 {'link': 'https://book.douban.com/subject/4906235', 'bookname': '共和国战争--新中国战争风云录', 'img': 'https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2591179524.jpg', 'score': '8.8', 'ratenum': '179', 'publisher': '道格拉斯·麦克阿瑟', 'publishing': '1996', 'author': '原名:共和国战争--新中国战争风云录', 'description': '《共和国战争》是一部历史文献系列纪录片由当时的北京五岳文化策划公司策划著名军旅作家冯精志参与撰稿于20世纪90年代前期进行录制并由中国康艺音像出版社在199...'} len 9
xxxx bkn 中国历史悬案 {'link': 'https://book.douban.com/subject/25895930', 'bookname': '中国历史悬案', 'img': 'https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2185170549.jpg', 'publisher': '', 'publishing': '2010', 'author': '原名:中国历史悬案', 'description': '《中国历史悬案》收录了中国历史上影响最大、最有研究价值和最被广泛关注的历史悬案,内容涉及国宝之谜、帝王身世、战争悬案、宫廷政变、神秘宝藏、历史奇案等。'} len 7
xxxx bkn 中国变法历史风云录 {'link': 'https://book.douban.com/subject/31107144', 'bookname': '中国变法历史风云录', 'img': 'https://img3.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif', 'author': 'class="subject-cast">商务印书馆 / 1992<', 'description': '《朝鲜历史风云录》合订本收录4位朝鲜历史名人小传和4次重大历史事件其中包括朝鲜民族英雄李舜臣近代农民革命领袖全琫准朝鲜1884年的政变爱国志士安重根著...'} len 5
xxxx bkn 经典军用飞机战史风云录 {'link': 'https://book.douban.com/subject/30668640', 'bookname': '经典军用飞机战史风云录', 'img': 'https://img3.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif', 'publisher': '四川大学', 'publishing': '2010', 'author': '范咏涛', 'description': '《美国历史风云》为“美国视野”丛书之一。《美国历史风云》把美国建国两百多年的重要历史事件中选出对美国文化的形成和发展颇具影响的事件若干编成16个单元向读者...'} len 7
xxxx bkn 近现代历史风云沙龙 {'link': 'https://book.douban.com/subject/32466', 'bookname': '近现代历史风云沙龙', 'img': 'https://img3.doubanio.com/icon/g32466-1.jpg', 'author': 'class="subject-cast">林如 周正 瞿弦 张筠英<', 'description': '《中国历史故事版》听故事,学历史。这是一套由著名历史学家专门为中小学生编写的中国历史故事读本。它以中国各朝代的著名历史人物和事件为主线,搭建了中国历史知识的...'} len 5
xxxx bkn 中国历史故事版(8CD) {'link': 'https://book.douban.com/subject/6547839', 'bookname': '中国历史故事版(8CD)', 'img': 'https://img3.doubanio.com/view/subject/s/public/s6548603.jpg', 'author': 'class="subject-cast">林如 周正 瞿弦 张筠英<', 'description': '《中国历史故事版》听故事,学历史。这是一套由著名历史学家专门为中小学生编写的中国历史故事读本。它以中国各朝代的著名历史人物和事件为主线,搭建了中国历史知识的...'} len 5
xxxx bkn 故事会历史传说6:中国历史文化名人珍闻1 {'link': 'https://book.douban.com/subject/2172579', 'bookname': '故事会历史传说6:中国历史文化名人珍闻1', 'img': 'https://img3.doubanio.com/f/music/11496305e2cd99415ec541326b236b6b8d61175c/pics/music/default_cover/lpic/music-default.gif', 'author': 'class="subject-cast">未知艺术家<', 'description': '《故事会历史传说6中国历史文化名人珍闻1》收集了在我国民间流传、传播着的大量与古代文化名人相关的民间传说和轶闻趣事它们构思新颖独特情节生动奇巧内容或慷...'} len 5
xxxx bkn 中国历史传奇故事(3CD) {'link': 'https://book.douban.com/subject/6552322', 'bookname': '中国历史传奇故事(3CD)', 'img': 'https://img3.doubanio.com/view/subject/s/public/s6556931.jpg', 'author': 'class="subject-cast">未知艺术家<', 'description': '《中国历史传奇故事》记载了从传说中的五帝时代到近现代时期上下五千年的传奇故事,一一展现中华民族传奇历史人物的光辉事迹!故事材料丰富,忠于史实,它对古代历史重...'} len 5
xxxx bkn 故事会历史传说7:中国历代文化名人珍闻2 {'link': 'https://book.douban.com/subject/2172730', 'bookname': '故事会历史传说7:中国历代文化名人珍闻2', 'img': 'https://img3.doubanio.com/f/music/11496305e2cd99415ec541326b236b6b8d61175c/pics/music/default_cover/lpic/music-default.gif', 'author': 'class="subject-cast">未知艺术家<', 'description': '《中国历史》历史如画,也如人。闻香识女人,特色读王朝!我们一起来了解一下夹缝中生存的宋朝、徘徊在汉文化的元朝、皇帝个性多姿多彩的明朝……每一个王朝,自有其与...'} len 5
xxxx bkn 中国历史 {'link': 'https://book.douban.com/subject/3727029', 'bookname': '中国历史', 'img': 'https://img3.doubanio.com/f/music/11496305e2cd99415ec541326b236b6b8d61175c/pics/music/default_cover/lpic/music-default.gif', 'publisher': '', 'publishing': '2007', 'author': '原名:Histoire du look', 'description': '衣服款式颜色的发展史。能翻墙的同学可以到IMDb把相关资料补齐。@玛娜娜'} len 7
xxxx bkn 中国古代历史风云:列国交聘(套装上下册) {'link': 'https://book.douban.com/subject/30845381', 'bookname': '中国古代历史风云:列国交聘(套装上下册)', 'img': 'https://img3.doubanio.com/f/shire/5522dd1f5b742d1e1394a17f44d590646b63871d/pics/book-default-lpic.gif', 'author': 'class="subject-cast">马永刚 / 江西人民出版社<', 'description': '中国历史风云录 【日】陈舜臣 陈亚坤(译) 桂林广西师范大学出版社2009.1 ISBN 978-7-5633-7973-6 CIP(2008)第200782号 ◇古代遗址与神话 P3中国神话的特征在于它具有高度的片段性而没有系统性。有个叫共...'} len 5
印度,漂浮的次大陆
xxxx bkn 印度,漂浮的次大陆 {'link': 'https://book.douban.com/subject/25716134', 'bookname': '印度,漂浮的次大陆', 'img': 'https://img3.doubanio.com/view/subject/s/public/s27044840.jpg', 'score': '7.5', 'ratenum': '623', 'publisher': '浙江大学出版社', 'publishing': '2013', 'author': '郭建龙', 'description': '为什么所有的人都喜欢把中国和印度做比较?'} len 9
xxxx bkn 神秘的南亚次大陆 {'link': 'https://book.douban.com/subject/1488786', 'bookname': '神秘的南亚次大陆', 'img': 'https://img1.doubanio.com/view/subject/s/public/s7616387.jpg', 'publisher': '海潮出版社', 'publishing': '2006', 'author': '白献竞,高晶编著', 'description': '印度是南亚次大陆的文明古国,是一个宗教复杂而具有多姿多彩的国家,也是一个充满奇异和神秘的大陆。'} len 7
xxxx bkn 看見南亞 {'link': 'https://book.douban.com/subject/30211892', 'bookname': '看見南亞', 'img': 'https://img1.doubanio.com/view/subject/s/public/s29757837.jpg', 'publisher': '八旗文化', 'publishing': '2018', 'author': '林汝羽', 'description': '五個國家、七個台灣人,九種觀察南亞的視角'} len 7
PythonCookbook
note_bleaf PythonCookbook第3版中文版异步图书(大卫·比斯利(DavidBeazley);布莱恩·K.琼斯 (1)
PythonCookbook
note_bleaf 印度,漂浮的次大陆 (36)
印度,漂浮的次大陆
note_bleaf 中国历史风云录(陈舜臣作品) (135)
中国历史风云录
note_bleaf 印度,漂浮的次大陆 (36)
印度,漂浮的次大陆
note_bleaf PythonCookbook第3版中文版异步图书(大卫·比斯利(DavidBeazley);布莱恩·K.琼斯 (1)
PythonCookbook
note_bleaf 印度,漂浮的次大陆 (36)
印度,漂浮的次大陆