kindle manager
This commit is contained in:
6
CLIP.md
Normal file
6
CLIP.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
|
||||||
|
--|--|--|--|--
|
||||||
|
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
|
||||||
|
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。--CG注:山寨 假货 问题
|
||||||
|
HL|庆余年(精校版)|猫腻|2020/1/19 20:00:29|园子里的护卫能掺多少人就掺多少人,我会派人盯着
|
||||||
|
HL|庆余年(精校版)|猫腻|2020/1/20 19:57:10|叶灵儿叹了口
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
137
changelog.md
137
changelog.md
@@ -1,17 +1,80 @@
|
|||||||
|
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## 1.0.0 (20200526)
|
## 1.1.1 (20200626)
|
||||||
### create
|
### feature
|
||||||
- unittest
|
- remove pandas library because it will make the package too large,
|
||||||
- abstract note/bookmark/highlight from kindle clipping
|
I write mTable calss to operate table data structure
|
||||||
- formatter output to special file
|
- 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)
|
## 1.0.1 (20200528)
|
||||||
### feature
|
### feature
|
||||||
- add search clip and some functions
|
- 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
|
- 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
|
- use defaultdict to solve obove problem, note, defaultdict only create two layer key auto
|
||||||
```
|
```
|
||||||
@@ -40,64 +103,24 @@ b['1']['2'] = {'3':1} # OK
|
|||||||
- unittest error line number is not collect, **unitest bug**?
|
- unittest error line number is not collect, **unitest bug**?
|
||||||
- **unitest testcase execution sequence is disorder, so must create test data for each case**
|
- **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
|
## 1.0.0 (20200526)
|
||||||
- change dict keys between iteration, will throw RuntimeError:
|
### create
|
||||||
dictionary changed size during iteration
|
- unittest
|
||||||
- reference - http://www.cocoachina.com/articles/89748
|
- abstract note/bookmark/highlight from kindle clipping
|
||||||
- pyQt5 reference - https://www.learnpyqt.com/
|
- formatter output to special file
|
||||||
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# feature list
|
# feature list (keep update / 20200625)
|
||||||
- [x] first abstract from kindle hard / local directory for different OS
|
- [x] first abstract from kindle hard / local directory for different OS
|
||||||
- [x] add GUI use QT
|
- [x] add GUI use QT
|
||||||
- [x] use thread to check kindle connection status
|
- [x] use thread to check kindle connection status
|
||||||
- import function:
|
- import function:
|
||||||
- [x] local
|
- [x] local
|
||||||
- [x] kindle
|
- [x] kindle
|
||||||
- [ ] duokan
|
- [x] duokan
|
||||||
- [ ] amazon
|
- [x] amazon **not test**
|
||||||
- [x] link to douban and amazon
|
- [x] link to douban and amazon
|
||||||
- export function:
|
- export function:
|
||||||
- [ ] to [evernote](https://github.com/benhorvath/kindle2evernote/blob/master/kindle2evernote.py)
|
- [ ] to [evernote](https://github.com/benhorvath/kindle2evernote/blob/master/kindle2evernote.py)
|
||||||
@@ -106,7 +129,6 @@ b['1']['2'] = {'3':1} # OK
|
|||||||
- [ ] onenote
|
- [ ] onenote
|
||||||
- [x] txt
|
- [x] txt
|
||||||
- [x] markdown
|
- [x] markdown
|
||||||
- [ ] easily copy filter notes to clipboard
|
|
||||||
- [ ] implement command line based on [argparse](https://www.jianshu.com/p/a41fbd4919f8)
|
- [ ] implement command line based on [argparse](https://www.jianshu.com/p/a41fbd4919f8)
|
||||||
- :x: mobi / epub reader
|
- :x: mobi / epub reader
|
||||||
- [ ] import .azw3/.epub/.mobi file to kindle
|
- [ ] import .azw3/.epub/.mobi file to kindle
|
||||||
@@ -121,5 +143,10 @@ b['1']['2'] = {'3':1} # OK
|
|||||||
- [ ] clean (sdr)
|
- [ ] clean (sdr)
|
||||||
- [ ] sync modify to kindle device
|
- [ ] sync modify to kindle device
|
||||||
- [ ] compare parse html info with xpath & beautisoap & regex
|
- [ ] compare parse html info with xpath & beautisoap & regex
|
||||||
|
- [x] save export markdown file name with book name(treeview's item name),<br>
|
||||||
|
such as **<<印度漂浮的次大陆.md>>**
|
||||||
|
- [x] easily copy filter notes to clipboard
|
||||||
|
- [x] add right click menu for book info treeview item, so I can copy book info by select menu, format:<br>
|
||||||
|
1| :book: | [存在主义咖啡馆](href) | 作者/出版社/出版时间/阅读时间 书评
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
cui
2
cui
@@ -1,2 +1,4 @@
|
|||||||
|
|
||||||
pyuic mainwindow.ui -o mainwindow.py --no-protection
|
pyuic mainwindow.ui -o mainwindow.py --no-protection
|
||||||
pyside2-rcc -binary kmanapp.qrc -o kmanapp_rc.py
|
pyside2-rcc -binary kmanapp.qrc -o kmanapp_rc.py
|
||||||
|
cp -fr *py *md *ico *qrc *ui ~/penv/kman/
|
||||||
|
|||||||
BIN
downimg/s29675043.jpg
Normal file
BIN
downimg/s29675043.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
179
export.md
179
export.md
@@ -1,122 +1,61 @@
|
|||||||
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
|
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
|
||||||
--|--|--|--|--
|
--|--|--|--|--
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:30:12|关于病菌的历史作用的最令人生畏的例子,来自随同哥伦布1492年的航行而开始的欧洲人对美洲的征服。虽然被那些杀人不眨眼的西班牙征服者杀死的印第安人不计其数,但凶恶的西班牙病菌杀死的印第安人却要多得多。为什么在欧洲和美洲之间这种可怕的病菌的交流是这样不对等?为什么印第安人的疾病没有大批杀死西班牙入侵者,并传回欧洲,消灭掉欧洲95%的人口?
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:12:49|这就是边际效用递减的规律,它指的是每多消耗一个单位的商品,所能带来的新增的享受在递减。例如我们吃东西的时候,食物带给我们的边际效用,通常都是递减的。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:32:56|可以从数学上把病菌的传播定义为:由每一个原发病人传染的新的受害者的数目。这个数目的大小取决于每一个受害者能够传染给新的受害者的持续时间的长短,以及这种病菌从一个受害者转移到下一个受害者的效率的高低。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 12:33:44|的是,在新闻里,我们不仅会听到抱怨贸易逆差,还会听到抱怨贸易顺差。当人们抱怨贸易逆差时,说我们国家的钱都被外国人赚走了。这时,他们没有提到,由于进口了许多外国商品,他们国家的人享受了许多优质商品。当人们抱怨贸易顺差时,他们会说,我们国家的资源都被外国人给买走了。但这时他们没有提,我们赚了不少外国人的钱,从而获取了将来进一步享受外国人提供的商品和服务的机会。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:33:12|病菌以各种稀奇古怪的方式使我们生病,如使我们得生殖器溃疡或腹泻。它这样做会得到什么样的演化利益呢?这似乎是特别令人费解而又自拆台脚的事,因为病菌杀死了它的宿主也就杀死了它自己。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:07|要成功实施价格歧视,最重要的就是要防止消费者之间对同样的产品进行转售。越是难以阻止消费者转售的产品,就越难实施价格歧视。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:33:47|我们的疾病的许多“症状”,实际上不过是某种非常聪明的病菌在改变我们的身体或行为以便使我们帮助传播病菌时所表现出来的一些方式而已。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:36|但对于那些不能转售的商品或者服务,商人就会进行大量价格歧视,比如在餐厅里喝的水和饮料,和餐厅以外的便利店比,它们的价格差距就非常大。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:43:11|这就是为什么“使我们生病”是符合病菌的利益的
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:49|人们对一种产品趋之若鹜的地区,价格就定得高一点;
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:43:17|但是,为什么病菌会演化出杀死宿主这种明显自拆台脚的策略呢?
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:43:35|价格歧视的三种方法
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:05:35|人类历史上最大的一次流行病是在第一次世界大战结束时杀死2100万人的流行性感冒。黑死病(腺鼠疫)在1346年到1352年间杀死了欧洲四分之一的人口,在有些城市里死亡人数高达70%。19世纪80年代初,当加拿大太平洋铁路修经萨斯喀彻温时,该省以前很少接触过白人及其病菌的印第安人死于肺结核的人数每年竟达到惊人的9%。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:44:23|实名制的飞机票,也是大量实施价格歧视的典型案例。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:08:40|但由于这种病菌除了在活人体内是不可能生存的,所以人死了这种病也就消失了,直到又一批后代达到易受感染的年代——直到一个受到感染的外来人使一场流行病重新开始。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:47:00|如果演唱会门票价格过低,歌迷就不得不展开价格以外的竞争,那就是排队等候。什么人更愿意排队等候呢?是那些时间成本比较低的人,通常是年轻人。这样做的结果,是能够把那些更年轻、更热情、更奔放的歌迷吸引到演唱会现场,这样演唱会的现场就会更加火爆。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:38:20|在第一年,黏液病毒在受到感染的兔子中造成了令人满意的(对澳大利亚农民来说)99.8%的死亡率。令这些农民感到失望的是,第二年兔子的死亡率下降到90%,最后下降到25%,使得要在澳大利亚完全消灭兔子的希望落空了
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:47:06|还有一种非常有趣的价格歧视:歌星在发售演唱会门票时,有时会刻意把门票的价格降低。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:40:17|一提起梅毒,我们立刻会联想到两种情况:生殖器溃疡和病情发展的十分缓慢,许多得不到治疗的患者要过好多年才会死去。然而,当梅毒于1495年首次在欧洲明确见诸记录时,它的脓疱通常从头部到膝部遍布全身,使脸上的肉一块块脱落,不消几个月就使人一命呜呼。到1546年,梅毒已演化成具有我们今天所熟悉的那些症状的疾病。显然,同多发性黏液瘤病一样,为使患者活得长些而进行演化的那些梅毒螺旋体因此就能够把它们的螺旋体后代传染给更多的患者
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:22:51|弗里德曼强调的就是我们这里所说的优质的第二层含义。对消费者来说,并不是质量越高越好,也不是价格越低越好,而是价格和品质要相当,成本和收益要相当。这才是他们最看中的品质。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:42:57|1519年科尔特斯率领600个西班牙人在墨西哥海岸登陆,去征服拥有好几百万人口的勇猛好战的阿兹特克帝国
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:14|好几年前,一辆校车发生意外,造成了严重的伤亡,社会各界人士出来说,应该制定校车的安全标准。我的一个朋友也参与了这一标准的制定,但后来他放弃了。因为他发现,如果不计成本凭空制定校车的安全标准,最后校车的标准快要接近坦克车的标准了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:46:47|当我们美国人想到存在于1492年的新大陆人口最多的社会时,出现在我们心头的往往只是阿兹特克人和印加人的那些社会。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:49:23|到1618年,墨西哥原来2000万左右的人口急剧减少到160万左右。
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:17|在我年轻的时候,美国小学生所受到的教育是:北美洲本来只有大约100万印第安人居住。把人数说得这样少,对于为白人的征服行为辩解是有用的,因为他们所征服的只不过是一个可以认为几乎是空无所有的大陆。然而,考古发掘和对最早的欧洲探险者所留下的关于我们海岸地区的描写的仔细研究现已表明,印第安人原来的人数在2000万左右。就整个新大陆来说,据估计在哥伦布来到后的一两个世纪中,印第安人口减少了95%。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:29:41|为了克服信息不对称,建立互信,人类社会构想出了各种各样有趣的解决方案,从重复交易到第三方背书,从质保、延保,再到收益共享。此外,还有三种非常接近的建立信任的办法:付出沉没成本、给出人质或者给出抵押。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:50|主要的杀手是旧大陆来的病菌。印第安人以前从来没有接触过这些病菌,因此对它们既没有免疫能力,也没有遗传抵抗能力。天花、麻疹、流行性感冒和斑疹伤寒争先恐后地要做杀手的头把交椅
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:32:11|银行是一个很难取信于人的行业。因为客户把钱交给银行,万一哪天银行把钱卷走了,客户就会血本无归。为了建立信任,好银行往往把总部设在城市最繁华的地段,而且不是租的,是买的,或者是自己盖的。这能让人相信,它们打算一直在这里,不会搬走,它们要做50年、100年。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:58|好像这些病还嫌不够似的,紧随其后的还有白喉、疟疾、流行性腮腺炎、百日咳、瘟疫、肺结核和黄热病
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:33:44|越是愿意自残的人,越是容易取信于人,所以中国人才有所谓“感情深一口闷”之类酒桌上的谚语。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:54:00|梅毒、淋病、肺结核和流行性感冒于1779年随库克船长[5]到来,接着于1804年又发生了一场斑疹伤寒大流行以及后来的许多“较小的”流行病,把夏威夷的人口从1779年的50万左右减少到1853年的84000人。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:02|可以推测,当我们这个社会信任机制建立得越来越健全,人与人之间可以通过其他各种各样的方式来建立信任时,通过喝酒等自残的方式来建立信任的办法就会用得越来越少,而喝酒就变得纯粹是娱乐了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 18:47:19|正如人类学家克劳德·莱维-斯特劳斯所说的那样,古代文字的主要功能是“方便对别人的奴役”。非专职人员个人使用文字只是很久以后的事,因为那时书写系统变得比较简单同时也更富于表现力。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:45|跟付出沉没成本相似的另外一个办法,是给出人质。一个大国的国王跟小国的国王说:“你放心吧,我不会侵略你,你们不要搞军备、不要武装起来了。”小国相信吗?小国不相信。但如果大国国王把自己的女儿嫁到小国去做人质,小国就比较容易相信了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 20:59:22|文字只在新月沃地、墨西哥、可能还有中国独立出现,完全是因为这几个地方是粮食生产在它们各自的半球范围内出现的最早地区
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:51|当然,除了交出人质以外,付出抵押也是同样的办法。如果既没有人质,又没有抵押,那该怎么办?还有一些比较便宜的办法,就是互相分享一点秘密,互相交个底,说点自己以前见不得光的事情,那也算是一种无形的抵押。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:36:29|问题似乎在他们的社会。否则又怎样来解释澳大利亚东北部的土著为什么没有采用弓箭?而他们见过与他们进行贸易的托雷斯海峡的岛上居民在使用弓箭。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:38:28|厂商用实际行动告诉消费者,我已经花掉了一个亿,将来只有一个办法才能把这一个亿收回来:那就是在未来若干年里,持续地卖出10亿瓶水,每瓶水都保证质量,每瓶水都多收一毛钱。在出售这10亿瓶水的销售期内,如果被发现质量问题,消费者就会减少甚至停止购买,厂商前面付出的那一个亿就打水漂了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:38:01|1942年,当第二次世界大战仍在进行时,美国政府制定了曼哈顿计划,其显而易见的目的就是抢在纳粹之前发明出为制造原子弹所需要的技术。3年后,这个计划成功了,共花去20亿美元(相当于今天的200多亿美元)
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:50:02|这时,有意思的结论就来了。从斯密和李嘉图开始,我们就知道分工能够提高生产效率,分工越细,人们就越能够集中生产自己擅长的东西,然后进行交换,这样人们的整体福利就能得以提高。 但是我们这里讲品质管理时是反其道而行的,我们要做的是垂直整合:加工厂不仅要有自己的奶牛,还要有自己的饲料来源。从种草、割草、送草,到养牛、挤奶、检验、加工,全是一家企业内部完成的工序。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:43:39|特利布·戴姆勒得以在一辆自行车上安装了一台发动机从而制造了第一辆摩托车
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:58:49|如果半成品或者成品的检验成本比较低,换手的次数就可以多些,就可以进行更细的分工;相反,如果品质检验的成本比较高,就需要更多地进行垂直整合,减少中间环节。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:45:48|从专利法律师观点看,最佳的发明就是全无先例的发明,就像雅典娜整个地从宙斯的前额跳出来一样[3]。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:00:09|损失掉一部分品牌的多样性,换来的是稳定性和可靠性,这么做可能是现代社会提高产品质量可靠性的一种趋势。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:50:58|从来就没有这样的人。所有公认的著名发明家都有一些有本领的前人和后人,而且他们是在社会有可能使用他们的成果的时候对原来的发明作出改进的
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:18:55|他的理由是,在这种产品发生意外对顾客造成伤害的案件中,要让顾客提出证据证明厂商存在疏忽是非常困难的,顾客必须是专家才有可能提出证据。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 23:58:58|在现代的西方,保护发明者的所有权的专利权和其他财产法奖励发明,而在现代的中国,缺乏这种保护妨碍了发明
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:19:26|他认为只要产品出现明显的质量问题,无需证据就可以判企业疏忽,企业应该对自己的产品负有严格的责任,顾客不需要提供关于疏忽的证据就能够获得赔偿。这种无条件的责任叫“严格责任”
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 23:59:42|对各种观点和异端观点的宽容促进了创新,而浓厚的传统观点(如中国强调中国古代的经典)则扼杀了创新
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:20:16|这样的做法有其合理之处。因为一件产品销售给千家万户,每一位顾客都有不同的使用场景,如果用玻璃瓶装的可口可乐,在一些比较正常的状态下也会发生爆炸,那就要求每一位顾客都成为专家,对气温、压强、搬动的程序等,都有比较深入的了解,否则意外就难以避免。这样,整个社会避免意外发生的成本也太高了。反过来,只要可口可乐公司能够把瓶子做得更结实一点,就能够一劳永逸地解决问题,所有顾客都不需要担心了。因此让商家承担严格责任,是有必要的。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:01:16|在整个历史上,战争常常是促进技术革新的主要因素
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:21:53|如果他的责任还是太大,既没有办法通过买保险来分摊,也没有办法通过提高商品的价格来分摊,那么他就会面临两个选择:一个是铤而走险,继续生产,一旦出了事就破产、坐牢;如果他小心谨慎一点,会做第二个选择,那就是不再生产,完全退出市场。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:02:15|强有力的集中统一的政府在19世纪后期的德国和日本对技术起了推动作用,而在公元1500年后的中国则对技术起了抑制作用。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:22:08|但是如果责任人事实上是没有能力解决问题、避免意外的,他会怎么办?他的一个合理做法是去买保险,将风险分摊给其他人。与此同时,他也可以提高产品的售价,把自己所要承担的风险重新分摊给所有顾客,让所有顾客一起来承担。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:04:24|许多北欧人认为,在气候条件严峻的地方,技术能够繁荣发展,因为在那里没有技术就不能生存,而在温和的气候下,技术则会枯萎凋零,因为那里不需要穿衣,而香蕉大概也会从树上掉下来。一种相反的观点则认为,有利的环境使人们用不着为生存进行不懈的斗争,而可以一门心思地去从事创新活动。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:24:00|大家会说提高食品和药品的质量总是好的,但经济学的分析告诉我们,凡事都有成本。与销售利润没有任何牵连的审批官员,他们确实不会像商家那样追求利润,但他们追求产品的极致安全,而追求产品的极致安全,本身是有成本的。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:07:53|现在,中东的伊斯兰社会相对而言比较保守,并不居于技术的最前列。但同一地区的中世纪伊斯兰教社会在技术上却是先进的,是能够接受新事物的。它的识字率比同时代的欧洲高得多;它吸收了古典的希腊文明的遗产,以致许多古典的希腊书籍只是通过阿拉伯文的译本才为我们所知;它发明了或精心制作了风车、用潮水推动的碾磨、三角学和大三角帆;它在冶金术、机械工程、化学工程和灌溉方法等方面取得了重大的进步;它采用了中国的纸和火药,又把它们传到欧洲。在中世纪,技术绝大多数是从伊斯兰世界流向欧洲,而不是像今天那样从欧洲流向伊斯兰世界。只是在公元1500年左右以后,技术的最终流向才开始逆转。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:25:45|美国食品药品监督管理局(Food and Drug Administration, FDA)为例
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:14:09|一个著名的例子是日本放弃枪支。火器在公元1543年到达日本,当时有两个葡萄牙人携带火绳枪(原始的枪)乘坐一艘中国货船抵达。日本人对这种新式武器印象很深,于是就开始在本地制造,从而大大地改进了枪支制造技术,到公元1600年已比世界上任何其他国家拥有更多更好的枪支。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:26:02|1962年,当时的美国总统约翰·肯尼迪还专门给FDA的局长颁发了一枚勋章,以表彰FDA的贡献。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:15:19|只是因为日本是一个人口众多的孤立的海岛,它才没有因为拒绝这种具有巨大作用的新军事技术而受到惩罚。1853年,美国海军准将佩里率领装备有许多大炮的舰队访问日本,使日本相信它有必要恢复枪支的制造,直到这时,日本因孤立而得到安全的状况才宣告结束。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:43:33|奥地利学派认为,要避免经济周期最根本的办法就是政府抑制住乱印钞票的冲动。关于如何抑制政府乱印钞票,奥地利学派提出了两个基本的主张:第一是要回到金本位,第二是实施自由发钞制度。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:15:46|日本拒绝枪支和中国抛弃远洋船只(以及抛弃机械钟和水力驱动纺纱机),是历史上孤立或半孤立社会技术倒退的著名例子
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:46:00|超超发 —>利率下降—>投资增加—>信心增加—>虚假繁荣
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:40:57|盗贼统治者和英明政治家的区别,强盗贵族和公益赞助人的区别,只是程度不同而已:这只是一个从生产者那里榨取来的财物有多少被上层人物留下来的问题,是平民对把重新分配的财物用于公共目的喜欢到什么程度的问题。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:21:10|没有哪个社会能忍受日益恶化的通货膨胀,政府迟早会采取行动,收缩货币的发行量。这时,当初人们制订的那些长远规划就找不到足够的资金来维持,资金链断裂、泡沫破灭了。这就是奥地利学派理解的经济周期发生的过程。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:42:32|为什么平民会容忍把他们艰苦劳动的成果奉送给盗贼统治者?从柏拉图到马克思的所有政治理论家都提出过这个问题,在现代的每一次选举中选民们又重新提出了这个问题
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:46:37|凯恩斯主义学派的基本主张是,政府应该逆经济周期而行,人们都在消费时政府就少采取动作;人们都不消费时,政府就应该积极消费、积极投资、积极生产。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:42:49|得不到公众支持的盗贼统治者有被推翻的危险,不是被受压迫的平民所推翻,就是被暴发的想要取而代之的盗贼统治者所推翻,这些新贵们用许诺为被窃取的果实提供更多服务的办法来谋求公众的支持。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:46:59|这里要说明的是,凯恩斯本人其实是深信市场自行调节功能的,他指出一旦社会回到正常的状态,政府就应该停止干预,让市场机制自己发挥作用。换句话说凯恩斯主义者关心的是要解决短期内出现的问题,尤其是短期内出现的失业问题,这时创造需求、制造就业机会就是政府的责任
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:43:25|从古至今的盗贼统治者混合使用了4种办法:
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:48:31|货币政策是通过发钞的办法影响人们的预期,使得就业得以增加。当然发钞会引起通货膨胀,但早期的凯恩斯主义者相信在通货膨胀和失业率之间存在着替代关系,多发钞失业率就会降低,但后来发现这一招不管用了,市场出现了通货膨胀率和失业率同时高涨——滞胀——的情况。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:46:10|盗贼统治者为了得到公众支持而使用的最后一个方法,是制造一种为盗贼统治辩护的意识形态或宗教
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:50:33|超发 通胀 刺激经济 提高就业 . 滞涨破坏这个逻辑
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:47:58|解除平民的武装,同时武装上层掌权人物。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:59:05|把今天的责任推给将来的人,这么做可行吗?这个国家长远该怎么办?凯恩斯的经典回答是:“从长远而言,我们都将一命呜呼。”他的意思是,情况很特殊,这次不一样,管不了那么远的事了。当然,人们随时都可以把“这次不一样”作为违反原则的理由
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:48:05|用通行的方法把得到的财物的很大一部分再分配给群众来博取他们的欢心
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:03:09|弗里德曼的名言是:“通货膨胀到处以及永远都是一种货币现象。”(Inflation is everywhere and always a monetary phenomenon.)
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:48:39|共同的意识形态或宗教有助于解决没有亲属关系的人们应如何共处而不致互相残杀这个问题——办法就是为他们规定一种不是以亲属关系为基础的约束。第二个好处是,它使人们产生了一种为别人而牺牲自己生命的动机,而不是产生利己之心
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:10:04|货市主义者认为,当新增的货币流动到社会每一个角落以后,除了物价水平上涨,其他任何事情都不会改变。长期而言,政府通过货币政策来干预经济的努力是无效的。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:58:37|所有这些例子都表明,战争或战争威胁在大多数(即使不是全部)社会合并中起了关键的作用。但是战争,甚至仅仅是族群间的战争,一直是人类社会的一个恒久不变的事实
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:17:31|既然通货膨胀无论何时何地都是一种货币现象,那怎样才能抑制政府乱发钞票的冲动呢? 奥地利学派认为,要回到金本位或者要实施自由发钞制度。而弗里德曼提出的办法是要盯死货币发行增量。哪个政府都会忍不住乱发钞票,所以解决的办法是把货币增长比例写到宪法里去,谁也改不了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:08:42|,为什么澳大利亚土著仍然不知金属工具为何物,而仍然生活在石器时代?
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:23:36|如果社会上的失业率高涨,政府就去制造通货膨胀来对付失业率,那么第一次、第二次人们会真的以为经济变好了,开始增加投资多雇人,但试过一两次以后,人们就会形成更明确的预期,知道这只不过是政府对付眼前问题的花招,他们再做投资和雇人决策时,就会格外小心。社会上这样的人多了,政府的政策就会失效。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:46:03|全世界6000种语言中有1000种挤在一个只比得克萨斯州稍大一点的地区里,分成几十个语族以及一些就像英语和汉语那样不同的互相独立的语言
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:23:53|同样的道理,当政府看到社会上消费不足,发钱让人们消费时,人们就会逐渐明白,“政府发的钱不是白给的,迟早会通过增加税收的方式要回去,所以哪怕是拿了政府的钱也不能乱花”。这时政府的政策又失效了。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:46:07|新几内亚是世界上语言最集中的地方
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:24:14|政府的政策赖以形成的经济模型,其中的参数是会随着人们预期的改变而改变的。当人们一旦形成预期,那些模型就不起作用了
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:30:58|贸易和战争交替进行,而战争的目的则是割取敌人的首级做战利品和把女人捉来做老婆。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:26:09|但真实经济周期理论却认为,经济学家所说的外生变量、冲击,其实是无所不在的,发生意外的冲击是我们真实生活里的常态。 干旱、地震、飓风、洪水,当然是一种冲击;新的科技发明、新的技术创新、新的市场营销手段、新的支付和结算方式,也是一种冲击;新的观念、风尚、潮流,也是冲击。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:34:45|欧洲移民的人数始终很少,今天新几内亚的人口仍然以新几内亚人为主。这同澳大利亚、美洲和南非的情况形成了鲜明的对比,因为在那些地方,欧洲人的殖民地数量多、时间久,在广大地区内取代了原来的土著人口。为什么新几内亚却不同呢?
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:27:55|如果在这以前,政府觉得出现了闲置的劳动力、出现了失业,应该管一管,从而刻意制造一些本来市场不需要的岗位让人们去做,让多余的劳动力去填充,这实际上是造成了浪费,妨碍了人们去寻找真正有价值的工作机会。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:34:58|在这些未能实现的对低地地区殖民的计划中,最雄心勃勃的计划是法国侯爵德雷伊于1880年左右在附近的新爱尔兰岛组织的,结果1000个殖民者在不到3年的时间里死掉了930人
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:28:55|生活中到处都是冲击,要应付这些冲击,人们的反应不是即时的,而是滞后的,不是全面的,而是渐进的。这本身就是合理的。额外的干预不会解决问题,而只会产生新的问题。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:49:56|欧洲人的拓殖用两种办法减少了土著的人数。一个办法就是开枪把他们打死,在19世纪和18世纪晚些时候,欧洲人认为这是一种可以接受的选择,到20世纪30年代他们进入新几内亚高原地区时,他们就很少这样考虑了。最后一次大规模的屠杀于1928年发生在艾利斯斯普林斯,共杀死了31个土著。另一个办法就是欧洲人引进的病菌,对这些病菌土著居民还没有机会获得免疫力或形成自然的抵抗力。1778年,第一批欧洲移民到达悉尼,不到一年,死于流行病的土著居民的尸体便随处可见。有案可查的主要的致命疾病有天花、流行性感冒、麻疹、伤寒、斑疹伤寒、水痘、百日咳、肺结核和梅毒。 在所有适于欧洲人发展粮食生产的地区,独立的土著社会就被用这两种办法消灭了。唯一的或多或少完好无损地幸存下来的社会,是对欧洲人无用的澳大利亚北部和西部地区的社会。在欧洲人殖民的一个世纪内,有4万年历史的土著传统基本上被消灭殆尽。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:29:16|一个人得了病,去住院是正常的,住院当然是劳动力的一种浪费,但产生这种浪费的根源——得病——早就已经发生了,再阻止这个人去住院是于事无补的。所以不让那些本来应该失业的人失业,那也是错的,错上加错并不能变成对。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 13:14:53|“中国戎夷,五方之民,皆有性也,不可推移。东方曰夷,被发文身,有不火食者矣。”这位周朝的作者接着又把南方、西方和北方的原始部落说成是沉溺于同样野蛮的习俗:“南方曰蛮,雕题交趾,有不火食者矣。西方曰戎,被发衣皮,有不粒食者矣。北方曰狄,衣羽毛穴居,有不粒食者矣。”
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:32:49|凯恩斯不是职业经济学家,而是英国上流社会的名人、记者、政府官员和投资家。他在1936年出版了《就业、利息和货币通论》,从此开创了宏观经济学这个学术分支。这本书到今天,已经有80多年的历史了,但是你到美国的书店去看,不管书店有多大,不管经济学的书架有多小,里面一定还放着凯恩斯的这本《就业、利息和货币通论》
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:04:32|事实上,正是由于欧洲是分裂的,哥伦布才成功地第五次在几百个王公贵族中说服一个来赞助他的航海事业。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:33:57|凯恩斯的直觉非常准,他看到了这一点,于是就用他独特的语言来描述这种现象。在他的著作里,这种难以名状、难以刻画、难以衡量的不确定性被称之为“动物精神”。他说正是由于这些莫名其妙的原因,人们的需求忽然下降了,经济不发展了,而解决问题的办法就是要鼓励人们去消费
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:04:41|哥伦布曾请求国王派船让他向西航行探险。他的请求被国王拒绝了,于是他就求助于梅迪纳-塞多尼亚公爵,也遭到了拒绝,接着他又求助于梅迪纳-塞利伯爵,依然遭到拒绝,最后他又求助于西班牙的国王和王后,他们拒绝了他的第一次请求,但后来在他再次提出请求时总算同意了。如果欧洲在这头3个统治者中任何一个的统治下统一起来,它对美洲的殖民也许一开始就失败了。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:34:11|当人们面对不确定性的冲击,当他们发现用精准的数学根本无力招架、无法应对的时候,他们就只能诉诸直觉、比喻、经验、自信以及勇气。这是宏观经济学真正深不可测的原因。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:02|对于欧洲的大炮、电灯照明、印刷术、小型火器和无数的其他发明,情况也是如此:每一项发明在欧洲的一些地方由于人们的习性起先或者被人忽视,或者遭人反对,但一旦某个地区采用了它,它最后总能传播到欧洲的其余地区。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:37:08|从长期看,通货膨胀和失业之间并不存在稳定的替代关系,也就是说不可能通过制造通货膨胀来降低失业率。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:09|欧洲分裂所产生的这些结果与中国统一所产生的结果形成了鲜明的对比
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:37:23|从长期看,货币增长率决定了通货膨胀率,滥印钞票迟早会造成通货膨胀
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:22|除了作出停止海外航行的决定外,中国的朝廷还作出停止其他一些活动的决定:放弃开发一种精巧的水力驱动的纺纱机,在14世纪从一场产业革命的边缘退了回来,在制造机械钟方面领先世界后又把它拆毁或几乎完全破坏了,以及在15世纪晚期以后不再发展机械装置和一般技术。统一的这些潜在的有害影响在现代中国又死灰复燃,特别是20世纪60年代和70年代“文化大革命”中的那种狂热,当时一个或几个领导人的决定就把全国的学校系统关闭了5年之久。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:25:39|到了1992年,联邦储备局波士顿分行发布了一份所谓的科学统计报告,声称经过严密的计算,美国商业银行肯定是对弱势群体进行了歧视。 实际上,商业银行迫于竞争,并不会随便歧视弱势群体。如果真的还得起房贷,而银行不给贷款,这岂不是银行的损失?但是大众并不相信。这份报告出来以后,整个社会掀起了一场要求对弱势群体发放更多贷款的运动。
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:06:19|欧洲内部的分歧今天在继续挫败甚至是想要通过欧洲经济共同体(EEC)来实现欧洲统一的并不过分的企图,这就表明欧洲对分裂的根深蒂固的执著。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:26:48|成本是放弃了的最大代价
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:06:39|中国自有文字以来就一直只有一种书写系统,长期以来只有一种占支配地位的语言,以及2000年来牢固的文化统一
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:27:16|沉没成本不是成本
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:09:09|我用本书的很大篇幅着重讨论了在没有难以克服的障碍的情况下技术的传播问题。但中国在地理上的四通八达最后却成了一个不利条件,某个专制君主的一个决定就能使改革创新半途而废,而且不止一次地这样做了。相比之下,欧洲在地理上的分割形成了几十个或几百个独立的、相互竞争的小国和发明创造的中心。如果某个国家没有去追求某种改革创新,另一个国家会去那样做的,从而迫使邻国也这样去做,否则就会被征服或在经济上处于落后地位。欧洲的地理障碍足以妨碍政治上的统一,但还不足以使技术和思想的传播停止下来。欧洲还从来没有哪一个专制君王能够像在中国那样切断整个欧洲的创造源泉。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:27:28|一块地不仅可以用来建采石场或者建住宅小区,它还有好多其他用途:A、B、C、D、E……当一个资源有若干个选项时,被选中的那个选项,它的成本就是所有放弃了的选项当中价值最高的那个。简言之,成本就是放弃了的最大代价。(Cost is the best opportunity foregone.)
|
HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:11:53|新月沃地和中国的历史还为现代世界留下了一个有益的教训:环境改变了,过去是第一并不能保证将来也是第一。人们甚至会怀疑,本书从头到尾所运用的地理学推论在现代世界上是否终于变得毫不相干,因为思想可以在因特网上立即向四处传播,而货物照例可以一下子从一个洲空运到另一个洲。看来,对全世界各民族之间的竞争已实行了一些全新的规则,结果,像朝鲜、马来西亚,尤其是日本这些新的力量出现了。 然而,仔细想来,我们发现,这些所谓的新规则不过是旧规则的改头换面而已。不错,1947年美国东部贝尔实验室发明的晶体管,跃进8000英里到日本去开创了电子工业——但它却没有跃进得近一些到扎伊尔或巴拉圭去建立新的工业。一跃而成为新兴力量的国家,仍然是几千年前就已被吸收进旧有的以粮食生产为基础的最高权力中心的那些国家,要不就是由来自这些中心的民族重新殖民的那些国家。与扎伊尔或巴拉圭不同,日本和其他新兴力量之所以能够迅速利用晶体管,是因为它们的国民已在文字、金属机械和中央集权的政府方面有了悠久的历史。世界上两个最早的粮食生产中心——新月沃地和中国仍然支配着现代世界,或者是通过它们的一脉相承的国家(现代中国),或者是通过位于很早就受到这两个中心影响的邻近地区内的一些国家(日本、朝鲜、马来西亚和欧洲),或者是通过由它们的海外移民重新殖民或统治的那些国家(美国、澳大利亚、巴西)。撒哈拉沙漠以南的非洲人、澳大利亚土著和美洲印第安人支配世界的前景仍然显得黯淡无光。公元前8000年时的历史进程之手仍然在紧紧抓住我们。
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:28:33|沉没成本,就是指那些已经发生但不可收回的支出。当我们没办法再收回、没办法再放弃时,就不存在成本。凡是提到成本,我们一定是向前(未来)看,而不是向后(过去)看的。所以,沉没成本不是成本。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:19|负面的感受不是成本
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:28|比如我们要在自家院子里修一个游泳池,修游泳池的过程,有许多负面感受:辛苦、劳累、一段时间的脏乱差等,但这些都不是修游泳池的成本,因为没有放弃什么东西。修了游泳池,这个地方就不能搭帐篷,那个放弃了的帐篷才是我们修游泳池的成本。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:48|你的成本由别人决定
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:31:23|如果有人愿意出2万元钱租这个铺位,那么坚持卖茶叶蛋的成本就是2万元;如果有人愿意出3万元,那么成本就是3万元,跟这房子是谁的没有关系。坚持卖茶叶蛋的成本,只跟一个因素有关系,那就是放弃了的最大收入。 你
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:07|一个青年,被征去当兵以后,就不能从事他原来的职业了。这时虽然多了一个廉价的士兵,但可能少了一位化学家、一个小提琴手,或是一位企业家。总的来说,义务兵制的成本是非常高的,因为它放弃的代价是不可估量的。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:21|义务征兵时,政府付出的货币成本确实比较低,但是他没有看到另外一个重要成本,就是放弃了的最大代价。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:37|给士兵发薪水比免费征兵更便宜
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:35:21|经济学家米尔顿·弗里德曼曾经给美国政府提过不少建议,大多数都没被接受。但是这一条,建议将征兵制改为志愿兵制,就被美国政府接受了。这对于减少美国的国防总成本,提高征兵效率,提高兵员质量,有极大的帮助。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:36:23|在大多数情况下,中间商在帮助我们减少总成本,而不是增加总成本,而中间商之间的竞争,会使物流的总成本降到最低。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:40:39|亏损会降低资源未来使用的成本
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:40:45|盈利提高了资源未来使用的成本
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:41:16|无论是盈利还是亏损,在经济学里,都是意外发生的。每当发生意外,我们就重新调整资源的未来估值,而资源使用的成本,就要按照这个新的估值来计算。所以,一旦出现盈利,资源的使用成本就会提高;一旦出现亏损,资源的使用成本就会下降。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:41:57|供需关系决定商品价格,商品价格决定资源成本
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:42:24|把钱赚到手以后,再把赚到的钱倒过来归结到前面每一个环节的生产要素里面去,从而给这些生产要素定价,决定它们的价值。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:45:11|政府无论是免费把土地送出去,还是高价拍卖土地,对最终的房价都不会造成影响。
|
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:46:51|不对 供需决定价格 土地供给充裕意味着房子供给充裕 怎么不导致房价降低?
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:47:08|经济学里有一个非常重要的概念,叫“租”。所谓租,就是对资产的付费
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:47:18|只要能够带来收入的资源都叫资产,而对资产的付费就叫租。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:48:31|他当歌星的收入减10万元,减20万元,减500万元,减900万元,他都照样当歌星。这990万元是他白赚的,这就叫租。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:49:09|赚多赚少都是白得的,这就叫租。当然,有时候我们也把它叫“垄断租”,因为它是在数量管制的保护之下从事的一种垄断经营。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:51:04|只要能够带来收入的资源都叫资产,而对资产的付费就叫租
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:53:40|科斯的看法则别具一格,他说所有的伤害都是相互的,我们得用新的眼光来看待这些案例:不是一方在伤害另外一方,而是双方为了不同的用途,在争夺相同的稀缺的资源:牛跟小麦争的是那块地,如果让牛吃小麦,那牛就伤害了小麦;但如果禁止牛吃小麦,小麦就伤害了牛。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:54:09|关于科斯理论的争论,法律经济学者、芝加哥大学法学院教授理查德·爱泼斯坦(Richard Epstein)曾提出过一个观点。他说,如果争夺资源的双方是同一个人,那会发生什么情况?以这样的角度来重新审视前面提到的案子,可能就会豁然开朗了。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:54:15|例如牛跟小麦之争,假设牛跟小麦同属一人,这时,牛能不能吃小麦,就取决于牛肉能卖多少钱,小麦能卖多少钱。如果小麦价格高,牛肯定不能随便吃小麦;但如果牛肉价格足够高,牛当然可以吃小麦,不仅可以吃小麦,还要给它听莫扎特的音乐,给它按摩呢。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:55:16|谁避免意外成本最低,谁的责任就最大
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:55:46|这个想法非常重要。正是基于这个想法,科斯的意思是说,火星烧着了亚麻,但是责任可能在农夫,虽然农夫并未招惹铁路公司。谁付出的成本更低,谁就应该承担更大的责任。那既然农夫避免意外所要付出的成本,比铁路公司避免意外所要付出的成本低得多,那挪开亚麻的责任,就要落到农夫身上了
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:57:03|科斯定律最流行的版本是:在交易费用为零或足够低的情况下,不管资源最初的主人是谁,资源都同样会流到价值最高的用途上去。用大白话来说,就是“谁用得好就归谁”。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:57:23|钻石归矿工还是白富美
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:59:13|这就引发了一场关于狼的争论。最后人们从科斯定律的角度找到了解决办法,那就是养牲口的人,如果能证明自己的牲口被狼咬死了,那么养狼的人就需要向他提供一定数目的赔偿。从此,养狼的人就知道养狼是有代价的,他们必须把狼群的数目控制在一个合理的范围内,我们通常把这个价叫作“科斯对价”(Coasian payment)。就这样,一个是否应该养狼的问题,一个非黑即白的问题,就转变为应该养多少狼、谁来监控、谁来负责的问题,也就是一个关于如何分配和使用资源的问题。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:01:09|我们说科斯定律最流行的一个版本是:在交易费用为零或足够低的情况下,不管产权归谁,资源都会落到最有价值的用途上。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:02:42|但现实生活中交易费用很高,很多资源是没有办法落到使用价值更高的人手里的。因此,初始的产权分配、制度法规、风俗习惯,乃至道德规范,就都变得很重要了。我们可以把社会上通行的制度、习俗和道德规范,都看成为了减少重复商议的成本而逐渐固定下来的解决纠纷的办法。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:03:07|而这就是所有的制度、风俗、习惯,以至政府、法院存在的根本理由——对资源、责任、权利进行初始的界定。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:04:36|另外一个问题就是不合作的所有者,也就是所谓的“钉子户”。根据《强制销售条例》,只要不可分割的物业的90%以上的所有者同意出售,那么剩下的10%的业主,就必须出售他的土地和相应的物业。2010年,这个条例又做了修改,比例从90%下降到80%。当然,里面还加了一条,要进行这种征地,必须经过特区行政长官的批准。 到底是90%合理还是80%更合理呢?我们不可能有一个科学的界定。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:07:10|这是我们寻求合作解,“不讲理,只讲数”的一个很好的例子。我们应当充分领会科斯定律的精神,在现实生活的各种冲突中积极地寻找合作解。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:25:11|当我们把口袋里有限的钱,按“边际效用等于边际成本”的原则,分别用来购买不同的产品时,我们从这些不同的产品那里获得的总收益,就会达到最大。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:33:40|我们怎么能知道不存在“价格上升需求量也上升”的例子呢?的确有过一些记载,但它们几乎全都可以理解为价格以外的某些因素发生了变化
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:37:31|一件商品是奢侈品还是必需品,完全取决于价格
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:37:51|需求曲线上的弹性处处不等,价格越高弹性越大
|
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:39:17|古董价格 vs 水电煤
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:39:47|掌握了商品的需求弹性会随着商品价格的变化而变化的规律,我们就容易明白一个道理:任何商品都既可以是奢侈品,也可以是必需品,这完全取决于价格的高低。当商品足够便宜的时候,它自然就成为必需品;当它贵到一定程度,它就自然变成奢侈品。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:40:16|例如,水到底是奢侈品还是必需品?一般人会说,水当然是必需品。但想想看,在水资源特别匮乏的地方,人们使用水的数量,跟水资源非常丰富的地方,是完全不一样的。水资源特别匮乏时,人们不会随便浇花、洗车、洗澡。少量的水会先洗脸再洗脚,洗完脚又刷鞋,刷完鞋还要拿来冲厕所。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:42:12|这种变化趋势就是需求第二定律所说的:随着时间的推移,需求对价格的弹性会变得越来越高,也就是说替代品将会越来越多、应变的空间会越来越大。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:45:02|这就是需求第三定律的含义:每当消费者必须支付一笔附加费时,高品质的产品相对低品质的产品就变得便宜了,这笔附加费越高,高品质产品相对就越便宜。正因为这样,我们也把这个定律叫作“好东西运到远方去定律”。
|
|
||||||
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:46:14|包装费用
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:48:56|大家都批评发国难财的行为。但我们想想,国难是那个发国难财的人造成的吗?如果不是,那么借机发财的人,其实就只是给别人多提供了一个选择罢了。发国难财的人可能确实利用了别人,但是这种利用,对另一方来说也是有好处的。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:49:25|生活中乘人之危来利用他人的现实,其实很多。比如,医生不就利用了病人生病吗?老师不就利用了学生无知吗?但是医生对病人来说是有帮助的,老师对学生来说也是有帮助的。但这个道理很多人都不明白,不管是中国人还是外国人
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:54:33|这三位经济学家,一位是1992年获得诺贝尔奖的加里·贝克尔(Gary Becker),他说发国难财是增加供给的最好办法,当然应该鼓励。第二位经济学家是2002年诺奖得主弗农·史密斯(Vernon Smith),他说发国难财是好事。第三位经济学家是大家熟悉的1976年的诺奖得主米尔顿·弗里德曼,他说:“这些发国难财的人,是在救别人的命,他们应该得到一个奖章,而不是得到惩罚。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:54:56|阻止发国难财的直接后果,就是剥夺了遭受灾害的人的选择。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:04|我们在经济学中学到的最重要的教训之一,就是把愿望和结果分开来看。愿望是一回事,但愿望造成的结果是另外一回事。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:30|一个人想要买最便宜的苹果,但是当他进入市场寻找最便宜的苹果时,他的寻找行为本身就使得苹果的价格上升了。一个人想要卖最贵的苹果,但是当他进入市场,想要把自己的苹果以最高价卖出去时,他的行为本身就已经使得苹果的价格下跌了。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:45|那些乘人之危发财的人,他们自己本身的行为就增加了供给,使商品的价格下降,缓解了供需之间的矛盾。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:00:43|回顾中国的经济改革,如果只用一句话来概括,我会说,它是有关竞争规则改变的改革:从过去看排队的时间,看出身,看政治面貌,变成另外一个规则,那就是看出价的高低。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:01:23|短缺是因为价格受到了抑制
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:01:48|春节期间买不到火车票,人们要找黄牛党才能买到,这叫短缺
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:02:46|同样的道理,我们也没有听说奔驰汽车短缺过。它只是稀缺,但不会短缺,它永远在商店里等着我们。哪怕是暂时缺货,也不会成为一个新闻,不会成为一个社会现象。蒂芙尼的钻石也一样,它永远待在商店里等待着顾客。人们只要把钱放下,就可以带它回家。我们从未看哪个媒体说,蒂芙尼的钻石短缺了
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:02:58|过剩是价格被人为拔高的结果
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:04:46|不难看出,价格管制越严重,价值耗散就越大,人们设法绕过政府管制的积极性也越大;而政府围堵的力度越大,人们采用的对策就越迂回,其中白白浪费掉的竞争成本也越大。从这个角度看,价格管制之下,形形色色的违法活动以及它们出场的次序,也都是可以理解的。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:05:49|人们常有这样的鸵鸟心态,在讨论价格的时候,会说那些正在触动神经的商品非常特殊,不是商品,所以价格规律不起作用。谈水费的时候,说水不是商品;谈学费的时候,教育不是商品;谈药费的时候,健康不是商品;谈旅费的时候,回家过年不是商品。然而,抱着这样的心态,只能让人脱离现实,而无法正视问题和寻求对策。毕竟经济规律是不以人的意志为转移的
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:06:32|反对的第二个理由是,回家过年是刚需。意思是不管价格多高,人们还是要回家过年,因此提价只会让铁路系统多赚钱,而不能降低人们回家过年的愿望。
|
|
||||||
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:17:24|他们说:帮穷人的最好办法,就是以金钱的方式给穷人补贴,同时让市场发挥它自身应有的功能,而不是直接去干预商品的价格。
|
|
||||||
|
|||||||
66
kman.py
66
kman.py
@@ -8,6 +8,7 @@
|
|||||||
#########################################################
|
#########################################################
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
@@ -60,20 +61,32 @@ books_data =
|
|||||||
SYS = 'WIN' if platform.system()=='Windows' else \
|
SYS = 'WIN' if platform.system()=='Windows' else \
|
||||||
('LINUX' if platform.system()=='LINUX' else 'MAC')
|
('LINUX' if platform.system()=='LINUX' else 'MAC')
|
||||||
|
|
||||||
|
frozen = 'not'
|
||||||
|
#CURRPATH = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||||
|
CURRPATH = ''
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# we are running in a bundle
|
||||||
|
frozen = 'ever so'
|
||||||
|
CURRPATH = sys._MEIPASS
|
||||||
|
os.chdir(CURRPATH)
|
||||||
|
else:
|
||||||
|
# we are running in a normal Python environment
|
||||||
|
CURRPATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
# some constants
|
# some constants
|
||||||
LASTLINE = '=========='
|
LASTLINE = '=========='
|
||||||
NTPREF = '--CG注:'
|
NTPREF = '--CG注:'
|
||||||
CLIPFN = 'My Clippings.txt'
|
CLIPFN = 'My Clippings.txt'
|
||||||
WORDFN = 'vocab.db'
|
WORDFN = 'vocab.db'
|
||||||
CLIPPATH = './'
|
OUTPREF = os.path.join(CURRPATH,'CLIP')
|
||||||
OUTPREF = './clip'
|
|
||||||
DEBUG = 1 # 0 - INFO; 1 - DEBUG
|
DEBUG = 1 # 0 - INFO; 1 - DEBUG
|
||||||
LOG2FILE = 1 # 0 - to stdio; 1 - to file
|
LOG2FILE = 1 # 0 - to stdio; 1 - to file
|
||||||
LOGFILE = 'log'
|
|
||||||
DELIMITER= '|'
|
DELIMITER= '|'
|
||||||
BACKUPNOTEFN = './backup/bk.note.data' # kindle notes
|
LOGFILE = os.path.join(CURRPATH,'log')
|
||||||
BACKUPWORDFN = './backup/bk.word.data' # kindle words
|
BACKUPFOLDER = os.path.join(CURRPATH,'backup')
|
||||||
BACKUPINFOFN = './backup/bk.info.data' # book information from douban/amazon
|
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'}
|
#HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'}
|
||||||
|
|
||||||
# log info
|
# log info
|
||||||
@@ -119,6 +132,7 @@ r'''
|
|||||||
class kMan:
|
class kMan:
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
|
|
||||||
self.hlnum = 0
|
self.hlnum = 0
|
||||||
self.ntnum = 0
|
self.ntnum = 0
|
||||||
self.refleshtime = '2020/10/10 10:00:00'
|
self.refleshtime = '2020/10/10 10:00:00'
|
||||||
@@ -130,9 +144,9 @@ class kMan:
|
|||||||
kp = self.get_kindle_path()
|
kp = self.get_kindle_path()
|
||||||
|
|
||||||
if not kp:
|
if not kp:
|
||||||
s2 = u'Disconnected ({})'.format(CLIPPATH+CLIPFN)
|
s2 = u'Disconnected ({})'.format(os.path.join(CURRPATH,CLIPFN))
|
||||||
else:
|
else:
|
||||||
with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f:
|
with open(os.path.join(kp,'system','version.txt'), 'r', encoding='utf8', errors='ignore') as f:
|
||||||
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
|
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
|
||||||
|
|
||||||
return [s1,s2]
|
return [s1,s2]
|
||||||
@@ -310,7 +324,7 @@ class kMan:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
|
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
|
||||||
op = fnpref+suff[ft]
|
op = os.path.join(CURRPATH, fnpref+suff[ft])
|
||||||
|
|
||||||
with open(op, 'w', encoding='utf8', errors='ignore') as fw:
|
with open(op, 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
if ft=='JSON':
|
if ft=='JSON':
|
||||||
@@ -344,7 +358,7 @@ class kMan:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
|
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
|
||||||
op = fnpref+suff[ft]
|
op = os.path.join(CURRPATH,fnpref+suff[ft])
|
||||||
|
|
||||||
with open(op, 'w', encoding='utf8', errors='ignore') as fw:
|
with open(op, 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
if ft=='JSON':
|
if ft=='JSON':
|
||||||
@@ -579,6 +593,10 @@ class kMan:
|
|||||||
#serr = str(stream_stderr.read())
|
#serr = str(stream_stderr.read())
|
||||||
#if sout: print('stdout {}'.format(sout))
|
#if sout: print('stdout {}'.format(sout))
|
||||||
#if serr: print('stderr {}'.format(serr))
|
#if serr: print('stderr {}'.format(serr))
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
#print('os.name {} sout {}'.format(os.name,sout))
|
||||||
|
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
|
||||||
if os.name == 'nt': # windows
|
if os.name == 'nt': # windows
|
||||||
for d in sout.split('\n'):
|
for d in sout.split('\n'):
|
||||||
if 'Kindle' in d:
|
if 'Kindle' in d:
|
||||||
@@ -604,7 +622,7 @@ class kMan:
|
|||||||
|
|
||||||
return False
|
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
|
"""import clips from local file or kindle
|
||||||
4 lines for each section seperated with '======='
|
4 lines for each section seperated with '======='
|
||||||
so read 4 lines before '======='
|
so read 4 lines before '======='
|
||||||
@@ -623,6 +641,8 @@ class kMan:
|
|||||||
path = fn
|
path = fn
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not os.path.exists(fp): return {}
|
||||||
|
|
||||||
# loop to fill books dict
|
# loop to fill books dict
|
||||||
with open(fp, 'r', encoding='utf8', errors='ignore') as f:
|
with open(fp, 'r', encoding='utf8', errors='ignore') as f:
|
||||||
bks = defaultdict(dict)
|
bks = defaultdict(dict)
|
||||||
@@ -677,7 +697,7 @@ class kMan:
|
|||||||
return self.drop_duplicate(bks)
|
return self.drop_duplicate(bks)
|
||||||
|
|
||||||
# words operations
|
# 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
|
"""import words from local file or kindle
|
||||||
vocab.db -> tables: BOOK_INFO, LOOKUPS, WORDS
|
vocab.db -> tables: BOOK_INFO, LOOKUPS, WORDS
|
||||||
|
|
||||||
@@ -791,7 +811,23 @@ class kMan:
|
|||||||
|
|
||||||
return [nu, bk_wd_num]
|
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__':
|
if __name__=='__main__':
|
||||||
|
|
||||||
|
"""
|
||||||
|
# XXX move to unitest.kman.py
|
||||||
#books = defaultdict(dict)
|
#books = defaultdict(dict)
|
||||||
km = kMan()
|
km = kMan()
|
||||||
books = km.import_clips()
|
books = km.import_clips()
|
||||||
@@ -814,12 +850,14 @@ if __name__=='__main__':
|
|||||||
km.add_note_to_highlight(books)
|
km.add_note_to_highlight(books)
|
||||||
|
|
||||||
# test dict json convert
|
# test dict json convert
|
||||||
with open('./xx', 'w', encoding='utf8', errors='ignore') as fw:
|
with open(os.path.join(CURRPATH,'xx'), 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
fw.write(km.dict2json(books))
|
fw.write(km.dict2json(books))
|
||||||
if km.json2dict('./xx')==books: print( 'test OK')
|
if km.json2dict(os.path.join(CURRPATH,'xx'))==books: print( 'test OK')
|
||||||
|
|
||||||
km.export_notes(books, OUTPREF, ft='MD')
|
km.export_notes(books, OUTPREF, ft='MD')
|
||||||
|
|
||||||
# print data with json format
|
# print data with json format
|
||||||
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))
|
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|||||||
BIN
kmanapp.png
Normal file
BIN
kmanapp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
232
kmanapp.py
232
kmanapp.py
@@ -9,11 +9,17 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import operator
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import pandas as pd
|
#import pandas as pd
|
||||||
|
from mtable import mTable
|
||||||
import threading
|
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, QMenu)
|
||||||
from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt)
|
from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt)
|
||||||
from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon)
|
from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon)
|
||||||
|
|
||||||
@@ -60,6 +66,7 @@ words_temp = """<br><span style='font-size:18pt;color:#31849B'>{usage}</span><br
|
|||||||
ONLY_TEST = 1
|
ONLY_TEST = 1
|
||||||
|
|
||||||
class kmanWindow(QMainWindow):
|
class kmanWindow(QMainWindow):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(kmanWindow, self).__init__(*args, **kwargs)
|
super(kmanWindow, self).__init__(*args, **kwargs)
|
||||||
@@ -68,6 +75,8 @@ class kmanWindow(QMainWindow):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(kmanWindow, self).__init__(parent)
|
super(kmanWindow, self).__init__(parent)
|
||||||
|
|
||||||
|
self.copyinfo = ''
|
||||||
|
|
||||||
# create ui and initial it
|
# create ui and initial it
|
||||||
ui = Ui_MainWindow()
|
ui = Ui_MainWindow()
|
||||||
ui.setupUi(self)
|
ui.setupUi(self)
|
||||||
@@ -81,6 +90,13 @@ class kmanWindow(QMainWindow):
|
|||||||
self.km = kMan()
|
self.km = kMan()
|
||||||
self.spide = bookInfoSpide()
|
self.spide = bookInfoSpide()
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
### in order to smaller the package,
|
||||||
|
### substitute pandas table with mTable
|
||||||
|
self.mt = mTable()
|
||||||
|
|
||||||
|
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
|
||||||
self.books_info = defaultdict(dict)
|
self.books_info = defaultdict(dict)
|
||||||
# initial check order:
|
# initial check order:
|
||||||
# 1. backup file bk.data ->
|
# 1. backup file bk.data ->
|
||||||
@@ -90,9 +106,8 @@ class kmanWindow(QMainWindow):
|
|||||||
if os.path.exists(BACKUPNOTEFN) and os.path.exists(BACKUPWORDFN):
|
if os.path.exists(BACKUPNOTEFN) and os.path.exists(BACKUPWORDFN):
|
||||||
self.books_data = self.km.json2dict(BACKUPNOTEFN)
|
self.books_data = self.km.json2dict(BACKUPNOTEFN)
|
||||||
self.words_data = self.km.json2dict(BACKUPWORDFN)
|
self.words_data = self.km.json2dict(BACKUPWORDFN)
|
||||||
if (len(self.books_data)*len(self.words_data[0]))>=1:
|
if self.books_data and len(self.books_data) >=1 and \
|
||||||
self.books_data = self.km.json2dict(BACKUPNOTEFN)
|
self.words_data and len(self.words_data[0])>=1:
|
||||||
self.words_data = self.km.json2dict(BACKUPWORDFN)
|
|
||||||
flg = 1
|
flg = 1
|
||||||
|
|
||||||
if not flg:
|
if not flg:
|
||||||
@@ -114,7 +129,10 @@ class kmanWindow(QMainWindow):
|
|||||||
# if the information exist in backup file, initial with this file,
|
# if the information exist in backup file, initial with this file,
|
||||||
# and grap new book's information from douban
|
# and grap new book's information from douban
|
||||||
# else grap all book information from douban
|
# else grap all book information from douban
|
||||||
|
<<<<<<< HEAD
|
||||||
"""
|
"""
|
||||||
|
=======
|
||||||
|
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
|
||||||
try:
|
try:
|
||||||
if os.path.exists(BACKUPINFOFN):
|
if os.path.exists(BACKUPINFOFN):
|
||||||
self.books_info = self.km.json2dict(BACKUPINFOFN)
|
self.books_info = self.km.json2dict(BACKUPINFOFN)
|
||||||
@@ -155,7 +173,8 @@ class kmanWindow(QMainWindow):
|
|||||||
#ui.searchLineEdit.returnPressed.connect(self.search_return_press())
|
#ui.searchLineEdit.returnPressed.connect(self.search_return_press())
|
||||||
ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change)
|
ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change)
|
||||||
ui.searchToolButton.clicked.connect(self.search_button_clicked)
|
ui.searchToolButton.clicked.connect(self.search_button_clicked)
|
||||||
ui.treeView.clicked.connect(self.tree_item_clicked)
|
#ui.treeView.clicked.connect(self.tree_item_clicked)
|
||||||
|
ui.treeView.selectionModel().selectionChanged.connect(self.tree_item_selectionchanged)
|
||||||
ui.tableView.clicked.connect(self.table_item_clicked)
|
ui.tableView.clicked.connect(self.table_item_clicked)
|
||||||
ui.tableView.horizontalHeader().setStretchLastSection(True)
|
ui.tableView.horizontalHeader().setStretchLastSection(True)
|
||||||
#ui.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
#ui.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
||||||
@@ -170,7 +189,11 @@ class kmanWindow(QMainWindow):
|
|||||||
ui.tablemodel = nTableModel(data)
|
ui.tablemodel = nTableModel(data)
|
||||||
ui.tableView.verticalHeader().hide()
|
ui.tableView.verticalHeader().hide()
|
||||||
ui.tableView.setModel(self.ui.tablemodel)
|
ui.tableView.setModel(self.ui.tablemodel)
|
||||||
|
ui.tableView.setSortingEnabled(True)
|
||||||
|
ui.tableView.selectionModel().selectionChanged.connect(self.table_item_selectionchanged)
|
||||||
|
#ui.tableView.contextMenuEvent().selectionChanged.connect(self.table_item_selectionchanged)
|
||||||
|
|
||||||
|
# XXXX
|
||||||
self.ui.textEdit.installEventFilter(self)
|
self.ui.textEdit.installEventFilter(self)
|
||||||
|
|
||||||
# XXXX
|
# XXXX
|
||||||
@@ -190,21 +213,6 @@ class kmanWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
super(kmanWindow, self).eventFilter(source, event)
|
super(kmanWindow, self).eventFilter(source, event)
|
||||||
|
|
||||||
"""
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
editor = MyWidget()
|
|
||||||
cursor = editor.textCursor()
|
|
||||||
fmt = cursor.charFormat()
|
|
||||||
fmt.setForeground(QColor('blue'))
|
|
||||||
address = 'http://example.com'
|
|
||||||
fmt.setAnchor(True)
|
|
||||||
fmt.setAnchorHref(address)
|
|
||||||
fmt.setToolTip(address)
|
|
||||||
cursor.insertText("Hello world again", fmt)
|
|
||||||
editor.show()
|
|
||||||
app.exec_()
|
|
||||||
"""
|
|
||||||
|
|
||||||
def check_increase_books(self, bks, bksinfo):
|
def check_increase_books(self, bks, bksinfo):
|
||||||
new_list = list(bks.keys()) # kindle's books with note
|
new_list = list(bks.keys()) # kindle's books with note
|
||||||
new_list = [re.split(r'[\((\-\::_\s]',nn.strip())[0] for nn in new_list]
|
new_list = [re.split(r'[\((\-\::_\s]',nn.strip())[0] for nn in new_list]
|
||||||
@@ -252,6 +260,7 @@ class kmanWindow(QMainWindow):
|
|||||||
self.ui.tableView.verticalHeader().hide()
|
self.ui.tableView.verticalHeader().hide()
|
||||||
self.ui.tableView.setModel(self.ui.tablemodel)
|
self.ui.tableView.setModel(self.ui.tablemodel)
|
||||||
#self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot)
|
#self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot)
|
||||||
|
self.ui.tableView.selectionModel().selectionChanged.connect(self.table_item_selectionchanged)
|
||||||
|
|
||||||
# refresh textedit content
|
# refresh textedit content
|
||||||
if comp in [1,2]:
|
if comp in [1,2]:
|
||||||
@@ -261,6 +270,11 @@ class kmanWindow(QMainWindow):
|
|||||||
note=stype, content=scontent, position=sposition)
|
note=stype, content=scontent, position=sposition)
|
||||||
print(re.split(r'[\((\-\::_\s]',sbookname.strip())[0])
|
print(re.split(r'[\((\-\::_\s]',sbookname.strip())[0])
|
||||||
self.ui.textEdit.setHtml(tt)
|
self.ui.textEdit.setHtml(tt)
|
||||||
|
|
||||||
|
self.copyinfo = \
|
||||||
|
""" {content} \n\n--《{bookname}》{author} ({time}) 【{note} P{position}】""". \
|
||||||
|
format( content=scontent, bookname=sbookname, author=sauthor, time=stime,
|
||||||
|
note=stype, position=sposition )
|
||||||
elif comp in [3,4]:
|
elif comp in [3,4]:
|
||||||
self.render_textedit_words(self.words_data)
|
self.render_textedit_words(self.words_data)
|
||||||
elif comp == 5:
|
elif comp == 5:
|
||||||
@@ -278,6 +292,7 @@ class kmanWindow(QMainWindow):
|
|||||||
word = index.sibling(index.row(), 0).data()
|
word = index.sibling(index.row(), 0).data()
|
||||||
|
|
||||||
txt = ""
|
txt = ""
|
||||||
|
self.copyinfo = ''
|
||||||
for row in lookups:
|
for row in lookups:
|
||||||
if words[row[1]]['word'] == word:
|
if words[row[1]]['word'] == word:
|
||||||
[susage, stimestamp, sbookname, sauthor, scategory, sposition] = \
|
[susage, stimestamp, sbookname, sauthor, scategory, sposition] = \
|
||||||
@@ -289,6 +304,12 @@ class kmanWindow(QMainWindow):
|
|||||||
author=sauthor,category=scategory,
|
author=sauthor,category=scategory,
|
||||||
timestamp=stimestamp,position=sposition)
|
timestamp=stimestamp,position=sposition)
|
||||||
|
|
||||||
|
self.copyinfo += \
|
||||||
|
"""{usage} \n\n-- {bookname} {author} {category} {timestamp} {position} \n """ \
|
||||||
|
.format( usage=susage,bookname=sbookname, \
|
||||||
|
author=sauthor,category=scategory, \
|
||||||
|
timestamp=stimestamp,position=sposition)
|
||||||
|
|
||||||
self.ui.textEdit.setHtml(txt)
|
self.ui.textEdit.setHtml(txt)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -317,30 +338,58 @@ class kmanWindow(QMainWindow):
|
|||||||
self.ui.textEdit.setOpenExternalLinks(True)
|
self.ui.textEdit.setOpenExternalLinks(True)
|
||||||
self.ui.textEdit.setHtml(infos_temp.format(link=vv['link'],
|
self.ui.textEdit.setHtml(infos_temp.format(link=vv['link'],
|
||||||
bookname=vv['bookname'],
|
bookname=vv['bookname'],
|
||||||
author=vv['author'], ratenum=vv['ratenum'],
|
author=vv['author'],
|
||||||
score=vv['score'], publisher=vv['publisher'],
|
ratenum=vv['ratenum'],
|
||||||
publishing=vv['publishing'],description=vv['description'],
|
score=vv['score'],
|
||||||
|
publisher=vv['publisher'],
|
||||||
|
publishing=vv['publishing'],
|
||||||
|
description=vv['description'],
|
||||||
img=vv['img'].split('/')[-1]))
|
img=vv['img'].split('/')[-1]))
|
||||||
|
|
||||||
|
self.copyinfo = """ 1 | :green_book: | [{bookname}]({link}) | """ \
|
||||||
|
"""{author}/{score}/{publisher}/{publishing} {description}""" \
|
||||||
|
.format( bookname=vv['bookname'], \
|
||||||
|
link=vv['link'],
|
||||||
|
author=vv['author'],
|
||||||
|
score=vv['score'],
|
||||||
|
publisher=vv['publisher'],
|
||||||
|
publishing=vv['publishing'],
|
||||||
|
description=vv['description'],)
|
||||||
|
|
||||||
else: pass
|
else: pass
|
||||||
|
|
||||||
|
|
||||||
def convert_to_panda(self, mlist, tp=0):
|
def convert_to_panda(self, mlist, tp=0):
|
||||||
if 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'])
|
columns = ['Type','Bookname','Author','Position','Date','Content'])
|
||||||
else:
|
else:
|
||||||
pdframe = pd.DataFrame(mlist, \
|
#pdframe = pd.DataFrame(mlist, \
|
||||||
|
# columns = ['Word','Bookname','Author','Category'])
|
||||||
|
self.mt.dataframe(mlist, \
|
||||||
columns = ['Word','Bookname','Author','Category'])
|
columns = ['Word','Bookname','Author','Category'])
|
||||||
return pdframe
|
return self.mt
|
||||||
|
|
||||||
def tabledata_update_slot(self, s):
|
def tabledata_update_slot(self, s):
|
||||||
print('call tabledata_update_slot() {}'.format(s))
|
print('call tabledata_update_slot() {}'.format(s))
|
||||||
|
|
||||||
|
def tree_item_selectionchanged(self, selected, deselected):
|
||||||
|
# selected, deselected are QItemSelection objects
|
||||||
|
model_index_list = selected.indexes() # QModelIndexList
|
||||||
|
if not model_index_list: return 0
|
||||||
|
model_index = model_index_list[0] # QModelIndex
|
||||||
|
self.tree_item_clicked(model_index)
|
||||||
|
|
||||||
def tree_item_clicked(self, modelidx):
|
def tree_item_clicked(self, modelidx):
|
||||||
|
# modelidx is a QModelIndex object
|
||||||
|
|
||||||
model_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
|
model_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
|
||||||
model_index = model_index_list[0] # QModelIndex
|
model_index = model_index_list[0] # QModelIndex
|
||||||
itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
|
itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
|
||||||
item = itemmodel.itemFromIndex(modelidx) #QStandardItem
|
item = itemmodel.itemFromIndex(model_index) #QStandardItem
|
||||||
|
|
||||||
self.tree_selected = item.accessibleDescription()
|
self.tree_selected = item.accessibleDescription()
|
||||||
print(self.tree_selected, item.text())
|
print(self.tree_selected, item.text())
|
||||||
@@ -351,6 +400,7 @@ class kmanWindow(QMainWindow):
|
|||||||
comp = 0
|
comp = 0
|
||||||
if self.tree_selected.split('_')[0] == 'info':
|
if self.tree_selected.split('_')[0] == 'info':
|
||||||
self.ui.tableView.setVisible(False)
|
self.ui.tableView.setVisible(False)
|
||||||
|
self.ui.tableView.clearSelection()
|
||||||
self.render_textedit_infos(self.books_info, item.text())
|
self.render_textedit_infos(self.books_info, item.text())
|
||||||
comp = 5
|
comp = 5
|
||||||
else:
|
else:
|
||||||
@@ -389,6 +439,13 @@ class kmanWindow(QMainWindow):
|
|||||||
|
|
||||||
self.refresh_ui_component(comp)
|
self.refresh_ui_component(comp)
|
||||||
|
|
||||||
|
def table_item_selectionchanged(self, selected, deselected):
|
||||||
|
# selected, deselected are QItemSelection objects
|
||||||
|
model_index_list = selected.indexes() # QModelIndexList
|
||||||
|
if not model_index_list: return 0
|
||||||
|
model_index = model_index_list[0] # QModelIndex
|
||||||
|
self.table_item_clicked(model_index)
|
||||||
|
|
||||||
def table_item_clicked(self, index):
|
def table_item_clicked(self, index):
|
||||||
"""
|
"""
|
||||||
print('tableView.currentIndex().row() {} tableView.currentIndex().column() {}'
|
print('tableView.currentIndex().row() {} tableView.currentIndex().column() {}'
|
||||||
@@ -410,7 +467,11 @@ class kmanWindow(QMainWindow):
|
|||||||
self.ui.textEdit.setHtml(notes_temp.format(
|
self.ui.textEdit.setHtml(notes_temp.format(
|
||||||
bookname=sbookname, author=sauthor, time=stime,
|
bookname=sbookname, author=sauthor, time=stime,
|
||||||
note=stype, content=scontent, position=sposition))
|
note=stype, content=scontent, position=sposition))
|
||||||
elif ss=='note':
|
self.copyinfo = \
|
||||||
|
""" {content} \n\n-- 《{bookname}》{author} ({time}) 【{note} P{position}】""". \
|
||||||
|
format( content=scontent, bookname=sbookname, author=sauthor, time=stime,
|
||||||
|
note=stype, position=sposition )
|
||||||
|
elif ss=='word':
|
||||||
self.render_textedit_words(self.words_data)
|
self.render_textedit_words(self.words_data)
|
||||||
|
|
||||||
def search_button_clicked(self):
|
def search_button_clicked(self):
|
||||||
@@ -434,6 +495,58 @@ class kmanWindow(QMainWindow):
|
|||||||
print('call keyPressEvent() {} '.format(event.key()))
|
print('call keyPressEvent() {} '.format(event.key()))
|
||||||
self.search_button_clicked()
|
self.search_button_clicked()
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
|
||||||
|
# do not work!?
|
||||||
|
#if(self.ui.tableView.geometry().contains(event.pos())):
|
||||||
|
# print('contextMenuEvent pos {}'.format(event.globalPos()))
|
||||||
|
tree_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
|
||||||
|
table_index_list = self.ui.tableView.selectedIndexes()
|
||||||
|
# tableview item selected
|
||||||
|
if table_index_list:
|
||||||
|
model_index = table_index_list[0] # QModelIndex
|
||||||
|
|
||||||
|
menu = QMenu()
|
||||||
|
copy_action = menu.addAction(u"Copy")
|
||||||
|
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||||
|
if action ==copy_action:
|
||||||
|
self.copy_action(model_index.row(), model_index.column())
|
||||||
|
elif tree_index_list:
|
||||||
|
model_index = tree_index_list[0] # QModelIndex
|
||||||
|
itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
|
||||||
|
item = itemmodel.itemFromIndex(model_index) #QStandardItem
|
||||||
|
|
||||||
|
pp = self.tree_selected.split('_')[0]
|
||||||
|
menu = QMenu()
|
||||||
|
[copy_info_action, export_note_action, export_word_action] = \
|
||||||
|
[None, None, None]
|
||||||
|
if pp=='info':
|
||||||
|
copy_info_action = menu.addAction(u"Copy")
|
||||||
|
elif pp=='note':
|
||||||
|
export_note_action = menu.addAction(u"Export Notes")
|
||||||
|
elif pp=='word':
|
||||||
|
export_word_action = menu.addAction(u"Export Notes")
|
||||||
|
else: pass
|
||||||
|
|
||||||
|
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||||
|
fn = self.tree_selected+item.text().split('(')[0].strip()
|
||||||
|
if action==copy_info_action:
|
||||||
|
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||||
|
self.copy_action(model_index.row(), model_index.column())
|
||||||
|
elif action==export_note_action: self.export_filter_notes(fn)
|
||||||
|
elif action==export_word_action: self.export_filter_words(fn)
|
||||||
|
else: pass
|
||||||
|
|
||||||
|
else: pass
|
||||||
|
|
||||||
|
print('contextMenuEvent pos {}'.format(event.globalPos()))
|
||||||
|
|
||||||
|
def copy_action(self, row, column):
|
||||||
|
import clipboard
|
||||||
|
clipboard.copy(self.copyinfo)
|
||||||
|
|
||||||
|
print('copy_action row {} column {}'.format(row, column))
|
||||||
|
|
||||||
def search_return_press(self):
|
def search_return_press(self):
|
||||||
self.search_button_clicked()
|
self.search_button_clicked()
|
||||||
|
|
||||||
@@ -478,8 +591,8 @@ class kmanWindow(QMainWindow):
|
|||||||
self.messagebox(ico=2, info='\n\n kindle is not connected')
|
self.messagebox(ico=2, info='\n\n kindle is not connected')
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
self.books_data = self.km.import_clips(fp+'documents/'+CLIPFN)
|
self.books_data = self.km.import_clips(os.path.join(fp,'documents',CLIPFN))
|
||||||
self.words_data = self.km.import_words(fp+'system/vocabulary/'+WORDFN)
|
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_books, self.filter_list] = self.km.filter_clips(self.books_data)
|
||||||
self.filter_wordlist = self.km.filter_words(self.words_data)
|
self.filter_wordlist = self.km.filter_words(self.words_data)
|
||||||
|
|
||||||
@@ -649,18 +762,22 @@ class kmanWindow(QMainWindow):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def export(self):
|
def export(self):
|
||||||
if self.tree_selected.split('_')[0]=='note':
|
pp = self.tree_selected.split('_')[0]
|
||||||
self.export_filter_notes()
|
if pp=='note':
|
||||||
else:
|
self.export_filter_notes('export')
|
||||||
self.export_filter_words()
|
elif pp=='word':
|
||||||
|
self.export_filter_words('export')
|
||||||
|
else: # info
|
||||||
|
pass
|
||||||
|
|
||||||
print("call export()")
|
print("call export()")
|
||||||
|
|
||||||
def export_filter_notes(self):
|
def export_filter_notes(self, fn):
|
||||||
self.km.export_notes(self.filter_books, 'export', ft='MD')
|
self.km.export_notes(self.filter_books, fn, ft='MD')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def export_filter_words(self):
|
def export_filter_words(self, fn):
|
||||||
self.km.export_words(self.words_data, self.filter_list, 'export', ft='MD')
|
self.km.export_words(self.words_data, self.filter_list, fn, ft='MD')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def messagebox(self, ico=1, info=''):
|
def messagebox(self, ico=1, info=''):
|
||||||
@@ -687,6 +804,8 @@ class kmanWindow(QMainWindow):
|
|||||||
# backup file when kman closed
|
# backup file when kman closed
|
||||||
# so we can read backup file when kman start
|
# so we can read backup file when kman start
|
||||||
def closeEvent(self, e):
|
def closeEvent(self, e):
|
||||||
|
if not os.path.exists(BACKUPFOLDER): os.mkdir(BACKUPFOLDER)
|
||||||
|
|
||||||
with open(BACKUPNOTEFN, 'w', encoding='utf8', errors='ignore') as fw:
|
with open(BACKUPNOTEFN, 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
fw.write(self.km.dict2json(self.books_data))
|
fw.write(self.km.dict2json(self.books_data))
|
||||||
with open(BACKUPWORDFN, 'w', encoding='utf8', errors='ignore') as fw:
|
with open(BACKUPWORDFN, 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
@@ -749,24 +868,46 @@ class nTableModel(QAbstractTableModel):
|
|||||||
|
|
||||||
def data(self, index, role):
|
def data(self, index, role):
|
||||||
if role == Qt.DisplayRole:
|
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()))
|
self.tabledata_update[str].emit('{} {}'.format(index.row(),index.column()))
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
def rowCount(self, index):
|
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):
|
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):
|
def headerData(self, section, orientation, role):
|
||||||
# section is the index of the column/row.
|
# section is the index of the column/row.
|
||||||
if role == Qt.DisplayRole:
|
if role == Qt.DisplayRole:
|
||||||
if orientation == Qt.Horizontal:
|
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:
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@@ -795,7 +936,7 @@ if __name__ == "__main__":
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# loop check kindle is connected or not
|
# loop check kindle is connected or not
|
||||||
# BUG to be implement XXXX
|
# BUG, to be implement XXXX
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
t = threading.Thread(target=kmw.check_kindle_status)
|
t = threading.Thread(target=kmw.check_kindle_status)
|
||||||
@@ -804,4 +945,5 @@ if __name__ == "__main__":
|
|||||||
print ("Error: can not start thread")
|
print ("Error: can not start thread")
|
||||||
"""
|
"""
|
||||||
app.exec_()
|
app.exec_()
|
||||||
|
#sys.exit(app.exec_())
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!DOCTYPE QtCreatorProject>
|
||||||
<!-- Written by QtCreator 4.11.2, 2020-06-20T21:18:52. -->
|
<!-- Written by QtCreator 4.11.2, 2020-06-29T09:50:04. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
|||||||
@@ -54,6 +54,5 @@
|
|||||||
<file>icons/book.png</file>
|
<file>icons/book.png</file>
|
||||||
<file>icons/amazon.png</file>
|
<file>icons/amazon.png</file>
|
||||||
<file>icons/flower.png</file>
|
<file>icons/flower.png</file>
|
||||||
<file>kmanapp.ico</file>
|
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
44
kmanapp.spec
44
kmanapp.spec
@@ -1,3 +1,4 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
block_cipher = None
|
block_cipher = None
|
||||||
@@ -38,3 +39,46 @@ coll = COLLECT(exe,
|
|||||||
upx=True,
|
upx=True,
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
name='kmanapp')
|
name='kmanapp')
|
||||||
|
=======
|
||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py', ],
|
||||||
|
pathex=['/Users/mark/kman'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='kmanapp',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=False , icon='kmanapp.ico')
|
||||||
|
coll = COLLECT(exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name='kmanapp')
|
||||||
|
app = BUNDLE(coll,
|
||||||
|
name='kmanapp.app',
|
||||||
|
icon='kmanapp.ico',
|
||||||
|
bundle_identifier=None)
|
||||||
|
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
|
||||||
|
|||||||
1009
kmanapp_rc.py
1009
kmanapp_rc.py
File diff suppressed because it is too large
Load Diff
45
makepkg.md
Normal file
45
makepkg.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
# 打包问题
|
||||||
|
|
||||||
|
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看有没有问题
|
||||||
|
1. 所有路径合并,不用+, 用os.path.join()
|
||||||
|
1.
|
||||||
|
```
|
||||||
|
frozen = 'not'
|
||||||
|
#CURRPATH = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||||
|
CURRPATH = ''
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# we are running in a bundle
|
||||||
|
frozen = 'ever so'
|
||||||
|
CURRPATH = sys._MEIPASS
|
||||||
|
os.chdir(CURRPATH)
|
||||||
|
else:
|
||||||
|
# we are running in a normal Python environment
|
||||||
|
CURRPATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
```
|
||||||
|
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',]
|
||||||
|
Analysis(['kmanapp.py'], ==>
|
||||||
|
Analysis(['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py', ],
|
||||||
|
...
|
||||||
|
**这里有个问题,打开app是会运行所有('kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py',)py文件里的main,所以在打包时要把不是入口py程序的main删除或注释掉**
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
132
mtable.py
Normal file
132
mtable.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
#########################################################
|
||||||
|
## @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__':
|
||||||
|
|
||||||
|
"""
|
||||||
|
# move to unitest.kman.py
|
||||||
|
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())
|
||||||
|
"""
|
||||||
|
pass
|
||||||
@@ -327,6 +327,8 @@ class bookInfoSpide():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
"""
|
||||||
|
# XXX move to unitest.kman.py
|
||||||
spide = bookInfoSpide()
|
spide = bookInfoSpide()
|
||||||
|
|
||||||
for bkname in testbooks:
|
for bkname in testbooks:
|
||||||
@@ -339,4 +341,6 @@ if __name__ == '__main__':
|
|||||||
logger.debug('================ {} ================'.format(bkname))
|
logger.debug('================ {} ================'.format(bkname))
|
||||||
logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
|
logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
|
||||||
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))
|
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
PySide2==5.15.0
|
PySide2==5.15.0
|
||||||
pandas==1.0.3
|
|
||||||
requests==2.23.0
|
requests==2.23.0
|
||||||
|
|||||||
5
searchtitle.md
Normal file
5
searchtitle.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
|
||||||
|
--|--|--|--|--
|
||||||
|
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
|
||||||
|
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
|
||||||
|
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题
|
||||||
@@ -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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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)
|
|
||||||
|
|
||||||
25236
tfile/tdouban.data
25236
tfile/tdouban.data
File diff suppressed because it is too large
Load Diff
@@ -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')
|
|
||||||
@@ -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))
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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))
|
|
||||||
|
|
||||||
@@ -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))
|
|
||||||
|
|
||||||
@@ -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_()
|
|
||||||
@@ -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()
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
import platform
|
|
||||||
|
|
||||||
print(platform.system(),platform.machine(),platform.platform(aliased=0, terse=0) )
|
|
||||||
|
|
||||||
|
|
||||||
106
tfile/tthread.py
106
tfile/tthread.py
@@ -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_())
|
|
||||||
@@ -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()
|
|
||||||
'''
|
|
||||||
137
unitest.kman.py
137
unitest.kman.py
@@ -13,6 +13,8 @@ import json
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from kman import *
|
from kman import *
|
||||||
|
from parseweb import *
|
||||||
|
from mtable import *
|
||||||
|
|
||||||
# log info
|
# log info
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
@@ -31,6 +33,7 @@ class TestKman(unittest.TestCase):
|
|||||||
DELIMITER= '|'
|
DELIMITER= '|'
|
||||||
|
|
||||||
self.km = kMan()
|
self.km = kMan()
|
||||||
|
self.util = Util()
|
||||||
|
|
||||||
global t_bm_sec
|
global t_bm_sec
|
||||||
global t_hl_sec
|
global t_hl_sec
|
||||||
@@ -166,7 +169,7 @@ class TestKman(unittest.TestCase):
|
|||||||
print("与预期匹配,sidx 3 重复被删除,抛出: %s" % 'keyerror')
|
print("与预期匹配,sidx 3 重复被删除,抛出: %s" % 'keyerror')
|
||||||
|
|
||||||
t_secd.clear()
|
t_secd.clear()
|
||||||
|
|
||||||
# test function format_time()
|
# test function format_time()
|
||||||
def test_format_time(self):
|
def test_format_time(self):
|
||||||
t_ds = '2020年1月13日 星期一 下午 8:11:05'
|
t_ds = '2020年1月13日 星期一 下午 8:11:05'
|
||||||
@@ -216,7 +219,7 @@ class TestKman(unittest.TestCase):
|
|||||||
logger.debug('========== 2 ==========\n')
|
logger.debug('========== 2 ==========\n')
|
||||||
logger.debug(json.dumps(bn, indent=2, ensure_ascii=False))
|
logger.debug(json.dumps(bn, indent=2, ensure_ascii=False))
|
||||||
bn = {}
|
bn = {}
|
||||||
|
|
||||||
# by author
|
# by author
|
||||||
bn = self.km.filter_clips(t_books, '贾雷德·戴蒙德', 2)
|
bn = self.km.filter_clips(t_books, '贾雷德·戴蒙德', 2)
|
||||||
logger.debug('========== 3 ==========\n')
|
logger.debug('========== 3 ==========\n')
|
||||||
@@ -227,10 +230,61 @@ class TestKman(unittest.TestCase):
|
|||||||
def test_import_words(self):
|
def test_import_words(self):
|
||||||
import pprint
|
import pprint
|
||||||
[bookinfo, lookups, words] = self.km.import_words()
|
[bookinfo, lookups, words] = self.km.import_words()
|
||||||
# how to beauty print to logger file
|
# how to beauty print to logger file
|
||||||
logger.debug('========== 4 ==========\n')
|
logger.debug('========== 4 ==========\n')
|
||||||
logger.debug(json.dumps(bookinfo, indent=2, ensure_ascii=False))
|
logger.debug(json.dumps(bookinfo, indent=2, ensure_ascii=False))
|
||||||
logger.debug('========== 5 ==========\n')
|
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(self.km.import_words(), '中国历史风云录 (陈舜臣作品)', tp=0)
|
||||||
|
|
||||||
|
logger.debug('========== 8 ==========\n')
|
||||||
|
self.km.filter_words(self.km.import_words(), 'zh:闾', tp=1)
|
||||||
|
|
||||||
|
def test_util(self):
|
||||||
|
logger.debug( 'test get_app_path: {}'.format( self.util.get_app_path()))
|
||||||
|
|
||||||
|
def test_mtable(self):
|
||||||
|
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.columns\n', mt.get_columns())
|
||||||
print('== frame.index\n', mt.get_index())
|
print('== frame.index\n', mt.get_index())
|
||||||
|
|
||||||
@@ -245,6 +299,80 @@ class TestKman(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
def test_search_clip(self):
|
def test_search_clip(self):
|
||||||
pass
|
pass
|
||||||
|
def test_statistic(self):
|
||||||
|
pass
|
||||||
|
def test_dict2json(self):
|
||||||
|
pass
|
||||||
|
def test_json2dict(self):
|
||||||
|
pass
|
||||||
|
def test_import_clips(self):
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_parseweb(self):
|
||||||
|
spide = bookInfoSpide()
|
||||||
|
|
||||||
|
"""
|
||||||
|
for bkname in testbooks:
|
||||||
|
bkname = re.split(r'[\((\-\::_\s]',bkname.strip())[0]
|
||||||
|
print(bkname)
|
||||||
|
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(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_kman(self):
|
||||||
|
#books = defaultdict(dict)
|
||||||
|
km = kMan()
|
||||||
|
books = km.import_clips()
|
||||||
|
|
||||||
|
# remove duplication
|
||||||
|
km.drop_duplicate(books)
|
||||||
|
|
||||||
|
# test search note function
|
||||||
|
searchnote = km.search_clip(books, '三大都市圈', 'ALL', 'CONTENT')
|
||||||
|
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchcontent', ft='MD')
|
||||||
|
searchnote = km.search_clip(books, '经济', 'ALL', 'TITLE')
|
||||||
|
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchtitle', ft='MD')
|
||||||
|
searchnote = km.search_clip(books, '巴曙松', 'ALL', 'AUTHOR')
|
||||||
|
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchauthor', ft='MD')
|
||||||
|
|
||||||
|
#print(km.get_bookname_num(books))
|
||||||
|
#print(km.get_author_num(books))
|
||||||
|
|
||||||
|
# add note content to hightlight, then delete note
|
||||||
|
km.add_note_to_highlight(books)
|
||||||
|
|
||||||
|
# test dict json convert
|
||||||
|
with open(os.path.join(CURRPATH,'xx'), 'w', encoding='utf8', errors='ignore') as fw:
|
||||||
|
fw.write(km.dict2json(books))
|
||||||
|
if km.json2dict(os.path.join(CURRPATH,'xx'))==books: print( 'test OK')
|
||||||
|
|
||||||
|
km.export_notes(books, OUTPREF, ft='MD')
|
||||||
|
|
||||||
|
# print data with json format
|
||||||
|
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))
|
||||||
|
|
||||||
|
def test_somepath(self):
|
||||||
|
frozen = 'not'
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# we are running in a bundle
|
||||||
|
frozen = 'ever so'
|
||||||
|
bundle_dir = sys._MEIPASS
|
||||||
|
else:
|
||||||
|
# we are running in a normal Python environment
|
||||||
|
bundle_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
print( 'we are',frozen,'frozen')
|
||||||
|
print( 'bundle dir is', bundle_dir )
|
||||||
|
print( 'sys.argv[0] is', sys.argv[0] )
|
||||||
|
print( 'sys.executable is', sys.executable )
|
||||||
|
print( 'os.getcwd is', os.getcwd() )
|
||||||
|
|
||||||
|
print('sys.path[0]', sys.path[0])
|
||||||
print('sys.argv[0]', sys.argv[0])
|
print('sys.argv[0]', sys.argv[0])
|
||||||
print('os.path.realpath(sys.executable)', os.path.realpath(sys.executable))
|
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.realpath(sys.argv[0]))', os.path.realpath(sys.argv[0]))
|
||||||
@@ -265,4 +393,3 @@ if __name__ == '__main__':
|
|||||||
suite = unittest.TestSuite ()
|
suite = unittest.TestSuite ()
|
||||||
suite.addTest(TestKman('test_parse_section'))
|
suite.addTest(TestKman('test_parse_section'))
|
||||||
suite.addTest(TestKman('test_format_time'))
|
suite.addTest(TestKman('test_format_time'))
|
||||||
suite.addTest(TestKman('test_format_data'))
|
|
||||||
|
|||||||
1
xx
Normal file
1
xx
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"\u859b\u5146\u4e30\u7ecf\u6d4e\u5b66\u8bb2\u4e49": {"author": "\u859b\u5146\u4e30", "1": {"type": "HL", "position": "1408-1410", "day": "2020\u5e741\u670813\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0a\u5348", "time": "8:11:05", "content": "\u4e48\u5230\u5e95\u4ec0\u4e48\u53eb\u8fb9\u9645\uff1f\u8fb9\u9645\u5c31\u662f\u201c\u65b0\u589e\u201d\u5e26\u6765\u7684\u201c\u65b0\u589e\u201d\u3002 \u4f8b\u5982\uff0c\u8fb9\u9645\u6210\u672c\u5c31\u662f\u6bcf\u65b0\u589e\u4e00\u4e2a\u5355\u4f4d\u4ea7\u54c1\u6240\u9700\u8981\u4ed8\u51fa\u7684\u65b0\u589e\u6210\u672c\uff1b\u8fb9\u9645\u6536\u5165\u662f\u6bcf\u591a\u5356\u4e00\u4e2a\u4ea7\u54c1\u80fd\u591f\u5e26\u6765\u7684\u65b0\u589e\u6536\u5165\uff1b\u8fb9\u9645\u4ea7\u91cf\u662f\u6bcf\u65b0\u589e\u4e00\u4efd\u6295\u5165\u6240\u5e26\u6765\u7684\u65b0\u589e\u4ea7\u91cf\uff1b\u8fb9\u9645\u6548\u7528\u662f\u6bcf\u6d88\u8017\u4e00\u4e2a\u5355\u4f4d\u7684\u5546\u54c1\u6240\u80fd\u5e26\u6765\u7684\u65b0\u589e\u4eab\u53d7\u3002"}, "2": {"type": "HL", "position": "4284-4286", "day": "2020\u5e741\u670830\u65e5", "week": "\u661f\u671f\u56db", "meridiem": "\u4e0a\u5348", "time": "10:23:58", "content": "\u4e00\u4e2a\u56fd\u5bb6\u5f88\u5927\uff0c\u8d2b\u5bcc\u6709\u5dee\u8ddd\uff0c\u5e76\u975e\u6bcf\u4e2a\u5b66\u6821\u548c\u5bb6\u957f\u90fd\u80fd\u8d1f\u62c5\u5f97\u8d77\u8fd9\u6837\u6807\u51c6\u7684\u6821\u8f66\u3002\u6807\u51c6\u592a\u9ad8\uff0c\u5c31\u4f1a\u903c\u7740\u5f88\u591a\u5b66\u6821\uff0c\u5c24\u5176\u662f\u519c\u6751\u7684\u5b66\u6821\u653e\u5f03\u63d0\u4f9b\u6821\u8f66\uff0c\u5bb6\u957f\u4eec\u5c31\u53ea\u80fd\u4f7f\u7528\u5b89\u5168\u6027\u80fd\u66f4\u4f4e\u7684\u4ea4\u901a\u5de5\u5177\uff0c\u6bd4\u5982\u81ea\u5df1\u9a91\u81ea\u884c\u8f66\u6216\u96c7\u7528\u9ed1\u8f66\u7b49\uff0c\u7ed3\u679c\u662f\u5b69\u5b50\u4eec\u4eab\u53d7\u5230\u7684\u5b89\u5168\u4fdd\u969c\u53cd\u800c\u964d\u4f4e\u4e86\u3002--CG\u6ce8:\u5c71\u5be8 \u5047\u8d27 \u95ee\u9898"}}, "\u5e86\u4f59\u5e74(\u7cbe\u6821\u7248\uff09": {"author": "\u732b\u817b", "4": {"type": "HL", "position": "48484-48484", "day": "2020\u5e741\u670819\u65e5", "week": "\u661f\u671f\u65e5", "meridiem": "\u4e0b\u5348", "time": "8:00:29", "content": "\u56ed\u5b50\u91cc\u7684\u62a4\u536b\u80fd\u63ba\u591a\u5c11\u4eba\u5c31\u63ba\u591a\u5c11\u4eba\uff0c\u6211\u4f1a\u6d3e\u4eba\u76ef\u7740"}, "5": {"type": "HL", "position": "49901-49901", "day": "2020\u5e741\u670820\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0b\u5348", "time": "7:57:10", "content": "\u53f6\u7075\u513f\u53f9\u4e86\u53e3"}}}
|
||||||
Reference in New Issue
Block a user