├── CONTRIBUTING.md ├── DataSource └── MachineLearningInAction.zip ├── ImageStore ├── 618价格.png ├── 一键登录.gif ├── 加密数据库.jpg ├── 图书馆续借.gif ├── 网易云音乐.png ├── 网易云音乐further.png ├── 翻译软件.png ├── 计算器.png ├── 豆瓣电影.jpeg └── 验证码识别.gif ├── LICENSE ├── MrLevo520-618电商价格分析 ├── 0613_0621pricedetail ├── Tutorial_618价格分析.md ├── furtherProject │ ├── 另一种价格分析-test123 │ │ └── test.md │ └── 提交方式.md └── showData │ ├── 618echarts_show.html │ └── echarts.min.js ├── MrLevo520-MySQL加密存储 ├── Tutorial_MySql进行存储加密.md ├── furtherProject │ └── 提交方式.md └── mysql_encryptWD │ ├── README.md │ ├── mysql_encryptWD.exe │ └── mysql_encryptWD.py ├── MrLevo520-N-gram概括全文 ├── .ipynb_checkpoints │ └── 2_gram-checkpoint.ipynb ├── 2_gram.ipynb ├── Tutorial_N-gram概括内容.md ├── furtherProject │ ├── ZLFlyApple-ngram │ │ ├── Obama's dinner speech 2013.txt │ │ ├── Obama's dinner speech 2014.txt │ │ ├── Obama's dinner speech 2015.txt │ │ ├── Obama's dinner speech 2016.txt │ │ ├── README.md │ │ └── n-gram.ipynb │ └── 提交方式.md ├── save.png └── simhei.ttf ├── MrLevo520-使用Git对自己项目进行管理 └── Tutorial_使用Git对自己项目进行管理.md ├── MrLevo520-有道翻译小软件 ├── Tutorial_有道翻译小软件.md └── furtherProject │ └── 提交方式.md ├── MrLevo520-简易GUI计算器 ├── Tutorial_简易GUI计算器.md └── furtherProject │ └── 提交方式.md ├── MrLevo520-网易云音乐爬取分析 ├── Tutorial_网易云音乐分析.md └── furtherProject │ ├── zxylina-网易云音乐爬取分析 │ ├── echarts.png │ ├── mymusic2.png │ ├── mymusic3.png │ ├── zxylina-网易云音乐爬取分析.md │ ├── 下载软件.png │ └── 报错信息.png │ └── 提交方式.md ├── MrLevo520-自动化一键登录 ├── Tutorial_自动化一键登录.md └── furtherProject │ ├── wwivywwivy-51job │ └── 51job简历自动刷新 │ └── 提交方式.md ├── MrLevo520-自动续借图书馆书籍 ├── Tutorial_自动续借图书集.md └── furtherProject │ ├── qingwalv-AutodownloadCSVfile │ ├── 007-Firefox_Set01.jpg │ ├── 007-Firefox_Set02.jpg │ ├── qingwalv-AutodownloadCSVfile │ └── qingwalv-AutodownloadCSVfiles.md │ └── 提交方式.md ├── MrLevo520-豆瓣数据爬取与分析 ├── Tutorial_豆瓣数据爬取与分析.md └── furtherProject │ ├── ZLFlyApple-douban │ ├── README.md │ └── douban400MovieDetail │ ├── cowboy231-DouBan │ ├── README.md │ ├── img │ │ ├── 163642.png │ │ ├── 163804.png │ │ ├── 163851.png │ │ ├── 170851.png │ │ ├── 171821.png │ │ ├── 172311.png │ │ ├── 174243.png │ │ └── 195451.png │ └── work9 │ │ ├── Demo1.html │ │ ├── douban.txt │ │ ├── echarts.min.js │ │ └── work9.py │ ├── love-apple-豆瓣冷门电影分析 │ ├── 1.PNG │ ├── 2.PNG │ └── README.md │ └── 提交方式.md ├── MrLevo520-验证码自动识别 ├── Tutorial_验证码自动识别.md └── furtherProject │ ├── twolun-刷暴京东验证码 │ └── 开发说明.md │ └── 提交方式.md └── README.md /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Join Us 2 | 3 | ## 提交Tutorial项目方式 4 | 5 | - 这是一个开源项目,只要你有想法,并且付诸实践,欢迎进行tutorial的提交,我会以最短的时间内进行回复,希望能遇到各路大神和有趣的项目共同学习 6 | - 提交内容:希望tutorial保持项目完整性且有以下几个主要板块构成,书写请用markdown,推荐一个编辑器Typora 7 | - 环境 8 | - 目的 9 | - 效果 10 | - 代码 11 | - 注意点 12 | 13 | - 提交方式:以自己github名字+项目名字 提交文件夹,文件夹中应包含tutorial文件,furtherProject文件夹等(可参考目前已有项目),文件目录结构如下 14 | - MrLevo520-验证码识别 15 | - Tutorial_验证码识别.md 16 | - furtherProject 17 | - 提交方式.md 18 | 19 | 20 | ## 提交furtherProject项目方式 21 | 22 | - 如果你想在已有的项目下进行延伸自己的项目,欢迎你提交到该主项目下的furtherProject文件夹中 23 | - 提交内容:希望你的延伸项目有以下几个主要板块构成,书写请用markdown,推荐一个编辑器Typora 24 | - 目的 25 | - 效果 26 | - 代码(代码版本注明) 27 | - 注意点 28 | 29 | - 提交方式:以自己github名字+项目名字 提交**文件夹** 到furtherProject文件夹下文件目录结构如下(你只需要完成加粗部分就行了,至于你的文件夹里怎么写可以自己安排) 30 | - MrLevo520-验证码识别 31 | - Tutorial_验证码识别.md 32 | - furtherProject 33 | - **你的github名字-你的延伸项目名字(文件夹)** 34 | - 提交方式.md 35 | 36 | 37 | ## 如何进行pull request? 38 | 39 | > 例子可以看这里 [使用github进行项目管理与提交](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 40 | 41 | ## 我会在两天内进行回复和更新,期待共同开发者! 42 | -------------------------------------------------------------------------------- /DataSource/MachineLearningInAction.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/DataSource/MachineLearningInAction.zip -------------------------------------------------------------------------------- /ImageStore/618价格.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/618价格.png -------------------------------------------------------------------------------- /ImageStore/一键登录.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/一键登录.gif -------------------------------------------------------------------------------- /ImageStore/加密数据库.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/加密数据库.jpg -------------------------------------------------------------------------------- /ImageStore/图书馆续借.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/图书馆续借.gif -------------------------------------------------------------------------------- /ImageStore/网易云音乐.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/网易云音乐.png -------------------------------------------------------------------------------- /ImageStore/网易云音乐further.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/网易云音乐further.png -------------------------------------------------------------------------------- /ImageStore/翻译软件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/翻译软件.png -------------------------------------------------------------------------------- /ImageStore/计算器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/计算器.png -------------------------------------------------------------------------------- /ImageStore/豆瓣电影.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/豆瓣电影.jpeg -------------------------------------------------------------------------------- /ImageStore/验证码识别.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/ImageStore/验证码识别.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 MrLevo520 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MrLevo520-618电商价格分析/Tutorial_618价格分析.md: -------------------------------------------------------------------------------- 1 | ***From [数据向:618价格到底~到底是不是最低?](http://blog.csdn.net/mrlevo520/article/details/73610022)*** 2 | 3 | 4 | 5 | - 云服务器:ESC Ubuntu 16.04 x64 6 | - PhantomJS:beta-Linux-ubuntu-xenial 2.1.1 7 | - Python 2.7.12 8 | 9 | ----- 10 | 前言 11 | --- 12 | > 好久没玩点有意思的了,这次借618这个购物节,自己也要搞台mbp,顺便搞一波大新闻。 13 | 14 | ----- 15 | 内容 16 | ---- 17 | > 对某宝的其中四家店,再加上某东一家店,对比同一款机型,对价格进行监控,至于监控时间,大概是不间断的监控吧,还有邮件提醒哦~ 18 | 19 | ![这里写图片描述](http://img.blog.csdn.net/20170624155004402?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 20 | 21 | ----- 22 | 涉及面 23 | --- 24 | 25 | - 爬虫中阶知识,因为涉及到动态页面抓取,一个页面对不同型号进行点击操作,之后再获取元素,使用了phantomjs包 26 | 27 | - python字符串处理中阶知识,涉及到数据清洗,重构,还有缺失值的填充,这次没用pandas进行处理,直接用的是字符串处理技巧 28 | 29 | - 因为需要进行数据分析展示,故需要初级的JavaScript知识以及echarts的了解,这次构图使用的是echarts,比较简单 30 | 31 | 32 | ---- 33 | 开搞-数据挖掘 34 | --- 35 | > 因为涉及到商家利益,具体的代码细节将不再展开,如果有学术想研究的,可以私信我,或者留言,这个我考虑一下,清洗一下再发吧,虽然是比较简单的。 36 | 37 | ***流程和以前的动态页面抓取一致,选择需要的url之后进行元素点击操作,目的是为了选中需要比较价格的机型,这都么有问题,我以前写过很多关于phantomjs和selenium的动态页面处理的博客,大家如果只是想参考代码,可以移步[这里参考](http://blog.csdn.net/MrLevo520/article/category/6321608),主要将一些注意点*** 38 | 39 | 1. A:元素加载过程中出现can't find element问题 40 | Q: 首先确定自己的元素位置是否写对,建议使用xpath的方法定位元素,再chrom上直接可用copy xpath,其余都对的情况可以加上wait等待时间,[参考这里](http://blog.csdn.net/mrlevo520/article/details/52397305) 41 | 2. A:如果ip被封了,connection refused 42 | Q: 请参考[Python爬虫防封杀方法集合](http://blog.csdn.net/mrlevo520/article/details/52397305) 43 | 3. Q:动态页面加载过程中,商家交换了商品的次序,导致获取到的div位置不正确,怎么办? 44 | A: 解决方法,额,我是每天看一下log,看看有没有不正常的,不正常就kill任务然后修改位置,再接着跑,一般来说,商家不会闲着无聊去修改位置的,另一个解法是对之后的结果手动清洗,如果价格与之前的价格差值大过一定范围,则直接认为是噪声数据,毕竟,怎么可能价格涨跌超过1000的呢,还有一个解法是,确定点击元素的值代表是什么型号,然后点击的价格就是什么型号的了,这个我懒得做了。。。。 45 | 4. Q:我想要及时知道哪家店价格已经到我的接受阈值了,怎么通知我? 46 | A:写监控邮件,当价格低于某个阈值,直接触发邮件功能,邮件如何书写,请参考,这个是直接可用的[@Kevin_zhai的博客](http://blog.csdn.net/kevin_zhai/article/details/47720789)不再赘述 47 | 48 | 49 | ----- 50 | 开搞-数据处理 51 | --- 52 | 53 | 54 | > ***数据请从同目录下的0613_0621pricedetail进行下载***;这代码没啥,就是处理一些细节注意,数据抓取的形式也给出,可以套用理解。我最后的目的是获取价格变动的时间戳,不管哪家变动,都获取,然后把值都给选出来,这里有一些问题的是脏数据的处理,还有就是阈值的设定,毕竟某东的优惠券是800这个幅度的,,,, 55 | 56 | 57 | ``` 58 | import time 59 | 60 | # def timestamp(t): 61 | # timeArray = time.strptime(t, "%Y-%m-%d %H:%M:%S") 62 | # timeStamp = int(time.mktime(timeArray)) 63 | # return timeStamp 64 | 65 | ''' 66 | 数据形式 67 | ''' 68 | # 2017-06-10 17:24:56|京东|原始价格:8998.0|优惠券:满8000减800|到手价格:8198.0|价格趋势:- 69 | # 2017-06-10 17:24:56|淘宝_A|原始价格:7890.0|运费:免运费|到手价格:7890.0|价格趋势:- 70 | # 2017-06-10 17:24:56|淘宝_B|原始价格:7800.0|运费:39.0|到手价格:7839.0|价格趋势:- 71 | # 2017-06-10 17:24:56|淘宝_C|原始价格:7800.0|运费:30.0|到手价格:7830.0|价格趋势:- 72 | # 2017-06-10 17:24:56|淘宝_D|原始价格:7750.0|运费:免运费|到手价格:7750.0|价格趋势:- 73 | # 2017-06-10 17:24:56|最优价格方:淘宝_D|目前最低到手价格:7750.0|历史最低价:7750.0|价格趋势:- 74 | 75 | 76 | shop = {} 77 | with open('/Users/mrlevo/Desktop/project/0613_0621pricedetail') as f: 78 | for line in f: 79 | linesplit = line.split("|") 80 | if ':' in linesplit[1]: # 剔除最优价格方的记录 81 | pass 82 | else: 83 | needline = str(linesplit[0]) + '|' + linesplit[-2].split(':')[-1] # 获取时间戳和到手价格,毕竟分析的时候只需要这两个就行 84 | if linesplit[1] not in shop: # 进行字典的构建,里面存储list 85 | shop[linesplit[1]]=[] 86 | shop[linesplit[1]].append(needline) 87 | # 预处理-替换缺失值,向上替换,pandas里有直接的方法,这里选择字符串处理方法 88 | if shop[linesplit[1]][-1].split("|")[-1]=='None': 89 | shop[linesplit[1]][-1] = shop[linesplit[1]][-1].split("|")[0] + '|' + shop[linesplit[1]][-2].split("|")[-1] 90 | t +=1 91 | else: 92 | pass 93 | # 预处理-波动异常点-由于抓取的时候店家对链接的修改,导致元素异位 94 | 95 | try: 96 | if ((float(shop[linesplit[1]][-1].split("|")[-1]) > float(shop[linesplit[1]][-2].split("|")[-1])+500) or (float(shop[linesplit[1]][-1].split("|")[-1]) < float(shop[linesplit[1]][-2].split("|")[-1])-500)) and (linesplit[1]!='京东'): 97 | shop[linesplit[1]][-1] = shop[linesplit[1]][-1].split("|")[0] + '|' + shop[linesplit[1]][-2].split("|")[-1] 98 | else: 99 | pass 100 | except Exception as ex: 101 | print ex 102 | 103 | def find_change_time(shop): 104 | chang_ = [] 105 | for shopname in shop.keys(): 106 | pricedetail = shop[shopname] 107 | for i in range(len(pricedetail)-2): # 最后一个时刻抛弃 108 | split_f = pricedetail[i].split('|') 109 | split_s = pricedetail[i+1].split('|') 110 | price_f = split_f[-1] 111 | time_f = split_f[0] 112 | price_s = split_s[-1] 113 | time_s = split_s[0] 114 | if price_f == price_s: 115 | pass 116 | else: 117 | chang_.append(time_s) 118 | return chang_ 119 | 120 | # 获取价格变更时间戳 121 | changtime = sorted(find_change_time(shop)) 122 | print changtime 123 | 124 | for shopname in shop.keys(): 125 | satisfy_ = [k.split('|')[-1] for k in shop[shopname] if k.split('|')[0] in changtime] 126 | print satisfy_ 127 | 128 | ``` 129 | ***注意:处理方式,因为只有几万条数据,我就直接采用读文件流的形式来了,数据量大的可以采用pandas和spark,这也是完全可行的*** 130 | 131 | ----- 132 | 开搞-数据分析 133 | ---- 134 | 135 | > 这里就用到了前端的一点点知识了,你可以不用理解这是搞的什么,替换数据会不会?可以参考我写的一点小白教程[echarts小白入门](http://blog.csdn.net/mrlevo520/article/details/54603300)实在自己懒得写的话,我写好了,你不会懒得下载吧[echarts演示价格趋势](http://download.csdn.net/detail/mrlevo520/9879581),如果想线上访问,请看这里[echarts价格趋势](https://mrlevo520.github.io/Mini-Python-Project/MrLevo520-618%E7%94%B5%E5%95%86%E4%BB%B7%E6%A0%BC%E5%88%86%E6%9E%90/showData/618echarts_show.html) 136 | 137 | ![这里写图片描述](http://img.blog.csdn.net/20170624150723543?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 138 | 139 | ***这是总体的一张图,额,我们可以,额,清楚的看到,某东的价格变动次数,相比较于某宝,额,动态调价调的起飞啊*** 140 | 141 | 142 | ![这里写图片描述](http://img.blog.csdn.net/20170624151208079?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 143 | 144 | > 接下来看比较细致的几张图 145 | 146 | ![这里写图片描述](http://img.blog.csdn.net/20170624151006395?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 147 | 148 | ***这一幅图可以看出,的确在618那一天,某东到到价格最低,但非第一次到达,我们可以看在16好16点多就又一次达到过这个价格,而相比较于某宝,商家的定价规则在15号之后就没有产生过变动,这点就有点意思了,采集的四家店的三家在13号之后都不变化,一家店最后修改价格的时间15号,嗯哼*** 149 | 150 | ![这里写图片描述](http://img.blog.csdn.net/20170624151416915?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 151 | 152 | ------ 153 | 154 | > 因为抖动太平凡,所以只有用柱状图来表现了 155 | 156 | 157 | ![这里写图片描述](http://img.blog.csdn.net/20170624153224662?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 158 | 159 | ------- 160 | 161 | > 三家店的动态调价,当然趋势是向下的,而且他们的店家貌似也非常关注同行价格,会进行及时调价 162 | 163 | ![这里写图片描述](http://img.blog.csdn.net/20170624153241355?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 164 | 165 | -------- 166 | 167 | > 当然也有不关心价格,然后一看同行,我曹,都调价了啊,不行不行,我得赶紧的,--所以,A这家店,调价的幅度是其他三家最大的,如果大家也关注这家店,你也知道原因的,科科。 168 | 169 | 170 | ![这里写图片描述](http://img.blog.csdn.net/20170624153255997?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 171 | 172 | 173 | ![这里写图片描述](http://img.blog.csdn.net/20170624153308951?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 174 | 175 | ----- 176 | 建议 177 | --- 178 | - 对于节日主办方,的确可以选择那天下手,不过考虑下那天下手的代价就是快递炒鸡慢 179 | - 对于非主场的凑热闹商家,其实商品会和主场错开峰,就像苹果开发布会,三星不也得缓缓,不能直接怼啊对不对,哈哈,所以差不多提前两三天买应该是没有问题的,当别人还在抢购的时候,你已经收到货啦。 180 | - 土豪随意 181 | 182 | 183 | ---- 184 | 其他 185 | --- 186 | 187 | > 我就不过度解读了,在18号的确大家可以用最低的价格入手,但是相比较于大家不断的网络流量,这一点差价而言,我想双方都是很乐意看到的,这也就是各种大促销的由来,巴不得一年四季有个节日就搞个促销,当然,抢券这种是比较例外的,的确有很大幅度的降低价格,但还是那句话,流量。更多的数据自己挖掘请下载上述的html文件,enjoy yourself! 188 | 189 | ---- 190 | 最后 191 | --- 192 | > 想去做的事,就去做,不要再等了,有想法,就去实现吧!与君共勉! 193 | 194 | 195 | 196 | ---- 197 | 致谢 198 | ---- 199 | 200 | @逗比的自己 201 | [@Kevin_zhai--用python发送邮件](http://blog.csdn.net/kevin_zhai/article/details/47720789) 202 | 203 | -------------------------------------------------------------------------------- /MrLevo520-618电商价格分析/furtherProject/另一种价格分析-test123/test.md: -------------------------------------------------------------------------------- 1 | ## This is a furtherProject demo 2 | 3 | - 2017.9.3 test2 4 | -------------------------------------------------------------------------------- /MrLevo520-618电商价格分析/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-618电商价格分析/showData/618echarts_show.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ECharts 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 141 | 142 | -------------------------------------------------------------------------------- /MrLevo520-MySQL加密存储/Tutorial_MySql进行存储加密.md: -------------------------------------------------------------------------------- 1 | ***From [Python+MySQL用户加密存储验证系统(进阶)](http://blog.csdn.net/mrlevo520/article/details/52089545)*** 2 | 3 | 4 | 5 | - Python 2.7 6 | - IDE Pycharm 5.0.3 7 | - PyMySQL 0.7.6 8 | - MySQL 5.7 9 | - MySQL Workbench 6.3 10 | 11 | > 至于MySQL和Python如何联调使用请看上期[Python与MySQL联动实例一两则 ](http://blog.csdn.net/mrlevo520/article/details/52083615) 12 | 13 | ---------- 14 | 我要填以前挖过的坑了,用户存储加密验证系统beta上线 15 | ---------- 16 | 17 | 填坑&目的 18 | -- 19 | > ​ 这坑是[Python用户存储加密及登录验证系统(乞丐版)](http://blog.csdn.net/mrlevo520/article/details/51789914)挖的,当时还不会使用数据库,现在学到了,不填坑不太好是不是? 20 | 21 | ---------- 22 | 23 | 应用场景 24 | -- 25 | > ​ 如果数据库是暂存在第三方,而且存入的数据又不想让第三方数据库管理员看到,消息涉及隐私,只有自己可见,那么怎么办呢,我自己设计了一套用户加密验证系统,对登录密码进行MD5/SHA1可选加密,对明文进行自定义的加密算法进行加密存储。短时间内无法破解(私以为) 26 | 27 | ---------- 28 | 29 | 特点 30 | -- 31 | 32 | > ​ 用户加密存储系统--用于托管第三方数据库,内容进行加密后存储,没有秘钥无法破解 33 | 34 | 1. 用户存储,登录,查看,删除操作,存储在数据库中 35 | 2. 用户密码加密存储,密码加密方式可选,目前只可选MD5和SHA1,用户存储内容加密存储,加密方式自定义 36 | 3. 自定义(我自己定义了一个加密解密函数)加密序列,拿到内容没有序列无法解密(自以为) 37 | 4. 可更改用户密码,更改自定义KEY值,更改加密存储内容 38 | 5. 支持任何位数和形式设置密码,甚至可以设置成中文!但是请注意,最好是杂乱无序字母夹杂的,不然被破解第一层密码后,KEY值可能会暴露! 39 | 40 | ---------- 41 | 42 | 实现流程框架 43 | ------ 44 | 这尼玛我图用Visio花了一个多小时。。。。可能我毕设都没那么较真的画图。。。。 45 | ![算法流程图](http://upload-images.jianshu.io/upload_images/2671133-70dc3fb3a64e675c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 46 | 47 | > 其实上面流程图的最左边如果新建用户和数据库中用户重名,则有两个选择,一个是重新命名,另一个就是对原来用户名进行修改密码,线有点多,太乱了连过去,所以这里省略了,实现效果请看下面的IDE交互章节。 48 | 49 | 好了,整体的思路框架就是这样,当然一开始我没有想那么多,只是做着做着,想着不断增加功能,更加完善性考虑,才会加入那么多选择项的,因为自己设计的,所以,难免存在瑕疵,也没有参考实际大的加密工程中如何处理,下次去看看。 50 | 51 | ---------- 52 | 53 | 实现代码 54 | ---- 55 | **这里我就不贴详细代码了,太长了,估计三四百行把,我上传资源在同级目录的mysql_encryptWD中,包括源码(带注释)+exe(exe由于打包软件限制只能用于英文字符输入)+README(请先阅读)** 56 | 57 | > 这里只是贴上自己写的加密算法部分。 58 | 59 | ``` 60 | #自定义加密、解密算法子函数。结合base64 61 | def encrypt(key,content): # key:密钥,content:明文 62 | s1 = base64.encodestring(str(content)) #将内容进行base64加密 63 | len1 = len(key) % 7 #取余数 64 | len1_list = list_key[len1] #取余数对应list_key中伪码 65 | mix_first = str(key)+s1 #将key转化为字符串后拼接第一次加密的内容 66 | mix = len1_list+base64.encodestring(mix_first) #对拼接后字符串再进行加密,再加个伪码 67 | 68 | return mix #存入数据库中,不能被反解 69 | 70 | def decrypt(key,mix): # key:密钥,content:密文 71 | 72 | len2 = len(key) % 7 73 | len2_findlist = list_key[len2] 74 | 75 | if len2_findlist==mix[0]: #先确定伪码 76 | s2_first = base64.decodestring(mix[1:])#反解出第一次的base64编码 77 | s2_second = s2_first[0:len(key)] #获取第一次解出前缀是否为key 78 | 79 | if s2_second==str(key):#key值对应了 80 | s2_end = base64.decodestring(s2_first[len(key):])#反解出去掉前缀后的真实内容的64位编码 81 | print '-------------------------------Validation Succeed!-------------------------------' 82 | 83 | return s2_end 84 | else: 85 | print "Warning!Validation Failed!Can't Get Secret Words!" 86 | 87 | else: 88 | print "Warning!Validation Failed!Can't Get Secret Words!" 89 | 90 | ``` 91 | 解释就在上面的注释上了,这里说一下实现效果,存入数据库中形式应该是这样的 92 | ![数据库表现形式](http://upload-images.jianshu.io/upload_images/2671133-b6da39552e213ecf?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 93 | 94 | ***对于自己的加密算法:自己尝试写,肯定会有纰漏的地方,如果以后有机会,可以学一下密码学,自己接触的到底还是太少了,只是添加了伪码表,然后两次base64加密,当然,数据库中内容直接拿来base64解码肯定是不会成功的。*** 95 | 96 | *登录密码是用MD5/SHA1进行加密的,而第二层,登陆之后,可以自己选择KEY值,对输入的明文进行加密,如果没有我的伪字典和KEY应该是不能反解出加密的内容的,所以对于数据库管理员来说,根本不能解密存入数据库中的内容,对于有特殊需求的记录也好,项目也好,我想应该有应用的地方。* 97 | 98 | ---------- 99 | 100 | 交互效果 101 | ---- 102 | 这里显示的是整个IDE交互界面,如何处理这些; 103 | 104 | ---------- 105 | 106 | 107 | 0.进行新用户注册(可以选择不设置明文默认KEY为123456) 108 | 109 | ``` 110 | -------------------------------Mode Choice------------------------------------- 111 | Store&Encrypt-1 Login&View&Update&Delete-2 Quit System-3 Clear Database-4 112 | Select Mode:1 113 | -------------------------------Store&Encrypt------------------------------- 114 | New User:k3 115 | Set Password:k3 116 | -------------------------------Password Encrypt Algorithm------------------------------------- 117 | MD5-1 SHA1-2 118 | Select Algorithm:1 119 | 120 | ``` 121 | 122 | ---------- 123 | 1.进行设置KEY和明文加密 124 | 125 | ``` 126 | -------------------------------What's Next?------------------------------------- 127 | Store Encrypt Plaintext-1 Maybe Next Time-2 128 | Your Choice:1 129 | Please Design Your KEY:k4 130 | Plaintext:k4'secret 131 | ############################################# 132 | #SHA1-Password&Plaintext Encryption Succeed!# 133 | ############################################# 134 | ``` 135 | 以下是默认KEY与明文设置,选择2即可 136 | ``` 137 | -------------------------------What's Next?------------------------------------- 138 | Store Encrypt Plaintext-1 Maybe Next Time-2 139 | Your Choice:2 140 | Default KEY '123456' 141 | Default Plaintext 'Default Storage' 142 | ############################################ 143 | #MD5-Password&Plaintext Encryption Succeed!# 144 | ############################################ 145 | ``` 146 | 147 | ---------- 148 | 2.查看加密明文 149 | (第0步中,如果没有自己设置KEY等,会有个默认值进行存储) 150 | 151 | 以下为自己设置了KEY和明文(没有设置时候,则KEY为123456),查看明文 152 | ``` 153 | -------------------------------k4:What's Next?------------------------------- 154 | Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6 155 | Your Choice: 2 156 | KEY:k4 157 | -------------------------------Validation Succeed!------------------------------- 158 | Secret Words:k4'secret 159 | ``` 160 | 更加详细的请自己测试使用。代码和上述流程图保持一致。 161 | 162 | ---------- 163 | 164 | 3.遇到新用户重名,解决途径,修改密码或者更换新名字 165 | 166 | ``` 167 | -------------------------------Mode Choice------------------------------------- 168 | Store&Encrypt-1 Login&View&Update&Delete-2 Quit System-3 Clear Database-4 169 | Select Mode:1 170 | -------------------------------Store&Encrypt------------------------------- 171 | New User:k1 172 | Warning!The Name Already Exist! 173 | -------------------------------Make Your Choice------------------------------------- 174 | Change Password-1 Create New User-2 175 | Select Mode:2 176 | New User:k2 177 | Set Password:k3 178 | 179 | ``` 180 | 181 | ---------- 182 | 183 | 4.更新登录密码选择,需要有以前密码,才能修改 184 | 185 | ``` 186 | -------------------------------Welcome k1------------------------------- 187 | -------------------------------k1:What's Next?------------------------------- 188 | Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6 189 | Your Choice: 3 190 | Please Enter Original Password:k1 191 | Please Enter New Password:k2 192 | ########################## 193 | #Update Password Succeed!# 194 | ########################## 195 | ``` 196 | 197 | ---------- 198 | 5.更新KEY值 199 | 200 | ``` 201 | -------------------------------Welcome k1------------------------------- 202 | -------------------------------k1:What's Next?------------------------------- 203 | Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6 204 | Your Choice: 4 205 | Please Enter Original KEY:k1 206 | Please Enter New KEY:k2 207 | -------------------------------Validation Succeed!------------------------------- 208 | ##################### 209 | #Update KEY Succeed!# 210 | ##################### 211 | ``` 212 | 213 | ---------- 214 | 6.更新明文 215 | 216 | ``` 217 | 218 | -------------------------------k4:What's Next?------------------------------- 219 | Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6 220 | Your Choice: 1 221 | KEY:k4 222 | -------------------------------Validation Succeed!------------------------------- 223 | Original Plaintext:k4'secret 224 | New Plaintext:k4's secret2 225 | ########################### 226 | #Update Plaintext Succeed!# 227 | ########################### 228 | ``` 229 | 230 | 231 | 232 | ---------- 233 | 234 | 遇到的问题及解决方案 235 | ---------- 236 | Q: MD5/SHA1加密存储时候的类型不同引起的错误。 237 | A: 解决方案,多进行try/except使用抛出错误,定位错误,常用输出语句进行和预期值之间的排错,如下,md5加密后为元组形式,而sha1为str类型 238 | ``` 239 | import hashlib 240 | 241 | #MD5和SHA1加密算法 242 | def md5(str1): 243 | md = hashlib.md5() 244 | md.update(str1) 245 | md_5=md.hexdigest() 246 | return md_5, 247 | 248 | def sha1(str1): 249 | sh = hashlib.sha1() 250 | sh.update(str1) 251 | sha_1 = sh.hexdigest() 252 | return sha_1 253 | 254 | print md5("123") 255 | print type(md5("123")) 256 | print sha1("123") 257 | print type(sha1("123")) 258 | ``` 259 | 运行后 260 | 261 | ``` 262 | ('202cb962ac59075b964b07152d234b70',) 263 | 264 | 40bd001563085fc35165329ea1ff5c5ecbdbbeef 265 | 266 | ``` 267 | 知道所出现的形式之后,对症下药就可以了! 268 | 269 | ---------- 270 | Q: 对数据库进行插入,删除,更新操作,数据库内容不改变问题 271 | A: 解决方案,没有进行事务提交! 272 | 比如,我实现添加操作,最后需要添加语句commit 273 | 274 | ``` 275 | cur.execute("insert into store(user_name,passwd,encrypt_words,encrypt_password) VALUES (%s,%s,%s,%s)",(user_name,passwd,encrypt_str,key_content)) 276 | 277 | cur.connection.commit()#commit()提交事物,做出改变后必须提交事务,不然不能更新 278 | ``` 279 | 280 | ---------- 281 | Q: 数据库出现Lock wait timeout exceeded错误,原因是如图 282 | 283 | ![原因解析](http://upload-images.jianshu.io/upload_images/2671133-fad1c3842980bb8a?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 284 | 285 | A: 解决方案;这里应该运行时候断开以前运行的程序,这点我做的不好,调试的时候,以前的程序还在运行,全部断开连接,只要一个运行就行 286 | 287 | ---------- 288 | Q: 结构功能问题 289 | A: 需要实践积累,怎样实现目的,产生比较清晰的架构,子函数应该怎么写,才能最大程度的调用,这些我都比较弱,需要不断的进行学习和测试,我的框架结构也是改了很多次,都是进行测试之后慢慢修改成型的,考虑到了几乎所有的操作需求,你能信当时我只是想弄个加密写入和读取的玩意就行了么,最后还是写成比较完善的一个小项目了,所以,这个问题,只有不断练习把,不过下次我会先拟构好一个流程图框架再写。 290 | 291 | ---------- 292 | 293 | What's new? 294 | ------------- 295 | 从最初涉及到全部功能实现,写函数的时间大概只占了百分之三十,其余时间都在进行排错调试,因为功能的繁多,并不知道哪里会出错,可能功能和功能之间衔接,可能大的分选项,退出到几级菜单,这些问题我几乎每个都遇到过,最后一一解决,感觉很棒!以后分块写模块调试,还是很重要的,还有就是,模块包裹的成分多少我还没把握好,最高效的调用模块才是个好模块呢! 296 | 297 | 298 | 299 | ## 致谢 300 | 301 | - Python网络数据采集[Ryan Mitchell著][人民邮电出版社] 302 | - [@Mrlevo520--Python用户存储加密及登录验证系统(乞丐版)](http://blog.csdn.net/mrlevo520/article/details/51789914) 303 | - [在线转换工具--将代码以BASE64方式加密、解密](http://www1.tc711.com/tool/BASE64.htm) 304 | - [@Mrlevo520--Python与MySQL联动实例一两则 ](http://blog.csdn.net/mrlevo520/article/details/52083615) 305 | - [@li_feibo--关于Lock wait timeout exceeded; try restarting transaction](http://www.51testing.com/html/16/390216-838016.html) -------------------------------------------------------------------------------- /MrLevo520-MySQL加密存储/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-MySQL加密存储/mysql_encryptWD/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Feature 4 | 5 | > ​ 用户加密存储系统--用于托管第三方数据库,内容进行加密后存储,没有秘钥无法破解 6 | 7 | 1. 用户存储,登录,查看,删除操作,存储在数据库中 8 | 2. 用户密码加密存储,密码加密方式可选,目前只可选MD5和SHA1,用户存储内容加密存储,加密方式自定义 9 | 3. 自定义(我自己定义了一个加密解密函数)加密序列,拿到内容没有序列无法解密 10 | 4. 可更改用户密码,更改自定义KEY,更改加密存储内容 11 | 12 | 13 | 14 | ## Pay Attention 15 | 16 | - 请先安装MySQL,并进行测试连接,exe文件可用于win8/10 x64 17 | - 注意exe,除非环境和我一样,否则 不能运行的,我的设置是host = '127.0.0.1',user='root',passwd='A089363b' 18 | - 注意还是自己从源码修改然后用pyinstaller进行打包吧 19 | 20 | 21 | 22 | ## Have Fun! 23 | 24 | > 如果有BUG请与我联系,也可以在我主页留言博客[Python+MySQL用户加密存储验证系统(进阶)](http://blog.csdn.net/mrlevo520/article/details/52089545)或者issue进行反馈 25 | 26 | -------------------------------------------------------------------------------- /MrLevo520-MySQL加密存储/mysql_encryptWD/mysql_encryptWD.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-MySQL加密存储/mysql_encryptWD/mysql_encryptWD.exe -------------------------------------------------------------------------------- /MrLevo520-MySQL加密存储/mysql_encryptWD/mysql_encryptWD.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #Author:哈士奇说喵 3 | #用户加密存储系统 4 | import base64 5 | import pymysql 6 | import hashlib 7 | 8 | 9 | 10 | #初始化数据库子函数 11 | def Init(): 12 | #创建数据库 13 | try: 14 | cur.execute('create database MD5_Storetest')#创建名为professors数据库 15 | cur.execute('use MD5_Storetest')#切到该数据库 16 | cur.execute('create TABLE store(id BIGINT(7) NOT NULL AUTO_INCREMENT,user_name VARCHAR(100),passwd VARCHAR(100),encrypt_words VARCHAR(10000),encrypt_password VARCHAR(100),created TIMESTAMP DEFAULT CURRENT_TIMESTAMP ,PRIMARY KEY(id))') 17 | print '-------------------------------Create Database Succeed-------------------------------' 18 | except: 19 | #print 'database existed' 20 | cur.execute('use MD5_Storetest')#切到该数据库 21 | 22 | #MD5和SHA1加密算法 23 | def md5(str1): 24 | md = hashlib.md5() 25 | md.update(str1) 26 | md_5=md.hexdigest() 27 | return md_5, 28 | 29 | def sha1(str1): 30 | sh = hashlib.sha1() 31 | sh.update(str1) 32 | sha_1 = sh.hexdigest() 33 | return sha_1 34 | 35 | 36 | #自定义加密、解密算法子函数。结合base64 37 | def encrypt(key,content): # key:密钥,content:明文 38 | s1 = base64.encodestring(str(content)) #将内容进行base64加密 39 | len1 = len(key) % 7 #取余数 40 | len1_list = list_key[len1] #取余数对应list_key中伪码 41 | mix_first = str(key)+s1 #将key转化为字符串后拼接第一次加密的内容 42 | mix = len1_list+base64.encodestring(mix_first) #对拼接后字符串再进行加密,再加个伪码 43 | 44 | return mix #存入数据库中,不能被反解 45 | 46 | def decrypt(key,mix): # key:密钥,content:密文 47 | 48 | len2 = len(key) % 7 49 | len2_findlist = list_key[len2] 50 | 51 | if len2_findlist==mix[0]: #先确定伪码 52 | s2_first = base64.decodestring(mix[1:])#反解出第一次的base64编码 53 | s2_second = s2_first[0:len(key)] #获取第一次解出前缀是否为key 54 | 55 | if s2_second==str(key):#key值对应了 56 | s2_end = base64.decodestring(s2_first[len(key):])#反解出去掉前缀后的真实内容的64位编码 57 | print '-------------------------------Validation Succeed!-------------------------------' 58 | 59 | return s2_end 60 | else: 61 | print "Warning!Validation Failed!Can't Get Secret Words!" 62 | 63 | else: 64 | print "Warning!Validation Failed!Can't Get Secret Words!" 65 | 66 | #密码加密与明文加密存储子函数 67 | def PasswdSecretWD_encrypt(name,keywords,cho,key_content,content_key): 68 | 69 | #根据选项进行下一步操作 70 | if cho == '1': 71 | 72 | mix = encrypt(key_content, content_key) 73 | print "############################################\n#MD5-Password&Plaintext Encryption Succeed!#\n############################################" 74 | 75 | try: 76 | key_content_sha1 = sha1(key_content)#对KEY进行加密 77 | store(name,md5(keywords),mix,key_content_sha1) 78 | except: 79 | print "Warning!Can't Find SQL!" 80 | 81 | elif cho == '2': 82 | 83 | mix = encrypt(key_content, content_key) 84 | print "#############################################\n#SHA1-Password&Plaintext Encryption Succeed!#\n#############################################" 85 | try: 86 | key_content_md5 = md5(key_content)[0]#对KEY进行加密 87 | store(name,sha1(keywords),mix,key_content_md5) 88 | except: 89 | print 'Warning!Insert SQL Failed!' 90 | else: 91 | print "Warning!Something Wrong in Your Encryption Algorithm!" 92 | 93 | #数据库的存储子函数 94 | def store(user_name,passwd,encrypt_str,key_content): 95 | cur.execute("insert into store(user_name,passwd,encrypt_words,encrypt_password) VALUES (%s,%s,%s,%s)",(user_name,passwd,encrypt_str,key_content)) 96 | 97 | cur.connection.commit()#commit()提交事物,做出改变后必须提交事务,不然不能更新 98 | 99 | #数据库的提取子函数 100 | def check(user_name): 101 | cur.execute('select * FROM store WHERE user_name=%s',(user_name)) 102 | return cur.fetchall()#抓取符合的一整行,元组形式返回 103 | 104 | #加密内容解密子函数 105 | def getSecret(key,sql_str):#获取数据库中加密数据后解密 106 | 107 | try: 108 | ans = decrypt(key,sql_str) 109 | return ans 110 | except: 111 | print "Warning!Decryption Failed!Can't Get Secret Words!" 112 | 113 | #更新密码子函数 114 | def updatePasswd(existed_name): 115 | 116 | try: 117 | #判断是否为该用户,要求输入原始密码,经过校验后,才允许修改 118 | ori_passwd = raw_input("Please Enter Original Password:") 119 | if str(check(existed_name)[0][2]) == md5(ori_passwd)[0] or str(check(existed_name)[0][2]) == sha1(ori_passwd): 120 | new_passwd = raw_input("Please Enter New Password:") 121 | new_passwdmd5 = md5(new_passwd)#新密码一律采用md5加密,实在懒得再写加密子函数了 122 | try: 123 | cur.execute('update store SET passwd =%s WHERE user_name=%s',(new_passwdmd5[0],existed_name)) 124 | cur.connection.commit()#commit()提交事物!!!! 125 | print "##########################\n#Update Password Succeed!#\n##########################" 126 | except: 127 | print "Warning!Update Password Failed!" 128 | else: 129 | print "Warning!Wrong Password!" 130 | except: 131 | print "Warning!Update Password Failed!" 132 | 133 | #更新KEY子函数 134 | def updateKEY(existed_name): 135 | try: 136 | ori_KEY = raw_input("Please Enter Original KEY:") 137 | if str(check(existed_name)[0][4]) == md5(ori_KEY)[0] or str(check(existed_name)[0][4]) == sha1(ori_KEY): 138 | new_KEY = raw_input("Please Enter New KEY:") 139 | new_KEYmd5 = md5(new_KEY) 140 | #因为自己设计的加密解密函数和key绑定,随key变换而变化,所以需要更新下Plaintext 141 | secwd = getSecret(ori_KEY,str(check(existed_name)[0][3])) 142 | mix_update = encrypt(new_KEY, secwd) 143 | try: 144 | cur.execute('update store SET encrypt_words =%s WHERE user_name=%s',(mix_update,existed_name)) 145 | #因为自己设计的加密算法有key的加入,所以更换key后,加密内容也会改变,所以执行更新加密内容 146 | cur.execute('update store SET encrypt_password =%s WHERE user_name=%s',(new_KEYmd5[0],existed_name)) 147 | cur.connection.commit()#commit()提交事物!!!! 148 | print "#####################\n#Update KEY Succeed!#\n#####################" 149 | except: 150 | print "Warning!Update KEY Failed!" 151 | else: 152 | print "Warning!Wrong KEY!" 153 | except: 154 | print "Warning!Update KEY Failed!" 155 | 156 | #删除用户子函数 157 | def DeleteUser(name_req): 158 | try: 159 | cur.execute('delete FROM store WHERE user_name=%s',(name_req)) 160 | cur.connection.commit()#commit()提交事物!!!! 161 | print "######################\n#Delete User Succeed!#\n######################" 162 | except: 163 | print "Warning!Delete User Failed!" 164 | 165 | #用户登录&更新子函数 166 | def LogIn(name_req,keywords_req): 167 | 168 | if str(check(name_req)[0][2]) == md5(keywords_req)[0] or str(check(name_req)[0][2]) == sha1(keywords_req): 169 | print "-------------------------------Welcome %s-------------------------------"%name_req 170 | 171 | while 1: 172 | print "-------------------------------%s:What's Next?-------------------------------"%name_req 173 | check_update = raw_input("Update Plaintext-1 View Plaintext-2 Update Password-3 Update KEY-4 Log out-5 Delete User-6\nYour Choice: ") 174 | if check_update =='1': 175 | key_encrypt = raw_input("KEY:") 176 | if sha1(key_encrypt)==str(check(name_req)[0][4]) or md5(key_encrypt)[0] ==str(check(name_req)[0][4]): 177 | #显示一下以前存储的内容,看看需不需要更新 178 | print "Original Plaintext:%s"%(getSecret(key_encrypt,str(check(name_req)[0][3]))) 179 | new_plaintext = raw_input("New Plaintext:") 180 | new_mix = encrypt(key_encrypt, new_plaintext) 181 | try: 182 | cur.execute('update store set encrypt_words=%s WHERE user_name=%s',(new_mix,name_req)) 183 | cur.connection.commit()#commit()提交事物!!!! 184 | print "###########################\n#Update Plaintext Succeed!#\n###########################" 185 | except: 186 | print "Warning!Update Plaintext Failed!" 187 | 188 | else: 189 | print "Warning!Wrong KEY!" 190 | 191 | elif check_update =='2': 192 | key_encrypt = raw_input("KEY:") 193 | if str(check(name_req)[0][4]) == md5(key_encrypt)[0] or str(check(name_req)[0][4]) == sha1(key_encrypt): 194 | try: 195 | secwd = getSecret(key_encrypt,str(check(name_req)[0][3])) 196 | print "Secret Words:%s"%(secwd) 197 | except: 198 | print "Warning!Get Secret Words Failed!" 199 | else: 200 | print "Warning!Wrong KEY!" 201 | 202 | elif check_update =='3': 203 | updatePasswd(name_req) 204 | 205 | elif check_update =='4': 206 | updateKEY(name_req) 207 | 208 | elif check_update=='5': 209 | break 210 | elif check_update=='6': 211 | DeleteUser(name_req) 212 | break 213 | 214 | else: 215 | print "Warning!Something Wrong in Your Choice!" 216 | 217 | 218 | 219 | else: 220 | print "Warning!Can't Find The User or Wrong Password!" 221 | 222 | 223 | def Store_Encrypt(): 224 | 225 | print '-------------------------------Store&Encrypt-------------------------------' 226 | name = raw_input('New User:') 227 | try: 228 | while name == check(name)[0][1].encode('utf-8'): 229 | print "Warning!The Name Already Exist!" 230 | print "-------------------------------Make Your Choice-------------------------------------" 231 | update = raw_input("Change Password-1 Create New User-2\nSelect Mode:") 232 | if update =='1': 233 | updatePasswd(name) 234 | break 235 | if update =='2': 236 | name = raw_input('New User:') 237 | 238 | 239 | except: 240 | keywords = raw_input('Set Password:') 241 | print "-------------------------------Password Encrypt Algorithm-------------------------------------" 242 | cho = raw_input('MD5-1 SHA1-2\nSelect Algorithm:') 243 | 244 | print "-------------------------------What's Next?-------------------------------------" 245 | kc=raw_input("Store Encrypt Plaintext-1 Maybe Next Time-2\nYour Choice:") 246 | 247 | if kc=='1': 248 | key_content = raw_input('Please Design Your KEY:') 249 | content_key = raw_input('Plaintext:') 250 | 251 | else: 252 | key_content ='123456' 253 | content_key = 'Default Storage' 254 | print "Default KEY '123456'\nDefault Plaintext 'Default Storage'" 255 | 256 | PasswdSecretWD_encrypt(name,keywords,cho,key_content,content_key) 257 | #主函数 258 | def Main(): 259 | 260 | 261 | while 1: 262 | Init() 263 | print "-------------------------------Mode Choice-------------------------------------" 264 | ty = raw_input('Store&Encrypt-1 Login&View&Update&Delete-2 Quit System-3 Clear Database-4\nSelect Mode:') 265 | 266 | if ty == '1': 267 | Store_Encrypt() 268 | 269 | if ty == '2': 270 | print '-------------------------------Login&View&Update&Delete-------------------------------' 271 | name_req = raw_input('User:') 272 | keywords_req = raw_input('Password:') 273 | try: 274 | LogIn(name_req,keywords_req) 275 | except: 276 | print "Warning!Can't Find The User or Wrong Password!" 277 | 278 | if ty == '3': 279 | print '-------------------------------Quit The System-------------------------------' 280 | break 281 | 282 | if ty == '4': 283 | print "-------------------------------Warning!ALL Data Will Be Wiped!-------------------------------" 284 | 285 | sure = raw_input('Confirm-Y Quit-N\nYour Choice:') 286 | if sure.upper() =='Y': 287 | try: 288 | cur.execute('drop database MD5_Storetest') 289 | print '-------------------------------Wipe Database Succeed-------------------------------' 290 | print "-------------------------------What's Next?-------------------------------" 291 | new_database = raw_input('Create New Database-Y Quit-N\nYour Choice:') 292 | if new_database.upper() == 'Y': 293 | Init() 294 | else: 295 | break 296 | except: 297 | print 'Warning!Wipe Database Failed!' 298 | else: 299 | print '-------------------------------Operation Aborted-------------------------------' 300 | 301 | 302 | 303 | #程序入口 304 | if __name__ == '__main__': 305 | 306 | try: 307 | conn = pymysql.connect(host = '127.0.0.1',user='root',passwd='A089363b',db='mysql',charset='utf8') 308 | #与数据库建立连接 309 | cur = conn.cursor() 310 | #实例化光标 311 | print "-------------------------------SQL Connection Succeed-------------------------------" 312 | 313 | except: 314 | print "Warning!SQL Connection Failed!" 315 | 316 | #创建属于自己的伪码表,用于自己的加密算法服务 317 | list_key = ['G','h','S','2','M','a','m'] 318 | Main() 319 | 320 | try: 321 | cur.close() 322 | conn.close() 323 | print "-------------------------------SQL Connection Closed-------------------------------" 324 | print "-------------------------------Over-------------------------------" 325 | except: 326 | print "Warning!Can't Close Connection!" -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/Tutorial_N-gram概括内容.md: -------------------------------------------------------------------------------- 1 | ***From [利用N-Gram模型概括数据(Python描述)](http://blog.csdn.net/mrlevo520/article/details/52149545)*** 2 | 3 | Python 2.7 4 | IDE PyCharm 5.0.3 5 | 6 | --- 7 | 8 | 9 | 10 | 讲在开头 11 | ---- 12 | 此文需要用到的相关知识包括数据清洗,正则表达式,字典,列表等。不然可能有点费劲。 13 | ---------- 14 | 15 | 16 | 什么是N-Gram模型? 17 | ---------- 18 | 19 | > 在自然语言里有一个模型叫做n-gram,表示文字或语言中的n个连续的单词组成序列。在进行自然语言分析时,使用n-gram或者寻找常用词组,可以很容易的把一句话分解成若干个文字片段。摘自Python网络数据采集[RyanMitchell著] 20 | 21 | 简单来说,就是找到核心主题词,那怎么算核心主题词呢,一般而言,重复率也就是提及次数最多的也就是最需要表达的就是核心词。下面的例子也就从这个开始展开 22 | 23 | ---------- 24 | 25 | 临时补充 26 | ---- 27 | 在栗子中出现,这里拿出来单独先试一下效果 28 | 29 | 1.string.punctuation获取所有标点符号,和strip搭配使用 30 | ``` 31 | import string 32 | list = ['a,','b!','cj!/n'] 33 | item=[] 34 | for i in list: 35 | i =i.strip(string.punctuation) 36 | item.append(i) 37 | print item 38 | ``` 39 | 40 | ``` 41 | ['a', 'b', 'cj!/n'] 42 | 43 | ``` 44 | 45 | ---------- 46 | 47 | 2.operator.itemgetter() 48 | operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号(即需要获取的数据在对象中的序号) 49 | 50 | 栗子 51 | ``` 52 | import operator 53 | dict_={'name1':'2', 54 | 'name2':'1'} 55 | 56 | print sorted(dict_.items(),key=operator.itemgetter(0),reverse=True) 57 | #dict_.items(),键值对 58 | ``` 59 | 60 | ``` 61 | [('name2', '1'), ('name1', '2')] 62 | ``` 63 | 64 | 当然,你可以直接直接使用这个 65 | 66 | ``` 67 | dict_={'name1':'2', 68 | 'name2':'1'} 69 | print sorted(dict_.iteritems(),key=lambda x:x[1],reverse=True) 70 | ``` 71 | 72 | ---------- 73 | 74 | 75 | 2-gram 76 | ------ 77 | 就以两个关键词来说吧,上个栗子来进行备注讲解 78 | 79 | ``` 80 | import urllib2 81 | import re 82 | import string 83 | import operator 84 | 85 | def cleanText(input): 86 | input = re.sub('\n+', " ", input).lower() # 匹配换行,用空格替换换行符 87 | input = re.sub('\[[0-9]*\]', "", input) # 剔除类似[1]这样的引用标记 88 | input = re.sub(' +', " ", input) # 把连续多个空格替换成一个空格 89 | input = bytes(input)#.encode('utf-8') # 把内容转换成utf-8格式以消除转义字符 90 | #input = input.decode("ascii", "ignore") 91 | return input 92 | 93 | def cleanInput(input): 94 | input = cleanText(input) 95 | cleanInput = [] 96 | input = input.split(' ') #以空格为分隔符,返回列表 97 | 98 | 99 | for item in input: 100 | item = item.strip(string.punctuation) # string.punctuation获取所有标点符号 101 | 102 | if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'): #找出单词,包括i,a等单个单词 103 | cleanInput.append(item) 104 | return cleanInput 105 | 106 | def getNgrams(input, n): 107 | input = cleanInput(input) 108 | 109 | output = {} # 构造字典 110 | for i in range(len(input)-n+1): 111 | ngramTemp = " ".join(input[i:i+n])#.encode('utf-8') 112 | if ngramTemp not in output: #词频统计 113 | output[ngramTemp] = 0 #典型的字典操作 114 | output[ngramTemp] += 1 115 | return output 116 | 117 | #方法一:对网页直接进行读取 118 | content = urllib2.urlopen(urllib2.Request("http://pythonscraping.com/files/inaugurationSpeech.txt")).read() 119 | #方法二:对本地文件的读取,测试时候用,因为无需联网 120 | #content = open("1.txt").read() 121 | ngrams = getNgrams(content, 2) 122 | sortedNGrams = sorted(ngrams.items(), key = operator.itemgetter(1), reverse=True) #=True 降序排列 123 | print(sortedNGrams) 124 | 125 | ``` 126 | 127 | ``` 128 | [('of the', 213), ('in the', 65), ('to the', 61), ('by the', 41), ('the constitution', 34),,,巴拉巴拉一堆 129 | ``` 130 | **上述栗子作用在于抓到2连接词的频率大小来排序的,但是这并不是我们想要的,你说这出现两百多次的 of the 有个猫用啊,所以,我们要进行对这些连接词啊介词啊的剔除工作。** 131 | 132 | ---------- 133 | 134 | Deeper 135 | ------ 136 | 137 | > 完整代码和测试图都在同级目录下的`2_gram.ipynb`,如要测试请手动下载工程,然后运行jupyter即可,不知道jupyter?百度啊,自己装 138 | 139 | ``` 140 | # -*- coding: utf-8 -*- 141 | import urllib2 142 | 143 | import re 144 | import string 145 | import operator 146 | 147 | #剔除常用字函数 148 | def isCommon(ngram): 149 | commonWords = ["the", "be", "and", "of", "a", "in", "to", "have", 150 | "it", "i", "that", "for", "you", "he", "with", "on", "do", "say", 151 | "this", "they", "is", "an", "at", "but","we", "his", "from", "that", 152 | "not", "by", "she", "or", "as", "what", "go", "their","can", "who", 153 | "get", "if", "would", "her", "all", "my", "make", "about", "know", 154 | "will","as", "up", "one", "time", "has", "been", "there", "year", "so", 155 | "think", "when", "which", "them", "some", "me", "people", "take", "out", 156 | "into", "just", "see", "him", "your", "come", "could", "now", "than", 157 | "like", "other", "how", "then", "its", "our", "two", "more", "these", 158 | "want", "way", "look", "first", "also", "new", "because", "day", "more", 159 | "use", "no", "man", "find", "here", "thing", "give", "many", "well"] 160 | 161 | if ngram in commonWords: 162 | return True 163 | else: 164 | return False 165 | 166 | def cleanText(input): 167 | input = re.sub('\n+', " ", input).lower() # 匹配换行用空格替换成空格 168 | input = re.sub('\[[0-9]*\]', "", input) # 剔除类似[1]这样的引用标记 169 | input = re.sub(' +', " ", input) # 把连续多个空格替换成一个空格 170 | input = bytes(input)#.encode('utf-8') # 把内容转换成utf-8格式以消除转义字符 171 | #input = input.decode("ascii", "ignore") 172 | return input 173 | 174 | def cleanInput(input): 175 | input = cleanText(input) 176 | cleanInput = [] 177 | input = input.split(' ') #以空格为分隔符,返回列表 178 | 179 | 180 | for item in input: 181 | item = item.strip(string.punctuation) # string.punctuation获取所有标点符号 182 | 183 | if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'): #找出单词,包括i,a等单个单词 184 | cleanInput.append(item) 185 | return cleanInput 186 | 187 | def getNgrams(input, n): 188 | input = cleanInput(input) 189 | 190 | output = {} # 构造字典 191 | for i in range(len(input)-n+1): 192 | ngramTemp = " ".join(input[i:i+n])#.encode('utf-8') 193 | 194 | if isCommon(ngramTemp.split()[0]) or isCommon(ngramTemp.split()[1]): 195 | pass 196 | else: 197 | if ngramTemp not in output: #词频统计 198 | output[ngramTemp] = 0 #典型的字典操作 199 | output[ngramTemp] += 1 200 | return output 201 | 202 | #获取核心词在的句子 203 | def getFirstSentenceContaining(ngram, content): 204 | #print(ngram) 205 | sentences = content.split(".") 206 | for sentence in sentences: 207 | if ngram in sentence: 208 | return sentence 209 | return "" 210 | 211 | #方法一:对网页直接进行读取 212 | content = urllib2.urlopen(urllib2.Request("http://pythonscraping.com/files/inaugurationSpeech.txt")).read() 213 | #对本地文件的读取,测试时候用,因为无需联网 214 | #content = open("1.txt").read() 215 | ngrams = getNgrams(content, 2) 216 | sortedNGrams = sorted(ngrams.items(), key = operator.itemgetter(1), reverse=True) # reverse=True 降序排列 217 | print(sortedNGrams) 218 | for top3 in range(3): 219 | print "###"+getFirstSentenceContaining(sortedNGrams[top3][0],content.lower())+"###" 220 | ``` 221 | 222 | ``` 223 | [('united states', 10), ('general government', 4), ('executive department', 4), ('legisltive bojefferson', 3), ('same causes', 3), ('called upon', 3), ('chief magistrate', 3), ('whole country', 3), ('government should', 3),,,,巴拉巴拉一堆 224 | 225 | ### the constitution of the united states is the instrument containing this grant of power to the several departments composing the government### 226 | ### the general government has seized upon none of the reserved rights of the states### 227 | ### such a one was afforded by the executive department constituted by the constitution### 228 | 229 | ``` 230 | 从上述栗子我们可以看出,我们对有用词进行了删选,去掉了连接词,取出核心词排序,然后再把包含核心词的句子抓出来,这里我只是抓了前三句,对于有两三百个句子的文章,用三四句话概括起来,我想还是比较神奇的。 231 | 232 | 233 | 234 | ---- 235 | 236 | ## show data 237 | 238 | > 词云都玩腻了,详见另一个项目的词云:[[MrLevo520-网易云音乐爬取分析](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90)] 239 | 240 | ![](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-N-gram%E6%A6%82%E6%8B%AC%E5%85%A8%E6%96%87/save.png?raw=true) 241 | 242 | ---------- 243 | 244 | 最后 245 | -- 246 | > ​ 材料来自于Python网络数据采集第八章,但是代码是python3.x的,而且有一些代码案例上跑不出来,所以整理一下,自己修改了一些代码片段,才跑出书上的效果。 247 | 248 | ​ 249 | 250 | ---------- 251 | 252 | 致谢 253 | -- 254 | 255 | - Python网络数据采集[Ryan Mitchell著][人民邮电出版社] 256 | - [python strip()函数 介绍](http://www.jb51.net/article/37287.htm) 257 | - [Python中的sorted函数以及operator.itemgetter函数](http://www.cnblogs.com/100thMountain/p/4719503.html) -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2013.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2013.txt -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2014.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2014.txt -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2015.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2015.txt -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2016.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/Obama's dinner speech 2016.txt -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/README.md: -------------------------------------------------------------------------------- 1 | - Python 3.6 2 | - Jupyter Notebook 5.0 3 | - ECharts 4 | 5 | ---------- 6 | 7 | 起因 8 | -- 9 | > DT君Python精英群作业,学习使用n-gram,分析了奥巴马第二任期内4次白宫记者招待会晚宴演讲。 10 | 11 | ---------- 12 | 13 | 目的 14 | -- 15 | 1. 学习调试n-gram 16 | 2. 使用ECharts进行结果展示 17 | 3. 使用1-gram分析4次演讲的听众反映,(Laughter and Applause) 18 | 19 | ---------- 20 | 21 | 数据说明及展示 22 | ---- 23 | 24 | 演讲记录中会根据实际情况标注听众laughter和applause的情况。使用1-gram简单统计,能够得出段子手奥巴马的2016年演讲“笑果”最好。 25 | 26 | 首先,2016年演讲中的laughter及applause次数均为历年之最。 27 | 28 | ![听众反映计数](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%90%AC%E4%BC%97%E5%8F%8D%E6%98%A0%E8%AE%A1%E6%95%B0.png) 29 | 30 | 再考虑到每年演讲长度不同: 31 | 32 | ![字数](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/wordscount.png) 33 | 34 | 我们再计算laughter及applause的频次(即平均多少个words会有一次laughter或applause),也会发现2016年演讲的“笑果”最密集。 35 | 36 | ![听众反映频次统计](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%90%AC%E4%BC%97%E5%8F%8D%E6%98%A0%E9%A2%91%E6%AC%A1%E7%BB%9F%E8%AE%A1.png) 37 | 38 | 39 | ---------- 40 | 41 | 42 | 代码及文本数据 43 | ---- 44 | 45 | 均包含在本文件夹中。 46 | 47 | ---------- 48 | 49 | 参考 50 | ---- 51 | - MrLevo520 52 | - 《Python网络数据采集》 53 | - 文本来源 https://obamawhitehouse.archives.gov/ 54 | 55 | -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/ZLFlyApple-ngram/n-gram.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "模块测试" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 2, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "['a', 'b', 'cj!/n']\n" 20 | ] 21 | } 22 | ], 23 | "source": [ 24 | "import string\n", 25 | "list = [',,a,','b!','cj!/n']\n", 26 | "item = []\n", 27 | "for i in list:\n", 28 | " i = i.strip(string.punctuation)\n", 29 | " #在循环体中用item.strip(string.punctuation) 对内容中的所有单词进行清洗,单词两端\n", 30 | " #的任何标点符号都会被去掉,但带连字符的单词(连字符在单词内部)仍然会保留。\n", 31 | " item.append(i)\n", 32 | "print(item)" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 11, 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "name": "stdout", 42 | "output_type": "stream", 43 | "text": [ 44 | "[('name1', '2'), ('name2', '1'), ('name3', '2')]\n" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "import operator\n", 50 | "dict_ = {'name1':'2','name2':'1','name3':'2'}\n", 51 | "print(sorted(dict_.items(),key=operator.itemgetter(0,1),reverse=False))#排序" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "## n-gram" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 143, 64 | "metadata": { 65 | "collapsed": true 66 | }, 67 | "outputs": [], 68 | "source": [ 69 | "from urllib.request import urlopen\n", 70 | "\n", 71 | "import re\n", 72 | "import string\n", 73 | "import operator\n", 74 | "\n", 75 | "def isCommon(ngram):\n", 76 | " commonWords = [\"the\", \"be\", \"and\", \"of\", \"a\", \"in\", \"to\", \"have\", \"it\",\n", 77 | " \"i\", \"that\", \"for\", \"you\", \"he\", \"with\", \"on\", \"do\", \"say\", \"this\",\n", 78 | " \"they\", \"is\", \"an\", \"at\", \"but\",\"we\", \"his\", \"from\", \"that\", \"not\",\n", 79 | " \"by\", \"she\", \"or\", \"as\", \"what\", \"go\", \"their\",\"can\", \"who\", \"get\",\n", 80 | " \"if\", \"would\", \"her\", \"all\", \"my\", \"make\", \"about\", \"know\", \"will\",\n", 81 | " \"as\", \"up\", \"one\", \"time\", \"has\", \"been\", \"there\", \"year\", \"so\",\n", 82 | " \"think\", \"when\", \"which\", \"them\", \"some\", \"me\", \"people\", \"take\",\n", 83 | " \"out\", \"into\", \"just\", \"see\", \"him\", \"your\", \"come\", \"could\", \"now\",\n", 84 | " \"than\", \"like\", \"other\", \"how\", \"then\", \"its\", \"our\", \"two\", \"more\",\n", 85 | " \"these\", \"want\", \"way\", \"look\", \"first\", \"also\", \"new\", \"because\",\n", 86 | " \"day\", \"more\", \"use\", \"no\", \"man\", \"find\", \"here\", \"thing\", \"give\",\n", 87 | " #\"laughter\", \"applause\", \n", 88 | " \"many\", \"well\", \"said\", \"was\", \"are\", \"were\", \"had\"]\n", 89 | " #############################################\n", 90 | " for word in ngram:\n", 91 | " if word in commonWords:\n", 92 | " return True\n", 93 | " return False\n", 94 | "\n", 95 | "\n", 96 | "\n", 97 | "\n", 98 | "def cleanInput(input):\n", 99 | " \n", 100 | " input = re.sub('\\n+', \" \", input).lower()\n", 101 | " input = re.sub('\\[[0-9]*\\]', \"\", input)\n", 102 | " input = re.sub(' +', \" \", input)\n", 103 | " input = bytes(input, \"UTF-8\")\n", 104 | " input = input.decode(\"ascii\", \"ignore\")\n", 105 | " \n", 106 | " cleanInput = []\n", 107 | " input = input.split(' ')\n", 108 | " for item in input:\n", 109 | " item = item.strip(string.punctuation)\n", 110 | " if len(item) > 1 or (item.lower() == 'a' or item.lower() == 'i'):\n", 111 | " cleanInput.append(item)\n", 112 | " return cleanInput\n", 113 | "\n", 114 | "def ngrams(input, n):\n", 115 | " input = cleanInput(input)\n", 116 | " print(\"Total words: %d\"%len(input))\n", 117 | " output = {}\n", 118 | " for i in range(len(input)-n+1):\n", 119 | " ngramTemp = \" \".join(input[i:i+n])\n", 120 | " #print(ngramTemp)\n", 121 | " if isCommon(ngramTemp.split()):\n", 122 | " #print(\"in\")\n", 123 | " pass\n", 124 | " else:\n", 125 | " if ngramTemp not in output:\n", 126 | " output[ngramTemp] = 0\n", 127 | " output[ngramTemp] += 1\n", 128 | " return (output,len(input))\n", 129 | "\n", 130 | "#content = str(\n", 131 | "# urlopen(\"http://pythonscraping.com/files/inaugurationSpeech.txt\").read(),\n", 132 | "# 'utf-8')\n", 133 | "\n", 134 | " " 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 149, 140 | "metadata": {}, 141 | "outputs": [ 142 | { 143 | "name": "stdout", 144 | "output_type": "stream", 145 | "text": [ 146 | "2013\n", 147 | "Total words: 2232\n", 148 | "laughter 61 36\n", 149 | "applause 26 85\n", 150 | "2014\n", 151 | "Total words: 2096\n", 152 | "laughter 60 34\n", 153 | "applause 22 95\n", 154 | "2015\n", 155 | "Total words: 2302\n", 156 | "laughter 79 29\n", 157 | "applause 32 71\n", 158 | "2016\n", 159 | "Total words: 2809\n", 160 | "laughter 99 28\n", 161 | "applause 52 54\n" 162 | ] 163 | } 164 | ], 165 | "source": [ 166 | "for i in range(4):\n", 167 | " year = 2013+i\n", 168 | " print(year)\n", 169 | " content=open(\"Obama's dinner speech %d.txt\"%year).read()\n", 170 | " (ngram,length)=(ngrams(content, 1))\n", 171 | " sortedNGrams = sorted(ngram.items(), key = operator.itemgetter(1), reverse=True)\n", 172 | " ngram.clear()\n", 173 | " for top2 in range(2):\n", 174 | " print(sortedNGrams[top2][0],sortedNGrams[top2][1],length//sortedNGrams[top2][1])" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 136, 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "name": "stdout", 184 | "output_type": "stream", 185 | "text": [ 186 | "2013\n", 187 | "Total words: 2232\n", 188 | "('white house', 4)\n", 189 | "('house correspondents', 3)\n", 190 | "('charm offensive', 3)\n", 191 | "('groucho marx', 3)\n", 192 | "2014\n", 193 | "Total words: 2096\n", 194 | "('white house', 4)\n", 195 | "('house correspondents', 4)\n", 196 | "('correspondents association', 4)\n", 197 | "2015\n", 198 | "Total words: 2302\n", 199 | "('white house', 5)\n", 200 | "('ted cruz', 5)\n", 201 | "('house correspondents', 4)\n", 202 | "('jeb bush', 3)\n", 203 | "('weve got', 3)\n", 204 | "('anger translator', 3)\n", 205 | "2016\n", 206 | "Total words: 2809\n", 207 | "('white house', 7)\n", 208 | "('free press', 5)\n", 209 | "('little bit', 4)\n", 210 | "('house correspondents', 3)\n", 211 | "('correspondents dinner', 3)\n", 212 | "('press corps', 3)\n" 213 | ] 214 | } 215 | ], 216 | "source": [ 217 | "for i in range(4):\n", 218 | " year = 2013+i\n", 219 | " print(year)\n", 220 | " content=open(\"Obama's dinner speech %d.txt\"%year).read()\n", 221 | " ngram=(ngrams(content, 2))\n", 222 | " sortedNGrams = sorted(ngram.items(), key = operator.itemgetter(1), reverse=True)\n", 223 | " ngram.clear()\n", 224 | " for top2 in range(20):\n", 225 | " if sortedNGrams[top2][1]>2:\n", 226 | " #print(type(sortedNGrams[top2]))\n", 227 | " print(sortedNGrams[top2])" 228 | ] 229 | } 230 | ], 231 | "metadata": { 232 | "kernelspec": { 233 | "display_name": "Python 3", 234 | "language": "python", 235 | "name": "python3" 236 | }, 237 | "language_info": { 238 | "codemirror_mode": { 239 | "name": "ipython", 240 | "version": 3 241 | }, 242 | "file_extension": ".py", 243 | "mimetype": "text/x-python", 244 | "name": "python", 245 | "nbconvert_exporter": "python", 246 | "pygments_lexer": "ipython3", 247 | "version": "3.6.1" 248 | } 249 | }, 250 | "nbformat": 4, 251 | "nbformat_minor": 2 252 | } 253 | -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/save.png -------------------------------------------------------------------------------- /MrLevo520-N-gram概括全文/simhei.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-N-gram概括全文/simhei.ttf -------------------------------------------------------------------------------- /MrLevo520-使用Git对自己项目进行管理/Tutorial_使用Git对自己项目进行管理.md: -------------------------------------------------------------------------------- 1 | ## 学习使用Git对自己项目进行管理 2 | 3 | ## First - 创建自己的仓库 4 | 5 | > 教程只需要看到分支管理之前的基本操作就行,知道如何提交自己的项目到github并进行版本管理 6 | 7 | - 墙裂建议自学从这里开始→_→[廖雪峰的git教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) 8 | 9 | 任务1:先自己建个仓库,然后提交一下自己的项目 10 | 11 | 12 | 13 | ## Second - fork项目并pull request 14 | 15 | > 提交自己认为满意的作业至furtherProject,提交方式请见提交方式.md文件 16 | 17 | - 过程先看一下这里→_→ *[Github上怎么修改别人的项目并且提交给原作者!图文并茂](http://blog.csdn.net/qq26787115/article/details/52133008)* 18 | 19 | 1. **注册好Github账号,最好使用outlook邮箱进行注册**,且需要验证下才能用 20 | 2. **fork项目,进入到别人的项目页下** 21 | 22 | ![这里写图片描述](http://img.blog.csdn.net/20170903112646542?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 23 | 24 | 3. **进入到自己的仓库可以看到从原作者项目中fork过来的项目,就是一个复刻版本,所有的都保持一致** 25 | 26 | ![这里写图片描述](http://img.blog.csdn.net/20170903112620790?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 27 | 28 | 4. **clone到自己本地,然后更新自己的文件夹到对应的项目的furtherProject文件夹下** 29 | 5. **需要提交的目录结构如下** 30 | 31 | ![这里写图片描述](http://img.blog.csdn.net/20170903112707161?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 32 | 33 | 6. **这里我写好了一个提交的版本,其实其余的什么你都不需要动,只需要提交自己的项目到对应的文件夹,至于怎么提交项目到自己的仓库,请参考廖雪峰的教程,提交规范请看提交方式.md说明文档** 34 | 35 | ![这里写图片描述](http://img.blog.csdn.net/20170903112718957?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 36 | 37 | 7. **然后pull request给原作者,意思是,我想要把这个版本和原作者版本进行合并,这样别人fork原作者的项目时候,你的项目也被fork了,相当于给原作者添砖加瓦** 38 | 39 | ![这里写图片描述](http://img.blog.csdn.net/20170903112734092?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 40 | 41 | 8. **如果是多次pull ,则会出现下面的现象,不用管,create pull request** 42 | 43 | ![这里写图片描述](http://img.blog.csdn.net/20170903112757567?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 44 | 45 | 9. **对自己pull的项目进行简单描述,不然原作者根本不知道你更新了啥** 46 | 47 | ![这里写图片描述](http://img.blog.csdn.net/20170903112808779?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 48 | 49 | 50 | 51 | --- 52 | 53 | 54 | 55 | > 这里是原始仓库角度,收到了贡献者的pull request 56 | 57 | ![这里写图片描述](http://img.blog.csdn.net/20170903112823067?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 58 | 59 | > 原作者会看到你自己pull的项目描述等,考虑是否将其merge到原项目 60 | 61 | ![这里写图片描述](http://img.blog.csdn.net/20170903112833444?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 62 | 63 | 64 | 65 | > ​ 进行pull request项目,就是告诉我,我这里有添加的新项目,想要合并到原始项目中,审核后我会进行merge,这样大家以后fork原始项目的时候,就会把你的新添加的project也fork过去。对于大家的操作,其实就是fork原项目,然后clone到本地,进行自己项目的相应更新,然后push到自己的仓库,然后再从自己的仓库pull request到原仓库,你自己的仓库任何改造都不会影响原仓库,如果你想贡献自己的一份延伸项目给别人也进行fork,那么,pull给我吧! 66 | 67 | 68 | 69 | 任务二:挑选以前做过的项目,根据撰写规范进行排版总结,发pull到原项目进行联合开发 70 | 71 | 72 | 73 | --- 74 | 75 | ## About Tool 76 | 77 | ### Markdown 编辑器 78 | 79 | > 个人推荐Typora,全平台兼容,至于md语法,百度下,很简单 80 | 81 | 82 | 83 | ![typora.gif](http://upload-images.jianshu.io/upload_images/2671133-86de28680ae3ea37.gif?imageMogr2/auto-orient/strip) 84 | 85 | 86 | 87 | ### git GUI 88 | 89 | > 对于win的用户,推荐使用 [git bash](https://git-for-windows.github.io/) 90 | > 91 | > 对于mac用户,自带终端即可使用 92 | > 93 | > 当然对于两种平台,首先先装一下git,如何装git,请自行百度 94 | > 95 | > BTW,sourceTree也是个不错的git工具,也是双平台 96 | 97 | -------------------------------------------------------------------------------- /MrLevo520-有道翻译小软件/Tutorial_有道翻译小软件.md: -------------------------------------------------------------------------------- 1 | ***From [用python做个翻译小软件吧~](http://blog.csdn.net/mrlevo520/article/details/51674188)*** 2 | 3 | - Python 2.7.13 4 | - IDE Pycharm 5.0.3 5 | - macOS 10.12.1 6 | 7 | 8 | 9 | ------ 10 | 11 | ## 前言 12 | 13 | > ​ 花了一点时间,半抄半写半修改的写了第一个能用的python小程序,作用是在IDE端模拟有道词典的访问,效果如下图所示,不足之处在于,当输入的中英文字符串超过一定数量,会抛出中间代码,新手并不知道怎么处理,望知道的不吝赐教 14 | 15 | 16 | 17 | ------ 18 | 19 | ## 初阶:交互界面 20 | 21 | > 首先在jupyter或者pycharm中进行交互的操作,核心语句是使用raw_input捕获系统输入 22 | 23 | ### 效果图 24 | 25 | ![效果图](http://img.blog.csdn.net/20160614193811639) 26 | 27 | 28 | 29 | ### 代码 30 | 31 | ```python 32 | # -*- coding: utf-8 -*- 33 | 34 | import urllib2 35 | import urllib # python2.7才需要两个urllib 36 | import json 37 | while True: 38 | content = raw_input("请输入需要翻译的内容:") # 系统捕获输入,就是命令框会弹出提示,需要你进行手动输入 39 | if content == 'q': # 输入q退出while循环 40 | break 41 | 42 | url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" 43 | data = {} # 构造data,里面构造参数传入 44 | data['type'] = 'AUTO' 45 | data['i']=content 46 | data['doctype'] = 'json' 47 | data['xmlVersion'] = '1.8' 48 | data['keyfrom'] = 'fanyi.web' 49 | data['ue'] = 'UTF-8' 50 | data['action'] = 'FY_BY_ENTER' 51 | data['typoResult'] = 'true' 52 | 53 | data = urllib.urlencode(data).encode('utf-8') # 将构造的data编码 54 | req = urllib2.Request(url) # 向浏览器发出请求 55 | response = urllib2.urlopen(req, data) # 带参请求,返回执行结果 56 | html = response.read().decode('utf-8') 57 | # print(html) # 可以取消print的注释,查看其中效果,这边获取的结果是进行解析 58 | 59 | target = json.loads(html) # 以json形式载入获取到的html字符串 60 | 61 | print "翻译的内容是:"+target['translateResult'][0][0]['tgt'].encode('utf-8') 62 | 63 | 64 | # 请输入需要翻译的内容:test 65 | # 翻译的内容是:测试 66 | # 请输入需要翻译的内容:测试 67 | # 翻译的内容是:test 68 | # 请输入需要翻译的内容:q 69 | ``` 70 | 71 | ### 72 | 73 | 74 | 75 | **注意:**这里的data字典中的数据根据实际网页中数据为准,可能会不一样,具体操作,点击审查元素。或见小甲鱼54讲。 76 | 77 | 78 | 79 | ------ 80 | 81 | 82 | 83 | ## 进阶:做成gui 84 | 85 | > 离实用还差那么两步,第一步是先做成GUI 86 | 87 | 可以参考以前我写的: [Python基于Tkinter的二输入规则器(乞丐版)](http://blog.csdn.net/mrlevo520/article/details/51812096) 88 | 89 | 90 | 91 | ### 界面效果 92 | 93 | 94 | 95 | ![这里写图片描述](http://img.blog.csdn.net/20170723211730352?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 96 | 97 | 98 | 99 | ### 代码 100 | 101 | ```Python 102 | # -*- coding: utf-8 -*- 103 | ''' 104 | Author:哈士奇说喵 105 | UDate: 2016.7.21 106 | ''' 107 | from Tkinter import * 108 | import difflib 109 | import urllib2 110 | import urllib # python2.7才需要两个urllib 111 | import json 112 | 113 | 114 | # ----------------------主框架部分---------------------- 115 | 116 | root = Tk() 117 | root.title('翻译GUI&beta1') 118 | root.geometry() 119 | Label_root=Label(root) 120 | 121 | #-----------------------定义规则------------------------ 122 | 123 | def translate(content): 124 | 125 | url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null" 126 | data = {} # 构造data,里面构造参数传入 127 | data['type'] = 'AUTO' 128 | data['i']=content 129 | data['doctype'] = 'json' 130 | data['xmlVersion'] = '1.8' 131 | data['keyfrom'] = 'fanyi.web' 132 | data['ue'] = 'UTF-8' 133 | data['action'] = 'FY_BY_ENTER' 134 | data['typoResult'] = 'true' 135 | 136 | data = urllib.urlencode(data).encode('utf-8') # 将构造的data编码 137 | req = urllib2.Request(url) # 向浏览器发出请求 138 | response = urllib2.urlopen(req, data) # 带参请求,返回执行结果 139 | html = response.read().decode('utf-8') 140 | # print(html) # 可以取消print的注释,查看其中效果,这边获取的结果是进行解析 141 | 142 | target = json.loads(html) # 以json形式载入获取到的html字符串 143 | 144 | #print u"翻译的内容是:"+target['translateResult'][0][0]['tgt'] 145 | return target['translateResult'][0][0]['tgt'].encode('utf-8') 146 | 147 | 148 | 149 | #还可以继续增加规则函数,只要是两输入的参数都可以 150 | #----------------------触发函数----------------------- 151 | 152 | def Answ():# 规则函数 153 | 154 | Ans.insert(END,"翻译 %s: "%var_first.get().encode('utf-8') + translate(var_first.get().encode('utf-8'))) 155 | 156 | def Clea():#清空函数 157 | input_num_first.delete(0,END)#这里entry的delect用0 158 | Ans.delete(0,END)#text中的用0.0 159 | 160 | 161 | #----------------------输入选择框架-------------------- 162 | frame_input = Frame(root) 163 | Label_input=Label(frame_input, text='请输入需要翻译的内容', font=('',15)) 164 | var_first = StringVar() 165 | input_num_first = Entry(frame_input, textvariable=var_first) 166 | 167 | 168 | #---------------------计算结果框架--------------------- 169 | frame_output = Frame(root) 170 | Label_output=Label(frame_output, font=('',15)) 171 | Ans = Listbox(frame_output, height=5,width=30) #text也可以,Listbox好处在于换行 172 | 173 | 174 | #-----------------------Button----------------------- 175 | 176 | calc = Button(frame_output,text='翻译', command=Answ) 177 | cle = Button(frame_output,text='清空', command=Clea) 178 | 179 | 180 | 181 | Label_root.pack(side=TOP) 182 | frame_input.pack(side=TOP) 183 | Label_input.pack(side=LEFT) 184 | 185 | input_num_first.pack(side=LEFT) 186 | 187 | frame_output.pack(side=TOP) 188 | Label_output.pack(side=LEFT) 189 | calc.pack(side=LEFT) 190 | cle.pack(side=LEFT) 191 | Ans.pack(side=LEFT) 192 | 193 | #-------------------root.mainloop()------------------ 194 | 195 | root.mainloop() 196 | ``` 197 | 198 | --- 199 | ##高阶:发布应用 200 | > 如何在小伙伴面前装B才是我学习的动力,哈哈哈 201 | > 教程更新如下:[将自己的python程序打包成.exe/.app(秀同学一脸呐)](http://blog.csdn.net/mrlevo520/article/details/51840217) 202 | 203 | ------ 204 | 205 | ## Pay Attention 206 | 207 | - python3的用户注意url包的使用和python2是有区别的,请根据实际需求自行百度 208 | 209 | 210 | 211 | - Python如果操作频率太快或者网页限制机器人对此的访问,则需要修改head了,修改代码后.当然每个电脑的user都不一样,具体去审查元素查看。 212 | 213 | ``` 214 | req = urllib2.Request(url) # 生成对象 215 | # 添加如下一行代码; 216 | req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'),这样就可以伪装成人类啦 217 | ``` 218 | 219 | 220 | 221 | - 当然也可以添加延时模块, 即可限定访问时间。 222 | 223 | ``` 224 | import time #添加延时模块 225 | time.sleep(1)#休息1秒钟再进行操作 226 | ``` 227 | 228 | 229 | 230 | - python3的同学需要Tkinter改成小写,还有就是注意编码部分的转化,具体建议可见[Python基于Tkinter的二输入规则器(乞丐版)](http://blog.csdn.net/mrlevo520/article/details/51812096)中的参考建议 231 | 232 | - mac的同学可能遇到tkinter无法输入中文问题,可能是由tkinter版本过低导致,解决方案参考:[MAC 系统中,Tkinter 无法用 中文输入法 输入中文](http://www.jianshu.com/p/f9e30bdc5806) 233 | 234 | 235 | --- 236 | ## 更新 237 | 238 | 239 | - 2016.6.14 初次撰写 240 | - 2017.7.22 重新排版,增加tkinter等 241 | -------------------------------------------------------------------------------- /MrLevo520-有道翻译小软件/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-简易GUI计算器/Tutorial_简易GUI计算器.md: -------------------------------------------------------------------------------- 1 | ***From [Python基于Tkinter的二输入规则器(乞丐版)](http://blog.csdn.net/mrlevo520/article/details/51812096)*** 2 | 3 | - Python 2.7 4 | - IDE Pycharm 5.0.3 5 | 6 | ------ 7 | 8 | ``` 9 | 有想法就去做,等等等等就没机会了 10 | ``` 11 | 12 | 13 | 14 | ------ 15 | 16 | ## 起因 17 | 18 | > ​ 昨天接触了Tkinter框架,之后就迫不及待的想写个计算器出来,结果呢,可想而知了,当初自己犟脾气,掌握几个语法后就想什么都不参考写自己的一段四则运算器出来,结果。。。。。。花了我一天时间,我竟然歪打正着写了个规则器出来窝草。。。。 19 | 20 | ------ 21 | 22 | 23 | 24 | ## 对比 25 | 26 | > ​ 贴个图,别人家的计算器是这样的;而且用了五十行,说的貌似很了不起的样子(老纸的规则器,只要40-就可以!不算上Scrollbar,分割子框架这类的) 27 | 28 | ![你们认为的‘计算器’](http://upload-images.jianshu.io/upload_images/2671133-ddcfd9df5f6bb6cc?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 29 | 30 | > 大家计算器都一个模样,大家都是一个idea么?还是局限于计算机就应该这样,本小白不服! 31 | 32 | ![吼吼](http://upload-images.jianshu.io/upload_images/2671133-b7af6424ab0b8634?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 33 | 34 | ------ 35 | 36 | ## But 37 | 38 | > 我的规则器是这样的。。。。 39 | 40 | ![规则器](http://upload-images.jianshu.io/upload_images/2671133-334ebc80c9070bd1?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 41 | 我知道布局排丑了,不要在意这些细节好么0.0 42 | 43 | 44 | 45 | ## 说说优点 46 | 47 | 1,以计算器角度说,能完美实现计算,而且带标号,记录存储等功能,知道上一步计算结果。 48 | 49 | 2,最大的优点在于二输入,调用各种def的函数,而四则运算只是最简单的函数而已,比如说我又写了字符串连接函数,相似度比较函数等等,做个实例而已,大家可以大开脑洞 50 | 51 | ------ 52 | 53 | ## 缺点 54 | 55 | 1,需要键盘输入,与普通计算器按键输入不同 56 | 57 | 2,我的代码冗余量比较大,因为自己需要看懂,所以不像别的教程那样直接跟着lambda和pack,一长串的,不利于我们这种小白读。等我水平再高一些,或许我也会采用lambda,这样才够pythontic~ 58 | 59 | ------ 60 | 61 | ## 后续 62 | 63 | 1.本身在做分类聚类方面的课题,结合这个规则器,我完全可以把k-means中的k参数在交互界面上输入,这样就不用每次上程序里面改了!还有DBSCAN里面的Eps和MinPts也可以直接用这个框架!!想想有点小激动呢!(挖的坑不计其数) 64 | 65 | 2.需要优化下布局,尝试用grid来做,感觉pack里面参数略多啊。 66 | 67 | ------ 68 | 69 | ## 构思框架 70 | 71 | 放代码之前,先来设计思路,我设计了两个框架,输入和输出在两个框架上,这样便于写代码思路清晰,框架大概是这样的; 72 | ![框架结构](http://upload-images.jianshu.io/upload_images/2671133-a4236fa14580a55a?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 73 | 74 | ------ 75 | 76 | ## 代码 77 | 78 | 此代码(就算再烂)绝此一家,别无分店哈哈 79 | 80 | ``` 81 | #-------------------二输入规则计算器-------------------- 82 | #Author:哈士奇说喵(第一次署名有点怕) 83 | # -*- coding: utf-8 -*- 84 | from Tkinter import * 85 | import difflib 86 | #主框架部分 87 | root = Tk() 88 | root.title('乞丐版规则器0.0') 89 | root.geometry() 90 | Label_root=Label(root,text='规则运算(根框架)',font=('宋体',15)) 91 | 92 | #-----------------------定义规则------------------------ 93 | 94 | def Plus(a,b): 95 | return round(a+b, 2) 96 | 97 | def Sub(a,b): 98 | return round(a-b,2) 99 | 100 | def Mult(a,b): 101 | return round(a*b, 2) 102 | 103 | def Div(a,b): 104 | return round(a/b, 2) 105 | 106 | def P_str(a,b): 107 | return a+b 108 | 109 | def Rep(a,b): 110 | return difflib.SequenceMatcher(None,a,b).ratio() 111 | #difflib可以看看其中的定义,计算匹配率的 112 | 113 | #还可以继续增加规则函数,只要是两输入的参数都可以 114 | #----------------------触发函数----------------------- 115 | 116 | def Answ():#规则函数 117 | 118 | if lb.get(lb.curselection()).encode('utf-8') == '加': 119 | Ans.insert(END,'规则:+ ->'+str(Plus(float(var_first.get()),float(var_second.get())))) 120 | if lb.get(lb.curselection()).encode('utf-8')=='减': 121 | Ans.insert(END,'规则:- ->'+str(Sub(float(var_first.get()),float(var_second.get())))) 122 | if lb.get(lb.curselection()).encode('utf-8')=='乘': 123 | Ans.insert(END,'规则:x ->'+str(Mult(float(var_first.get()),float(var_second.get())))) 124 | if lb.get(lb.curselection()).encode('utf-8')=='除': 125 | Ans.insert(END,'规则:/ ->'+str(Div(float(var_first.get()),float(var_second.get())))) 126 | if lb.get(lb.curselection()).encode('utf-8')=='字符串连接': 127 | Ans.insert(END,'规则:字符串连接 ->' +P_str(var_first.get(),var_second.get()).encode('utf-8')) 128 | if lb.get(lb.curselection()).encode('utf-8')=='字符串相似度': 129 | Ans.insert(END,'规则:字符串相似度 ->'+str(Rep(var_first.get(),var_second.get()))) 130 | 131 | #添加规则后定义规则函数 132 | 133 | def Clea():#清空函数 134 | input_num_first.delete(0,END)#这里entry的delect用0 135 | input_num_second.delete(0,END) 136 | Ans.delete(0,END)#text中的用0.0 137 | 138 | 139 | #----------------------输入选择框架-------------------- 140 | frame_input = Frame(root) 141 | Label_input=Label(frame_input, text='(输入和选择框架)', font=('',15)) 142 | var_first = StringVar() 143 | var_second = StringVar() 144 | input_num_first = Entry(frame_input, textvariable=var_first) 145 | input_num_second = Entry(frame_input, textvariable=var_second) 146 | 147 | #---------------------选择运算规则--------------------- 148 | #还可以添加其他规则 149 | 150 | lb = Listbox(frame_input,height=4) 151 | list_item=['加', '减', '乘', '除','字符串连接','字符串相似度'] 152 | for i in list_item: 153 | lb.insert(END,i) 154 | 155 | #---------------------计算结果框架--------------------- 156 | frame_output = Frame(root) 157 | Label_output=Label(frame_output, text='(计算结果框架)', font=('',15)) 158 | Ans = Listbox(frame_output, height=5,width=30)#text也可以,Listbox好处在于换行 159 | 160 | 161 | #-----------------------Button----------------------- 162 | 163 | calc = Button(frame_output,text='计算', command=Answ) 164 | cle = Button(frame_output,text='清除', command=Clea) 165 | 166 | 167 | #---------------------滑动Scrollbar------------------- 168 | scr1 = Scrollbar(frame_input) 169 | lb.configure(yscrollcommand = scr1.set) 170 | scr1['command']=lb.yview 171 | 172 | scr2 = Scrollbar(frame_output) 173 | Ans.configure(yscrollcommand = scr2.set) 174 | scr2['command']=Ans.yview 175 | 176 | 177 | #-------------------------布局------------------------ 178 | #布局写在一块容易排版,可能我low了吧 179 | Label_root.pack(side=TOP) 180 | frame_input.pack(side=TOP) 181 | Label_input.pack(side=LEFT) 182 | 183 | input_num_first.pack(side=LEFT) 184 | lb.pack(side=LEFT) 185 | scr1.pack(side=LEFT,fill=Y) 186 | input_num_second.pack(side=RIGHT) 187 | 188 | frame_output.pack(side=TOP) 189 | Label_output.pack(side=LEFT) 190 | calc.pack(side=LEFT) 191 | cle.pack(side=LEFT) 192 | Ans.pack(side=LEFT) 193 | scr2.pack(side=LEFT,fill=Y) 194 | 195 | #-------------------root.mainloop()------------------ 196 | 197 | root.mainloop() 198 | ``` 199 | 200 | Tkinter还是比价好上手的,知道一些基本语法就可以实现自己想要的效果了,这里我把自己遇到的问题写一下,如果也有人遇到,恰好能帮助的话,我很荣幸。 201 | 202 | ------ 203 | 204 | ## 问题&解决 205 | 206 | 1.Q.button或插件不显示 207 | A.记得加上pack显示函数!!一般我都定义完了插件直接补上 pack函数 208 | 209 | ------ 210 | 211 | 2.Q.插件位置显示问题 212 | A.这个要看你的pack函数写在哪了,所以我一般直接写在最后,容易排序,比如side都是LEFT的话,就按先后顺序显示的 213 | 214 | ------ 215 | 216 | 3.Q.刚开始键入的被get之后,直接运算出错。 217 | A.结果是str类型,所以记得用float强制转换,不用int是因为int做除法时候不好使,需要float,切记(python2.7) 218 | 219 | 220 | 221 | ------ 222 | 223 | ## Pay Attention 224 | 225 | 1. 在自定义规则的时候,主要get抓到的数据类型和你的def里面的数据类型,保持一致。 226 | 227 | 228 | 1. 清空函数中,text和entry,listbox的delect清空不一样!比如被实例的是Listbox(Entry)的,那么清空是Obj.delect(0,END),而如果是Text的对象,那么就是Obj.delect(0.0,END),这个是我之前没想到的,只有实践过才记得住把。而且,用listbox好处在于计算一个值之后,下一个值自动换行,用text时候\n还不好使 229 | 230 | 231 | 1. 如果使用python3,会出现,no model name Tkinter,其实py3只是把它改成小写了,所以导入包的时候改成tkinter 小写就行 232 | 2. 出现点击运算符之后无法输出结果或者gui中文乱码问题,一般也是出现在python3的问题上,所以解决方案是吧encode('utf-8')删掉就可以了。 233 | 234 | ------ 235 | 236 | -------------------------------------------------------------------------------- /MrLevo520-简易GUI计算器/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/Tutorial_网易云音乐分析.md: -------------------------------------------------------------------------------- 1 | ***From- [数据向:我到底是谁的歌迷?](http://blog.csdn.net/mrlevo520/article/details/74908066)*** 2 | 3 | 4 | - MacOS Sierra 10.12.1 5 | - Python 2.7 6 | - selenium 3.4.3 7 | - phantomjs 忘了 8 | 9 | ---- 10 | 11 | ## 前言 12 | 13 | > 发现自己有时候比挖掘别人来的更加有意义,自己到底喜欢谁的歌,自己真的知道么?习惯不会骗你 14 | 15 | ----- 16 | 17 | ## 搭建爬虫环境 18 | 19 | ### 1.安装selenium 20 | 21 | ```shell 22 | pip install selenium 23 | # anaconda环境的可用conda install selenium 24 | # 网速不好的可用到https://pypi.python.org/pypi/selenium下载压缩包,解压后使用python setup.py install 25 | ``` 26 | 27 | 28 | 29 | ### 2.安装Phantomjs 30 | #### Mac版本 31 | 32 | ``` 33 | 步骤一下载包:去这里下载对应版本http://phantomjs.org/download.html 34 | 步骤二解压:双击就行,用unzip这都无所谓 35 | 步骤三切入路径:cd ~/Downloads/phantomjs-2.1.1-macosx/bin # 我下的路径的路径是download,版本不一,注意修改 36 | 步骤四:chmod +x phantomjs 37 | 步骤五: 配置环境,因为我装的的zsh,所以文件需要修改的是~/.zshrc这个文件,加上这句话export PATH="/Users/mrlevo/Downloads/phantomjs-2.1.1-macosx/bin/:$PATH",然后source ~/.zshrc 即可生效(没用zsh的同学,直接修改的文件时~/.bash_profile,添加内容和上述一致) 38 | 查看是否生效:phantomjs -v # 有信息如 2.1.1 则生效 39 | ``` 40 | 41 | mac若遇到问题请参考[PhantomJS 安装](https://segmentfault.com/a/1190000009020535) 42 | 43 | #### Win版本 44 | 45 | ``` 46 | 官网http://phantomjs.org/下载PhantomJS解压后如下图所示: 47 | ``` 48 | 49 | ![image](http://img.blog.csdn.net/20150819192938843) 50 | 51 | 52 | 53 | > 调用时可能会报错“**Unable to start phantomjs with ghostdriver**”如图: 54 | 55 | 56 | 57 | ![image](http://img.blog.csdn.net/20150819193434045) 58 | 59 | 60 | 61 | > 此时可以设置下Phantomjs的路径,同时如果你配置了Scripts目录环境变量,可以解压Phantomjs到该文件夹下。可参考[Selenium with GhostDriver in Python on Windows - stackoverflow](http://stackoverflow.com/questions/21768554/selenium-with-ghostdriver-in-python-on-windows),整个win安装过程可参考[在Windows下安装PIP+Phantomjs+Selenium](http://blog.csdn.net/eastmount/article/details/47785123)],Mac和Linux/Ubuntu 下可参考[[解决:Ubuntu(MacOS)+phantomjs+python的部署问题](http://blog.csdn.net/mrlevo520/article/details/73196256) 62 | 63 | 64 | 65 | ### 测试安装是否成功 66 | 67 | ``` 68 | # 进入python环境后执行如下操作 69 | 70 | # win下操作 71 | >>> from selenium import webdriver # pip install selenium 72 | >>> driver_detail = webdriver.PhantomJS(executable_path="F:\Python\phantomjs-1.9.1-windows\phantomjs.exe") 73 | >>> driver_detail.get('https://www.baidu.com') 74 | >>> news = driver_detail.find_element_by_xpath("//div[@id='u1']/a") 75 | >>> print news.text 76 | 新闻 77 | >>> driver_detail.quit() # 记得关闭,不然耗费内存 78 | 79 | ------------------------------------------------------------------------ 80 | 81 | # mac下操作 82 | >>> from selenium import webdriver # pip install selenium 83 | >>> driver_detail = webdriver.PhantomJS() 84 | >>> driver_detail.get('https://www.baidu.com') 85 | >>> news = driver_detail.find_element_by_xpath("//div[@id='u1']/a") 86 | >>> print news.text 87 | 新闻 88 | >>> driver_detail.quit() # 记得关闭,不然耗费内存 89 | ``` 90 | 91 | 92 | 93 | ------ 94 | 95 | 96 | 97 | ## 爬取动态数据 98 | 99 | > 获取自己的id号,这个可以自己登陆自己的网易云音乐后获得,就是id=后面那个值 100 | 101 | 102 | 103 | ![这里写图片描述](http://img.blog.csdn.net/20170710004159441?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 104 | 105 | 106 | 107 | 108 | 109 | > 构造爬取的id,因为我发现,每个人的id只要被获取到,他的歌单都是公开的!!!这就节省了自动登录的一步,而且,我还有个大胆的想法,哈哈哈,我还要搞个大新闻!这次先不说~ 110 | 111 | ***墙裂推荐先阅读该博客掌握获取元素方法:[Python爬虫 Selenium实现自动登录163邮箱和Locating Elements介绍](http://blog.csdn.net/eastmount/article/details/47825633)*** 112 | 113 | ```python 114 | # -*- coding: utf-8 -*- 115 | # Author:哈士奇说喵 116 | # Create:20170707 117 | 118 | import traceback 119 | from selenium import webdriver 120 | import selenium.webdriver.support.ui as ui 121 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 122 | import time 123 | import random 124 | 125 | 126 | # 存储为文本的子函数 127 | def write2txt(data,path): 128 | f = open(path,"a") 129 | f.write(data) 130 | f.write("\n") 131 | f.close() 132 | 133 | 134 | # 获取该id喜欢音乐的列表 135 | def catchSongs(url_id,url): 136 | 137 | user = url_id.split('=')[-1].strip() 138 | print 'excute user:',user 139 | 140 | driver = webdriver.PhantomJS()#,executable_path='/Users/mrlevo/phantomjs-2.1.1-macosx/bin/phantomjs') # 注意填上路径 141 | driver.get(url) 142 | 143 | driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 144 | 145 | try: 146 | wait = ui.WebDriverWait(driver,15) 147 | wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="j-flag"]/table/tbody')) # 等待元素渲染出来 148 | try: 149 | song_key = 1 150 | wrong_time = 0 151 | while wrong_time < 5: # 不断获取歌信息,假定5次获取不到值,就判无值可获取,跳出循环 152 | try: 153 | songs = driver.find_elements_by_xpath('//*[@class="j-flag"]/table/tbody/tr[%s]'%song_key) 154 | info_ = songs[0].text.strip().split("\n") 155 | if len(info_) == 5: 156 | info_.insert(2,'None') # 没有MV选项的进行插入None 157 | new_line = '%s|'%user+'|'.join(info_) 158 | song_key +=1 159 | #new_line = "%s|%s|%s|%s|%s|%s|%s"%(user,info_[0],info_[1],info_[2],info_[3],info_[4],info_[5]) 160 | 161 | print new_line 162 | 163 | write2txt(new_line.encode('utf-8'),user) # mac写入文件需要改变字符,以id命名的文件,存储在执行脚本的当前路径下,在win下请去掉编.endcode('utf-8') 164 | 165 | 166 | except Exception as ex: 167 | wrong_time +=1 168 | # print ex 169 | except Exception as ex: 170 | pass 171 | 172 | except Exception as ex: 173 | traceback.print_exc() 174 | finally: 175 | driver.quit() 176 | 177 | 178 | 179 | # 获取id所喜爱的音乐的url 180 | def catchPlaylist(url): 181 | 182 | 183 | driver = webdriver.PhantomJS()#,executable_path='/Users/mrlevo/phantomjs-2.1.1-macosx/bin/phantomjs') # 注意填上路径 184 | driver.get(url) 185 | 186 | driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 187 | 188 | try: 189 | wait = ui.WebDriverWait(driver,15) 190 | wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a')) # 根据xpath获取元素 191 | 192 | urls = driver.find_elements_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a') 193 | favourite_url = urls[0].get_attribute("href") 194 | 195 | except Exception as ex: 196 | traceback.print_exc() 197 | finally: 198 | driver.quit() 199 | # print favourite_url 200 | return favourite_url 201 | 202 | 203 | 204 | if __name__ == '__main__': 205 | 206 | for url in ['http://music.163.com/user/home?id=67259702']: # 这里把自己的id替换掉,想爬谁的歌单都可以,只要你有他的id 207 | time.sleep(random.randint(2, 4)) # 随机休眠时间2~4秒 208 | url_playlist = catchPlaylist(url) 209 | time.sleep(random.randint(1, 2)) 210 | catchSongs(url,url_playlist) 211 | 212 | 213 | ``` 214 | 215 | > 不出意外的话,你的执行脚本的目录下会产生一个以你的id命名的文件,里面打开应该是这样的 216 | 217 | ```shell 218 | 67259702|2|因为了解|None|04:08|汪苏泷|慢慢懂 219 | 67259702|3|潮鳴り|None|02:37|折戸伸治|CLANNAD ORIGINAL SOUNDTRACK 220 | 67259702|4|每个人都会|None|02:58|方大同|橙月 Orange Moon 221 | 67259702|5|Don't Cry (Original)|MV|04:44|Guns N' Roses|Greatest Hits 222 | 67259702|6|妖孽(Cover:蒋蒋)|None|02:58|醉影An|醉声梦影 223 | 67259702|7|好好说再见(Cover 陶喆 / 关诗敏)|None|04:06|锦零/疯疯|zero 224 | 67259702|8|好好说再见(cover陶喆)|None|03:34|AllenRock|WarmCovers ·早 225 | 226 | # 这边分别爬取的数据结构是: id|歌次序|歌名|是否有MV|时长|歌手|专辑 227 | 228 | ``` 229 | 230 | 231 | 232 | 233 | 234 | ------ 235 | 236 | ## Show数据-ROUND1 237 | 238 | 239 | 240 | > 接下来就是处理自己下好的自己的歌单了,为了方便起见,我在构造爬取代码的时候,已经构造的比较好了,这也就帮助大家减少了数据预处理的时间了,一般来说,数据不会那么干净的。 241 | 242 | 243 | 244 | ***我只是做了最简单的歌手词云的例子,数据比较丰富的情况下,自己处理吧,想做什么统计都可以,或许以后我会补上可视化相关的一些例子*** 245 | 246 | --- 247 | 248 | ### 自定义遮罩层版本 249 | 250 | ```python 251 | # -*- coding: utf-8 -*- 252 | # 如果还不清楚词云怎么搞,请参考这里https://mp.weixin.qq.com/s/0Bw8QUo1YfWZR_Boeaxu_Q,或者自行百度,很简单的一个包 253 | 254 | import numpy as np 255 | import PIL.Image as Image 256 | from wordcloud import WordCloud, ImageColorGenerator 257 | import matplotlib.pyplot as plt 258 | 259 | # 统计词频 260 | # win的用户,把解码去掉即可,因为当时mac写入的文件有编码,所以读出来需要解码 261 | def statistics(lst): 262 | dic = {} 263 | for k in lst: 264 | if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 265 | dic[k.decode('utf-8')] +=1 266 | return dic 267 | 268 | 269 | path = '67259702' # 自己路径自己搞定 270 | list_ = [] 271 | with open(path,'r') as f: 272 | for line in f: 273 | list_.append(line.strip().split('|')[-2].strip()) 274 | 275 | dict_ = statistics(list_) 276 | 277 | 278 | # the font from github: https://github.com/adobe-fonts 279 | font = r'SimHei.ttf' 280 | coloring = np.array(Image.open("screenshot.png")) # 遮罩层自己定义,可选自己的图片 281 | wc = WordCloud(background_color="white", 282 | collocations=False, 283 | font_path=font, 284 | width=1400, 285 | height=1400, 286 | margin=2, 287 | mask=np.array(Image.open("screenshot.png"))).generate_from_frequencies(dict_) 288 | 289 | # 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词 290 | # 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了 291 | 292 | image_colors = ImageColorGenerator(np.array(Image.open("screenshot.png"))) 293 | plt.imshow(wc.recolor(color_func=image_colors)) 294 | plt.imshow(wc) 295 | plt.axis("off") 296 | plt.show() 297 | 298 | wc.to_file('mymusic2.png') # 把词云保存下来 299 | 300 | ``` 301 | 302 | ![这里写图片描述](http://img.blog.csdn.net/20170710011012839?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 303 | 304 | 305 | ### 方块版本 306 | 307 | 308 | ``` 309 | # -*- coding: utf-8 -*- 310 | # 稍微修改下参数,就是另一幅图,这是没有遮罩层的 311 | import numpy as np 312 | import PIL.Image as Image 313 | from wordcloud import WordCloud, ImageColorGenerator 314 | import matplotlib.pyplot as plt 315 | 316 | # 统计词频 317 | def statistics(lst): 318 | dic = {} 319 | for k in lst: 320 | if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 321 | dic[k.decode('utf-8')] +=1 322 | return dic 323 | 324 | 325 | path = '67259702' # 自己路径自己搞定 326 | list_ = [] 327 | with open(path,'r') as f: 328 | for line in f: 329 | list_.append(line.strip().split('|')[-2].strip()) 330 | 331 | dict_ = statistics(list_) 332 | 333 | 334 | # the font from github: https://github.com/adobe-fonts 335 | font = r'SimHei.ttf' 336 | coloring = np.array(Image.open("screenshot.png")) 337 | wc = WordCloud( 338 | collocations=False, 339 | font_path=font, 340 | width=1400, 341 | height=1400, 342 | margin=2, 343 | ).generate_from_frequencies(dict_) 344 | 345 | # 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词 346 | # 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了 347 | 348 | image_colors = ImageColorGenerator(np.array(Image.open("screenshot.png"))) 349 | 350 | plt.imshow(wc) 351 | plt.axis("off") 352 | plt.show() 353 | 354 | wc.to_file('mymusic2.png') # 把词云保存下来 355 | 356 | ``` 357 | 358 | 359 | 360 | 361 | ![这里写图片描述](http://img.blog.csdn.net/20170710015354120?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 362 | 363 | --- 364 | 365 | ## SHOW数据-ROUND2 366 | 367 | > 刚看到个好玩的,迫不及待的试了下,这是关于语种翻译的API接口,阿里云买的,0.01=1000条,买买买,买来玩玩试试自己歌曲语种 368 | 369 | ``` 370 | # -*- coding:utf-8 -*- 371 | # 调用的阿里云的API接口实现语种翻译 372 | # API官网:https://market.aliyun.com/products/57124001/cmapi010395.html?spm=5176.730005.0.0.UrR9bO#sku=yuncode439500000 373 | import urllib, urllib2, sys 374 | import ssl 375 | 376 | def Lang2Country(text): 377 | host = 'https://dm-12.data.aliyun.com' 378 | path = '/rest/160601/mt/detect.json' 379 | method = 'POST' 380 | appcode = 'xxxxx' # 购买后提供的appcode码 381 | querys = '' 382 | bodys = {} 383 | url = host + path 384 | bodys['q'] = text 385 | post_data = urllib.urlencode(bodys) 386 | request = urllib2.Request(url, post_data) 387 | request.add_header('Authorization', 'APPCODE ' + appcode) 388 | # 根据API的要求,定义相对应的Content-Type 389 | request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') 390 | ctx = ssl.create_default_context() 391 | ctx.check_hostname = False 392 | ctx.verify_mode = ssl.CERT_NONE 393 | response = urllib2.urlopen(request, context=ctx) 394 | content = response.read() 395 | if (content): 396 | # print(content) 397 | return content 398 | else: 399 | return None 400 | 401 | 402 | # 403 | # 67259702|1|Claux - 水之畔(8lope Remix) (feat. 陶心瑶)|None|02:44|8lope|水之畔(feat. 陶心瑶) (8lope Remix) 404 | list_songs = [] 405 | list_songwithsinger = [] 406 | with open('67259702') as f: # 文件名写上次爬下来的 407 | for line in f: 408 | line_split = line.split('|') 409 | list_songs.append(line_split[2]) 410 | list_songwithsinger.append(line_split[2]+line_split[5]) 411 | 412 | 413 | # 调用接口进行语种识别 414 | dict_lang = {} 415 | for i in range(537): 416 | try: 417 | content = Lang2Country(list_songwithsinger[i]) 418 | lag_ = json.loads(content)['data']['language'] 419 | if lag_ not in dict_lang: 420 | dict_lang[lag_]=0 421 | dict_lang[lag_] +=1 422 | except: 423 | pass 424 | 425 | print dict_lang 426 | 427 | # {u'ru': 1, u'fr': 9, u'en': 111, u'zh': 259, u'pt': 21, u'ko': 8, u'de': 7, u'tr': 15, u'it': 47, u'id': 2, u'pl': 7, u'th': 1, u'nl': 10, u'ja': 17, u'es': 20} 428 | 429 | ``` 430 | > ok,数据准备好了,接下来可视化就好了!这次我用Echarts,换个口味的就不用云词了,来个统计效果好看点的!不会Echarts?看这里→_→[@Mrlevo520--Echarts入门(零基础小白教程)](http://blog.csdn.net/mrlevo520/article/details/54603300) 431 | 432 | ![这里写图片描述](http://img.blog.csdn.net/20170713031050134?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 433 | 434 | 435 | ``` 436 | # 进入该网页:http://echarts.baidu.com/demo.html#pie-simple 437 | # 然后把里面的内容替换掉就行 438 | option = { 439 | title : { 440 | text: '哈士奇说喵喜欢的音乐', 441 | x:'center' 442 | }, 443 | tooltip : { 444 | trigger: 'item', 445 | formatter:'{b} : {c} ({d}%)' 446 | }, 447 | legend: { 448 | orient: 'vertical', 449 | left: 'left', 450 | data:['中文','英文','俄语','法语','葡萄牙语','韩语','德语','土耳其语','意大利语'] 451 | }, 452 | series : [ 453 | { 454 | name: '访问来源', 455 | type: 'pie', 456 | radius : '55%', 457 | center: ['50%', '60%'], 458 | itemStyle: { 459 | normal: {label:{ 460 | show:true, 461 | formatter:'{b} : {c} ({d}%)' 462 | }, 463 | }}, 464 | data:[ 465 | {value:259, name:'中文'}, 466 | {value:111,name:'英文'}, 467 | {value:1, name:'俄语'}, 468 | {value:9, name:'法语'}, 469 | {value:21, name:'葡萄牙语'}, 470 | {value:8, name:'韩语'}, 471 | {value:7, name:'德语'}, 472 | {value:15, name:'土耳其语'}, 473 | {value:47, name:'意大利语'}, 474 | {value:2, name:'印尼语'}, 475 | {value:7, name:'波兰语'}, 476 | {value:1, name:'泰语'}, 477 | {value:10, name:'荷兰语'}, 478 | {value:17, name:'日语'}, 479 | {value:20, name:'西班牙语'}, 480 | 481 | ], 482 | 483 | } 484 | ] 485 | }; 486 | 487 | ``` 488 | 489 | ------ 490 | ## Pay Attention 491 | 492 | 1. 这里遇到的最大问题,就是网易云的网页竟然还iframe框来做!!!不切入那个内联框架连phantomjs都无能为力!!这是最值得注意的一点,即使你找对了元素,也可能获取不到值! 493 | 494 | 2. 如果是win的计算机,在 driver = webdriver.PhantomJS()里面填上phantomjs.exe的路径,上面抓取数据的代码里面有两个需要引擎需要填写路径 495 | 3. 如果有打印出字段,但是记录的数据为0KB,那么是文件没有写进去,对于win的用户,把代码写入的部门,编码方式去掉即可 496 | 4. 有些win的小伙伴反应路径都加载对了,但是还是找不到exe,那么请在路径前面加r比如 `executable_path=r"F:\Python\phantomjs-1.9.1-windows\phantomjs.exe"` 497 | 498 | ---- 499 | ## 结论 500 | 501 | > 果然一下子就看出是上个世纪九十年代的人(:,还有就是,音乐不分国界,就是动感~ 502 | 503 | ------ 504 | 505 | ## 最后 506 | 507 | > 关于phantomjs和selenium的做过很多,大家可以自己参考我以前的几篇博客:[用python做些有趣的事情](http://blog.csdn.net/MrLevo520/article/category/6321608) 508 | 509 | 510 | 511 | 512 | 513 | ## 致谢 514 | 515 | 516 | 517 | - @爱搞事情的自己 518 | 519 | - [@Eastmoun--在Windows下安装PIP+Phantomjs+Selenium](http://blog.csdn.net/eastmount/article/details/47785123) 520 | 521 | - [@mrlevo520--Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录](http://blog.csdn.net/mrlevo520/article/details/51901579) 522 | 523 | - [@Alfred--一件有趣的事:用Python 爬了爬自己的微信朋友](https://mp.weixin.qq.com/s/0Bw8QUo1YfWZR_Boeaxu_Q) 524 | - [@Eastmount--Python爬虫 Selenium实现自动登录163邮箱和Locating Elements介绍](http://blog.csdn.net/eastmount/article/details/47825633) 525 | 526 | ## 附录 527 | > 对照表 528 | 529 | ![这里写图片描述](http://img.blog.csdn.net/20170814124806754?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJMZXZvNTIw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 530 | -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/echarts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/echarts.png -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/mymusic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/mymusic2.png -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/mymusic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/mymusic3.png -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/zxylina-网易云音乐爬取分析.md: -------------------------------------------------------------------------------- 1 | # zxylina-网易云音乐爬取分析 2 | 3 | *Windows10 企业版* 4 | 5 | *Python 2.7* 6 | 7 | *selenium 3.4.3* 8 | 9 | *phantomjs-2.1.1-windows* 10 | 11 | ## 搭建爬虫环境 12 | 13 | ### 1.安装selenuim 14 | 15 | ``` 16 | pip install selenium 17 | ``` 18 | 19 | ### 2.安装phantomjs 20 | 21 | ``` 22 | 官网http://phantomjs.org/下载PhantomJS解压后如下图所示: 23 | ``` 24 | 25 | ![下载软件](https://github.com/zxylina/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/%E4%B8%8B%E8%BD%BD%E8%BD%AF%E4%BB%B6.png) 26 | 27 | 测试安装是否成功: 28 | 29 | ``` 30 | # 进入python环境后执行如下操作 31 | 32 | # win下操作 33 | >>> from selenium import webdriver # pip install selenium 34 | >>> driver_detail = webdriver.PhantomJS(executable_path="D:\Python\Anaconda\phantomjs-2.1.1-windows\phantomjs.exe") 35 | >>> driver_detail.get('https://www.baidu.com') 36 | >>> news = driver_detail.find_element_by_xpath("//div[@id='u1']/a") 37 | >>> print news.text 38 | ``` 39 | 40 | 会报错: ![报错信息](https://github.com/zxylina/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/%E6%8A%A5%E9%94%99%E4%BF%A1%E6%81%AF.png) 41 | 42 | 按照错误信息里的路径(本截图非当时报错信息): 43 | 44 | 1、修改或者注释相应py文件的line 76行即可; 45 | 46 | 2、需要配置phantomjs的真实路径,压缩完phantomjs.exe在bin目录下,如有报错可以复制到上一层目录;建议测试使用jypyter notebook。 47 | 48 | 参考:在Windows下安装PIP+Phantomjs+Selenium:http://blog.csdn.net/eastmount/article/details/47785123 49 | 50 | ## 爬取动态数据 51 | 52 | 本人没有网易云音乐号,同事那里借了个号(骗他登录偷偷记下id) 53 | 54 | http://music.163.com/user/home?id=305514369 55 | 56 | ## SHOW数据-ROUND1 57 | 58 | ``` 59 | # -*- coding: utf-8 -*- 60 | # Author:哈士奇说喵(可爱的课代表) 61 | # Create:20170707 62 | 63 | import traceback 64 | from selenium import webdriver 65 | import selenium.webdriver.support.ui as ui 66 | from selenium.webdriver.common.desired_capabilities import DesiredCapabilities 67 | import time 68 | import random 69 | 70 | # 存储为文本的子函数 71 | def write2txt(data,path): 72 | f = open(path,"a") 73 | f.write(data) 74 | f.write("\n") 75 | f.close() 76 | 77 | # 获取该id喜欢音乐的列表 78 | def catchSongs(url_id,url): 79 | 80 | user = url_id.split('=')[-1].strip() 81 | print 'excute user:',user 82 | 83 | driver = webdriver.PhantomJS(executable_path="D:\Python\Anaconda\phantomjs-2.1.1-windows\phantomjs.exe")# 注意填上路径 84 | driver.get(url) 85 | 86 | driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 87 | 88 | try: 89 | wait = ui.WebDriverWait(driver,15) 90 | wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="j-flag"]/table/tbody')) # 等待元素渲染出来 91 | try: 92 | song_key = 1 93 | wrong_time = 0 94 | while wrong_time < 5: # 不断获取歌信息,假定5次获取不到值,就判无值可获取,跳出循环 95 | try: 96 | songs = driver.find_elements_by_xpath('//*[@class="j-flag"]/table/tbody/tr[%s]'%song_key) 97 | info_ = songs[0].text.strip().split("\n") 98 | if len(info_) == 5: 99 | info_.insert(2,'None') # 没有MV选项的进行插入None 100 | new_line = '%s|'%user+'|'.join(info_) 101 | song_key +=1 102 | #new_line = "%s|%s|%s|%s|%s|%s|%s"%(user,info_[0],info_[1],info_[2],info_[3],info_[4],info_[5]) 103 | 104 | print new_line 105 | 106 | write2txt(new_line.encode('utf-8'),user) # mac写入文件需要改变字符,以id命名的文件,存储在执行脚本的当前路径下,在win下请去掉编.endcode('utf-8') 107 | except Exception as ex: 108 | wrong_time +=1 109 | # print ex 110 | except Exception as ex: 111 | pass 112 | except Exception as ex: 113 | traceback.print_exc() 114 | finally: 115 | driver.quit() 116 | 117 | # 获取id所喜爱的音乐的url 118 | def catchPlaylist(url): 119 | driver = webdriver.PhantomJS(executable_path="D:\Python\Anaconda\phantomjs-2.1.1-windows\phantomjs.exe") # 注意填上路径 120 | driver.get(url) 121 | 122 | driver.switch_to_frame('g_iframe') # 网易云的音乐元素都放在框架内!!!!先切换框架 123 | 124 | try: 125 | wait = ui.WebDriverWait(driver,15) 126 | wait.until(lambda driver: driver.find_element_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a')) # 根据xpath获取元素 127 | 128 | urls = driver.find_elements_by_xpath('//*[@class="m-cvrlst f-cb"]/li[1]/div/a') 129 | favourite_url = urls[0].get_attribute("href") 130 | 131 | except Exception as ex: 132 | traceback.print_exc() 133 | finally: 134 | driver.quit() 135 | # print favourite_url 136 | return favourite_url 137 | 138 | if __name__ == '__main__': 139 | 140 | for url in ['http://music.163.com/user/home?id=305514369']: # 这里把自己的id替换掉,想爬谁的歌单都可以,只要你有他的id 141 | #D:\Python\Anaconda\test\67259702,305514369 142 | time.sleep(random.randint(2, 4)) # 随机休眠时间2~4秒 143 | url_playlist = catchPlaylist(url) 144 | time.sleep(random.randint(1, 2)) 145 | catchSongs(url,url_playlist) 146 | ``` 147 | 148 | 获取到了同事的全部歌单,后来我给他看歌单云图的时候他惊讶的说好熟悉~ 149 | 150 | ``` 151 | # -*- coding: utf-8 -*- 152 | # 如果还不清楚词云怎么搞,请参考这里https://mp.weixin.qq.com/s/0Bw8QUo1YfWZR_Boeaxu_Q,或者自行百度,很简单的一个包 153 | 154 | import numpy as np 155 | import PIL.Image as Image 156 | from wordcloud import WordCloud, ImageColorGenerator 157 | import matplotlib.pyplot as plt 158 | 159 | # 统计词频 160 | # win的用户,把解码去掉即可,因为当时mac写入的文件有编码,所以读出来需要解码 161 | # def statistics(lst): 162 | # dic = {} 163 | # for k in lst: 164 | # if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 165 | # dic[k.decode('utf-8')] +=1 166 | # return dic 167 | 168 | path = r'D:/Python/Anaconda/test/305514369' # 自己路径自己搞定 169 | list_ = [] 170 | with open(path,'r') as f: 171 | for line in f: 172 | list_.append(line.strip().split('|')[-2].strip()) 173 | dict_ = statistics(list_) 174 | 175 | # the font from github: https://github.com/adobe-fonts 176 | font = r'C:/Windows/WinSxS/amd64_microsoft-windows-font-truetype-simhei_31bf3856ad364e35_10.0.14393.0_none_219c21d98ac787bb/SimHei.ttf' 177 | coloring = np.array(Image.open(r"D:/Python/Anaconda/test/yingzi.jpg")) # 遮罩层自己定义,可选自己的图片 178 | 179 | wc = WordCloud(background_color="white", 180 | collocations=False, 181 | font_path=font, 182 | width=1400, 183 | height=1400, 184 | margin=2, 185 | mask=np.array(Image.open(r"D:/Python/Anaconda/test/yingzi.jpg"))).generate_from_frequencies(dict_) 186 | 187 | # 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词 188 | # 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了 189 | 190 | image_colors = ImageColorGenerator(coloring) 191 | plt.imshow(wc.recolor(color_func=image_colors)) 192 | plt.imshow(wc) 193 | plt.axis("off") 194 | plt.show() 195 | 196 | wc.to_file('mymusic2.png') # 把词云保存下来 197 | ``` 198 | 199 | 得到以下词云图:![mymusic2](https://github.com/zxylina/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/mymusic2.png) 200 | 201 | ``` 202 | # -*- coding: utf-8 -*- 203 | # 稍微修改下参数,就是另一幅图,这是没有遮罩层的 204 | import numpy as np 205 | import PIL.Image as Image 206 | from wordcloud import WordCloud, ImageColorGenerator 207 | import matplotlib.pyplot as plt 208 | 209 | # 统计词频 210 | def statistics(lst): 211 | dic = {} 212 | for k in lst: 213 | if not k.decode('utf-8') in dic:dic[k.decode('utf-8')] = 0 214 | dic[k.decode('utf-8')] +=1 215 | return dic 216 | 217 | path = 'D:/Python/Anaconda/test/305514369' # 自己路径自己搞定 218 | list_ = [] 219 | with open(path,'r') as f: 220 | for line in f: 221 | list_.append(line.strip().split('|')[-2].strip()) 222 | 223 | dict_ = statistics(list_) 224 | 225 | # the font from github: https://github.com/adobe-fonts 226 | font = r'C:/Windows/WinSxS/amd64_microsoft-windows-font-truetype-simhei_31bf3856ad364e35_10.0.14393.0_none_219c21d98ac787bb/SimHei.ttf' 227 | coloring = np.array(Image.open("D:/Python/Anaconda/test/yingzi.jpg")) 228 | wc = WordCloud( 229 | collocations=False, 230 | font_path=font, 231 | width=1400, 232 | height=1400, 233 | margin=2, 234 | ).generate_from_frequencies(dict_) 235 | 236 | # 这里采用了generate_from_frequencies(dict_)的方法,里面传入的值是{‘歌手1’:5,‘歌手2’:8,},分别是歌手及出现次数,其实和jieba分词 237 | # 之后使用generate(text)是一个效果,只是这里的text已经被jieba封装成字典了 238 | 239 | image_colors = ImageColorGenerator(coloring) 240 | 241 | plt.imshow(wc) 242 | plt.axis("off") 243 | plt.show() 244 | 245 | wc.to_file('mymusic3.png') # 把词云保存下来 246 | ``` 247 | 248 | 变了个样子方块字:![mymusic3](https://github.com/zxylina/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/mymusic3.png) 249 | 250 | ## SHOW数据-ROUND2 251 | 252 | 语种翻译的API接口,阿里云买的,0.01=1000条 253 | 254 | ``` 255 | # -*- coding:utf-8 -*- 256 | # 调用的阿里云的API接口实现语种翻译 257 | # API官网:https://market.aliyun.com/products/57124001/cmapi010395.html?spm=5176.730005.0.0.UrR9bO#sku=yuncode439500000 258 | import urllib, urllib2, sys 259 | import ssl 260 | import json 261 | 262 | def Lang2Country(text): 263 | host = 'https://dm-12.data.aliyun.com' 264 | path = '/rest/160601/mt/detect.json' 265 | method = 'POST' 266 | appcode = '2aae9b143c164e8f9a91333a3d2eee9e' # 购买后提供的appcode码 267 | querys = '' 268 | bodys = {} 269 | url = host + path 270 | bodys['q'] = text 271 | post_data = urllib.urlencode(bodys) 272 | request = urllib2.Request(url, post_data) 273 | request.add_header('Authorization', 'APPCODE ' + appcode) 274 | # 根据API的要求,定义相对应的Content-Type 275 | request.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') 276 | ctx = ssl.create_default_context() 277 | ctx.check_hostname = False 278 | ctx.verify_mode = ssl.CERT_NONE 279 | response = urllib2.urlopen(request, context=ctx) 280 | content = response.read() 281 | if (content): 282 | # print content 283 | return content 284 | else: 285 | return None 286 | 287 | list_songs = [] 288 | list_songwithsinger = [] 289 | 290 | with open('D:/Python/Anaconda/test/305514369') as f: # 文件名写上次爬下来的 291 | for line in f: 292 | #print line 293 | line_split = line.split('|') 294 | list_songs.append(line_split[2]) 295 | #print list_songs 296 | list_songwithsinger.append(line_split[2]+line_split[5]) 297 | #print list_songwithsinger 298 | 299 | # 调用接口进行语种识别 300 | dict_lang = {} 301 | dict = {} 302 | 303 | for i in range(171): 304 | try: 305 | content = Lang2Country(list_songwithsinger[i]) 306 | #print content 307 | lag_ = json.loads(content)['data']['language'] 308 | if lag_ not in dict_lang: 309 | dict_lang[lag_]=0 310 | dict_lang[lag_] +=1 311 | except KeyError: 312 | pass 313 | except urllib2.HTTPError: 314 | pass 315 | 316 | print dict_lang 317 | ``` 318 | 319 | 输出以下,对照阿里云映射表自己填入Echarts里: 320 | 321 | ``` 322 | {u'fr': 3, u'en': 26, u'zh': 71, u'pt': 3, u'ko': 1, u'de': 7, u'tr': 3, u'it': 8, u'es': 7, u'vi': 1, u'nl': 1, u'ja': 35, u'pl': 4} 323 | ``` 324 | 325 | ![echarts](https://github.com/zxylina/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/echarts.png) 326 | 327 | ``` 328 | 329 | 330 | 331 | 332 | ECharts 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 |
342 | 394 | 395 | 396 | ``` 397 | 398 | ## 结论 399 | 400 | 作为一个初级运维产生了想学习python的念头,希望自己坚持下去,感谢带我入门的DT君VX:dtcaijing002 and 兢兢业业的课代表VX:MrLevo ——20170907 401 | 402 | -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/下载软件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/下载软件.png -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/报错信息.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-网易云音乐爬取分析/furtherProject/zxylina-网易云音乐爬取分析/报错信息.png -------------------------------------------------------------------------------- /MrLevo520-网易云音乐爬取分析/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-自动化一键登录/Tutorial_自动化一键登录.md: -------------------------------------------------------------------------------- 1 | ***From [基于Selenium一键写CSDN博客](http://blog.csdn.net/mrlevo520/article/details/51932076)*** 2 | 3 | 4 | 5 | - Python 2.7 6 | - IDE Pycharm 5.0.3 7 | - Firefox 47.0.1 8 | 9 | > 至于Selenium等环境配置,则请看[ Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录](http://blog.csdn.net/mrlevo520/article/details/51901579) 10 | 11 | 12 | 13 | --- 14 | 15 | 16 | 17 | 起因 18 | -- 19 | > ​ 我发现CSDN的博客很奇怪,不知道大家发觉没有,即使是记住账号的,点击自己的博客,然后操作步骤是,写博客,再点击重新输入账号密码(虽然浏览器记住了),但是还要操作一回,如果没有上述步骤,那也需要再点击‘切换到MarkDown编辑器 ’然后才可以开始写博客,干的事情从点击浏览器开始,然后打开了三个浏览器子页面,一个是登录主页,一个是自己的博客主页,一个就是编辑博客界面了---我TM竟然重复操作了那么多回,我真的好愚钝啊!早该写个脚本了!! 20 | 21 | ---------- 22 | 23 | 目的 24 | -- 25 | 1. 一键登录直接撰写CSDN博客 26 | 2. 程序健壮性增量,添加其余五个候选项 27 | 3. 打包程序,供没有安装python环境的小伙伴使用(firefox必须要有)[如何将python打包成exe请点击这里](http://blog.csdn.net/mrlevo520/article/details/51840217) 28 | 29 | ---------- 30 | 31 | 实现方案 32 | ---- 33 | > Python+Selenium自动化脚本+Firefox的联合使用+Pyinstaller打包成exe 34 | 35 | ---------- 36 | 37 | 实现代码 38 | ---- 39 | 40 | ### 第一部分--一键登录写博客 41 | 42 | ``` 43 | # -*- coding: utf-8 -*- 44 | #Author:哈士奇说喵 45 | #CSDN--实现一键写博客 46 | 47 | from selenium import webdriver 48 | import time 49 | 50 | #shift-tab多行缩进(左) 51 | print 'Please wait...Firefox loading...' 52 | print '---------------------------------' 53 | #reload(sys) 54 | 55 | PostUrl = "https://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn" 56 | driver=webdriver.Firefox()#用浏览器实现访问 57 | #driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器 58 | driver.get(PostUrl) 59 | 60 | 61 | #账号填充输入 62 | elem_user = driver.find_element_by_id('username') 63 | elem_psw = driver.find_element_by_id('password') 64 | 65 | #可以自己修改登录名和账户密码,我自己的隐去了 66 | elem_user.send_keys('mrlevo@outlook.com') 67 | elem_psw.send_keys('xxxxxxxx') 68 | 69 | #点击登录 70 | click_login = driver.find_element_by_xpath("//input[@class='logging']") 71 | click_login.click() 72 | print 'log in...' 73 | print '---------------------------------' 74 | time.sleep(1) 75 | 76 | #先点击写博客图标,不然元素隐藏 77 | click_wbic = driver.find_element_by_xpath("//ul[@class='btns']/li[5]/div") 78 | click_wbic.click() 79 | print 'jumping...' 80 | print '---------------------------------' 81 | 82 | click_choice = driver.find_element_by_xpath("//div[@class='wrap clearfix']/dl/dt[4]/a") 83 | click_choice.click()#将点击操作放在内部比较好 84 | #定位新页面元素,将handle重定位即可 85 | driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面 86 | click_markdown = driver.find_element_by_xpath("//p[@class='subtit']/a") 87 | click_markdown.click() 88 | print '---------------------------------' 89 | print 'here we go!' 90 | #关闭无关页面,也可以根据自己喜好保留无关页面 91 | driver.close()#关闭第二个页面,也就是一般编辑器下的CSDN 92 | driver.switch_to_window(driver.window_handles[0])#关闭第1个页面,也就是登录主页 93 | driver.close() 94 | 95 | ``` 96 | > ​ 实现画面我就用动图表示了,最后达到的结果就是,关闭无关页面,(当然你也可以选择不关闭),直接将页面停留在编辑页面下,直接可进行编辑操作,省去中间一大串非人操作。。。 97 | 98 | ![这里写图片描述](http://img.blog.csdn.net/20160717141027248) 99 | 100 | ![这里写图片描述](http://img.blog.csdn.net/20160717140442699) 101 | 102 | ---------- 103 | 104 | ### 第二部分--选择项目操作 105 | 106 | 107 | 108 | ``` 109 | # -*- coding: utf-8 -*- 110 | #Author:哈士奇说喵 111 | #CSDN--实现一键写博客/传资源/写代码片/etc.. 112 | 113 | from selenium import webdriver 114 | import time 115 | 116 | #shift-tab多行缩进(左) 117 | print 'Please wait...Firefox loading...' 118 | print '---------------------------------' 119 | #reload(sys) 120 | 121 | PostUrl = "https://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn" 122 | driver=webdriver.Firefox()#用浏览器实现访问 123 | #driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器 124 | driver.get(PostUrl) 125 | 126 | 127 | #账号填充输入 128 | elem_user = driver.find_element_by_id('username') 129 | elem_psw = driver.find_element_by_id('password') 130 | elem_user.send_keys('mrlevo@outlook.com') 131 | elem_psw.send_keys('xxxxxxx') 132 | 133 | #点击登录 134 | click_login = driver.find_element_by_xpath("//input[@class='logging']") 135 | click_login.click() 136 | print 'log in...' 137 | print '---------------------------------' 138 | time.sleep(1) 139 | 140 | #先点击写博客图标,不然元素隐藏 141 | click_wbic = driver.find_element_by_xpath("//ul[@class='btns']/li[5]/div") 142 | click_wbic.click() 143 | print 'jumping...' 144 | print '---------------------------------' 145 | choice = input("what do you want?\n1-put ppt\n2-ask question\n3-write blog\n4-put resource\n5-create project\n6-create codes\nplease enter num:") 146 | click_choice = driver.find_element_by_xpath("//div[@class='wrap clearfix']/dl/dt[%s]/a"%(choice+1)) 147 | 148 | #根据点击操作后的观察可知,对于除了写博客之外的其余项目,只需要点击一次即可 149 | if choice==3 : 150 | click_choice.click()#将点击操作放在内部比较好 151 | #定位新页面元素,将handle重定位即可 152 | driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面 153 | click_markdown = driver.find_element_by_xpath("//p[@class='subtit']/a") 154 | click_markdown.click() 155 | print '---------------------------------' 156 | print 'here we go!' 157 | #关闭无关页面,也可以根据自己喜好保留无关页面 158 | driver.close()#关闭第二个页面,也就是一般编辑器下的CSDN 159 | driver.switch_to_window(driver.window_handles[0])#关闭第1个页面,也就是登录主页 160 | driver.close() 161 | 162 | else: 163 | click_choice.click() 164 | print '---------------------------------' 165 | print 'here we go!' 166 | driver.switch_to_window(driver.window_handles[0])#关闭第1个页面,也就是登录主页 167 | driver.close() 168 | ``` 169 | > ​ 这里需要的是在cmd窗口进行选择项目操作,我这里只写了其中的六个项目,好吧,其实我是用不到的,我就象征性的扩充了一下程序,万一有别的小伙伴要用到呢,反正目前我打开博客最多的就是写博客,所以我应该只会用第一部分代码做的exe。 170 | 171 | ![这里写图片描述](http://img.blog.csdn.net/20160717140049760) 172 | 173 | ---------- 174 | 175 | 176 | Pay Attention 177 | --- 178 | 179 | 1.点击exe后,cmd窗口出现后,点一下cmd窗口,让它保持在活动窗口最前面,当然你也可以被浏览器覆盖后自己找cmd在哪。 180 | 181 | 2.注意在操作过程中,不要动浏览器,特别是在选择写博客那一栏,动一下,那一栏就隐藏了,元素找不到就直接报错了,不要动浏览器完事ok 182 | 183 | 3.请务必安装Firefox浏览器,不然那上哪调用浏览器啊。我的版本是Firefox浏览器:47.0.1的,其余的我没测试过 184 | 185 | ---------- 186 | 187 | 遇到的问题及解决方案 188 | ---------- 189 | 1.抛出如下错误,元素不可见 190 | ``` 191 | ElementNotVisibleException: Message: Element is not currently visible and so may not be interacted with 192 | Stacktrace 193 | ``` 194 | 1.解决方案,实行点击操作,先点击一下,那个有一支笔的图标,然后才会有下面的六个选项,才能再进行选择写博客。主要方法就是把自己想象成浏览器,应该怎么一步步操作就达到目的。 195 | 196 | ---------- 197 | 2.弹出页面太多,还不够真正一键操作 198 | 199 | 2.解决方案,使用定位窗口然后关闭浏览器操作,在定位窗口时注意多做测试,才能深刻理解定位句柄的意义,定位窗口请看[解决Selenium弹出新页面无法定位元素问题(Unable to locate element) 200 | ](http://blog.csdn.net/mrlevo520/article/details/51926145) 201 | 202 | ---------- 203 | 204 | 最后 205 | -- 206 | > ​ CSDN的元素非常好找,真的非常正规,一切都显得非常顺利,大概写了十几分钟就把脚本写成exe了,大多数时间花在测试上了,其实就是脚本相互套的把戏,成功过几次模拟登陆一切都显得轻车熟路,多练习和解决实际问题才是最快的进步途径啊! 207 | 208 | ---------- 209 | 210 | ps 211 | -- 212 | 本来想写个gui出来的,然后加上用户名密码,但是,这样就脱离了一键操作的轻快感,主要还是给自己用的,如果能给你也带来灵感和提供帮助,我感到非常荣幸!以后就点击这个就可以啦! 213 | 214 | ![这里写图片描述](http://img.blog.csdn.net/20160717143434656) 215 | ​ 216 | **今天就酱紫,大家周末愉快!** 217 | 218 | ![这里写图片描述](http://img.blog.csdn.net/20160717143007666) 219 | -------------------------------------------------------------------------------- /MrLevo520-自动化一键登录/furtherProject/wwivywwivy-51job/51job简历自动刷新: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | import time 4 | from selenium import webdriver 5 | driver = webdriver.Chrome(r'chromedriver的路径') 6 | driver.get('https://login.51job.com/login.php?lang=c&url=http%3A%2F%2Fwww.51job.com%2F') 7 | 8 | #账号填充输入 9 | elem_user = driver.find_element_by_id('loginname') 10 | elem_psw = driver.find_element_by_id('password') 11 | 12 | #可以自己修改登录名和账户密码 13 | elem_user.send_keys('自己的账号') 14 | elem_psw.send_keys('自己的密码') 15 | 16 | #点击登录 17 | driver.find_element_by_xpath('//*[@id="signup"]/div[5]').click() 18 | driver.find_element_by_xpath('//*[@id="login_btn"]').click() 19 | 20 | #点击刷新,修改i可以修改点击次数 21 | i=5 22 | while i>0: 23 | driver.find_element_by_xpath('//*[@id="refreshresume"]').click() 24 | time.sleep(6) 25 | i = i-1 26 | 27 | print 'log in...' 28 | print '---------------------------------' 29 | time.sleep(1) 30 | 31 | #driver.quit() 32 | -------------------------------------------------------------------------------- /MrLevo520-自动化一键登录/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/Tutorial_自动续借图书集.md: -------------------------------------------------------------------------------- 1 | From 2 | 3 | - [Selenium+PhantomJS自动续借图书馆书籍(上)](http://blog.csdn.net/mrlevo520/article/details/51924757) 4 | 5 | - [Selenium+PhantomJS自动续借图书馆书籍(下)](http://blog.csdn.net/mrlevo520/article/details/51930741) 6 | 7 | 8 | ---- 9 | 10 | - Python 2.7 11 | - IDE Pycharm 5.0.3 12 | - Firefox浏览器:47.0.1 13 | 14 | > 具体环境,Selenium及PhantomJS使用等看我前一篇博客[Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录 ](http://blog.csdn.net/mrlevo520/article/details/51901579) 15 | 16 | ---------- 17 | 18 | # 上篇 19 | 20 | 吐槽 21 | -- 22 | 23 | 自从我欠图书馆6块钱的过期书后,立马要写一个自动续约的小工具压压惊 24 | ---------- 25 | 26 | 27 | 目的 28 | -- 29 | 30 | 自动实现图书馆借书籍的书单截图,并一键续约全部书籍,我登录校图书馆的目的无非就这两个咯,我才不去预约没有的书呢--反正没有一次预约成功过0.0 31 | 32 | ---------- 33 | 34 | 35 | 实现方法 36 | ---- 37 | 38 | Selenium+PhantonJS自动化脚本执行 39 | 40 | ---------- 41 | 42 | 43 | 实现方案 44 | ---- 45 | 46 | 1. 采用Firefox浏览器进行模拟登录,这个比较酷炫把,可以看着浏览器自己在那边跑,欢快的停不下来。。。 47 | 2. 调用PhantomJS.exe,不展现浏览器的运作,直接在cmd窗口跑(用pyinstaller打包成exe后有cmd窗) 48 | 49 | ---------- 50 | 51 | 52 | 方案实现过程 53 | ------- 54 | 55 | 1.采用Selenium+Firefox方式: 56 | 先来个最后成品动图: 57 | ![这里写图片描述](http://img.blog.csdn.net/20160716154214757) 58 | 59 | (自从剪成动图之后,根本停不下来。更加生动形象有木有!) 60 | 61 | ---------- 62 | 63 | 64 | 然后来程序代码--主模块(被调用模块,也可单独执行) 65 | 66 | ``` 67 | # -*- coding: utf-8 -*- 68 | #Author:哈士奇说喵 69 | 70 | from selenium import webdriver 71 | import time 72 | 73 | #shift-tab多行缩进(左) 74 | print 'please wait...system loading...' 75 | #reload(sys) 76 | 77 | PostUrl = "http://lib.hrbeu.edu.cn/#" 78 | 79 | driver=webdriver.Firefox()#用浏览器实现访问 80 | #driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器 81 | driver.get(PostUrl) 82 | 83 | elem_user = driver.find_element_by_name('number') 84 | elem_psw = driver.find_element_by_name('passwd') 85 | 86 | 87 | #选择我的图书馆,点击后才能看到输入账号密码 88 | click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]") 89 | click_first.click() 90 | elem_user.send_keys('S315080092') 91 | elem_psw.send_keys('xxxxxxxxx') 92 | 93 | #点击登录 94 | click_second = driver.find_element_by_name('submit') 95 | click_second.click() 96 | print 'log in...' 97 | time.sleep(1) 98 | 99 | #定位新页面元素,将handle重定位即可 100 | 101 | driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面 102 | #sreach_window = driver.current_window_handle #此行代码用来定位当前页面#不可行 103 | driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a").click() 104 | driver.save_screenshot('image_booklist_firefox.jpg') 105 | print 'turning to the mylib...' 106 | time.sleep(1)#搜索结果页面停留片刻 107 | 108 | #driver.switch_to_window(driver.window_handles[1]) 109 | #没有跳出新窗口就是在同一页面的! 110 | for i in range(2,30):#这里限定是29本书,一般我们都不会借那么多书的 111 | try: 112 | #driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/%s/td[8]/div/input"%('tr[%s]'%i)).click()#下面的比较好理解 113 | driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/tr[%s]/td[8]/div/input"%i).click() 114 | print 'renewing...the %d\'th book renewed '%(i-1) 115 | except: 116 | print '%d books have been renewed !'%(i-2) 117 | a=i-2 118 | time.sleep(4) 119 | driver.save_screenshot('image_done_firefox.jpg') 120 | print 'the picture is saving...' 121 | print 'done!' 122 | break 123 | 124 | time.sleep(1) 125 | 126 | driver.close() 127 | driver.quit() 128 | ``` 129 | 130 | 调用上述模块的主执行函数(其实就是为了封装上述模块而已,封装成gui界面,为后续的打包做准备,如何打包请见博文[将自己的python程序打包成exe(秀同学一脸呐) 131 | ](http://blog.csdn.net/mrlevo520/article/details/51840217)): 132 | 133 | ``` 134 | # -*- coding: utf-8 -*- 135 | #Author:哈士奇说喵 136 | 137 | from Tkinter import * 138 | import tkMessageBox#执行gui窗 139 | import time 140 | 141 | def check_renew(): 142 | print 'checking and renewing...' 143 | tkMessageBox.showinfo('提示','即将开启装逼模式,请确认已安装Firefox浏览器') 144 | #time.sleep(4) 145 | import Selenium_PhantomJS_lib_firefox 146 | tkMessageBox.showinfo('提示','已执行成功!\n(截图已保存于程序目录)') 147 | 148 | 149 | 150 | #主框架部分 151 | root = Tk() 152 | root.title('图书馆查询续约(哈尔滨工程大学专版)--by 哈士奇说喵') 153 | label=Label(root,text=' 图书馆一键查询与续约Firefox版本 (✪ω✪) ') 154 | button_check=Button(root,text='查询书单并续期━Σ(゚Д゚|||)━开启Firefox有形装逼模式 ',background='green',command=check_renew) 155 | 156 | label.pack() 157 | button_check.pack() 158 | root.mainloop() 159 | ``` 160 | 实现效果如图所示: 161 | ![这里写图片描述](http://img.blog.csdn.net/20160716154059598) 162 | 163 | 程序中的注释相信可以把程序解释的差不多了把。。。。 164 | 165 | ---------- 166 | 167 | 168 | 遇到问题和解决方案 169 | --------- 170 | 171 | 1.selenium对新页面元素无法定位抛出 172 | ``` 173 | NoSuchElementException: Message: Unable to locate element 174 | ``` 175 | 错误,导致无法进行对新的界面进行点击操作。 176 | 1.解决方案:专门写了一篇博客,请见 177 | [解决Selenium弹出新页面无法定位元素问题(Unable to locate element)](http://blog.csdn.net/mrlevo520/article/details/51926145) 178 | 179 | ---------- 180 | 2.对打包后的版本无法运行,抛出如图错误Errno 10054 181 | ![这里写图片描述](http://img.blog.csdn.net/20160716210003855) 182 | 2.解决方案:暂未找到解决方案,exe文件不可用,程序执行可用 183 | 184 | ---------- 185 | 3.对未知书籍数目重复点击操作,代码冗余 186 | 3.解决方案:因为点击续借按钮的元素每个都不一样,通过观察可知其中的规律,之后就知道在那进行修改,但是,光修改的话,十本书就有十个相似的代码串,很不pythontic,所以,采用格式化字符串的方式进行for循环带入,方便又漂亮! 187 | 188 | ---------- 189 | 4.使用了1中的解决方案还是不能定位元素 190 | 4.可能查找元素的方式出现错误,我现在的使用方法是采用xpath的方式来找,比如说这样 191 | ``` 192 | driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a") 193 | ``` 194 | 虽然看起来有点长,但是元素相当好找,而且定位很准,如果采用类似这种`driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]")`,我现在还不能很好地驾驭,出错可能性有点大,下次要多进行尝试。 195 | 196 | ---------- 197 | 198 | 下篇预告 199 | ---- 200 | 201 | 实现方案二,调用PhantomJS.exe,不展现浏览器的运作,直接在cmd窗口跑(用pyinstaller打包成exe后有cmd窗) 202 | 203 | 204 | 205 | ---- 206 | 207 | ---- 208 | 209 | # 下篇 210 | 211 | 接着上篇 212 | ---- 213 | 214 | [Selenium+PhantomJS自动续借图书馆书籍(上) 215 | ](http://blog.csdn.net/mrlevo520/article/details/51924757)接下来实现方案二的构思: 216 | 调用PhantomJS.exe,不展现浏览器的运作,直接在cmd窗口跑(用pyinstaller打包成exe后有cmd窗) 217 | 218 | ---------- 219 | 220 | 方案实现过程 221 | ------- 222 | 223 | ### 效果 224 | 225 | ![这里写图片描述](http://img.blog.csdn.net/20160717084320058) 226 | 227 | ### 代码 228 | 229 | > 被调模块(可单独执行) 230 | 231 | ``` 232 | # -*- coding: utf-8 -*- 233 | #Author:哈士奇说喵 234 | 235 | from selenium import webdriver 236 | import time 237 | import sys 238 | from PIL import Image 239 | #shift-tab多行缩进(左) 240 | print 'please wait...system loading...' 241 | reload(sys) 242 | 243 | PostUrl = "http://lib.hrbeu.edu.cn/#" 244 | 245 | driver = webdriver.PhantomJS(executable_path="phantomjs.exe")#没用浏览器 246 | driver.get(PostUrl) 247 | 248 | elem_user = driver.find_element_by_name('number') 249 | elem_psw = driver.find_element_by_name('passwd') 250 | 251 | 252 | #选择我的图书馆,点击后才能看到输入账号密码 253 | click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]") 254 | click_first.click() 255 | elem_user.send_keys('S315080092') 256 | elem_psw.send_keys('xxxxxxxx') 257 | 258 | #点击登录 259 | click_second = driver.find_element_by_name('submit') 260 | click_second.click() 261 | print 'log in...' 262 | time.sleep(1) 263 | 264 | #定位新页面元素,将handle重定位即可 265 | 266 | driver.switch_to_window(driver.window_handles[1])#定位弹出的第一个页面,也就是当前页面 267 | driver.find_element_by_xpath("/html/body/div[4]/div/div/ul/li[3]/a").click() 268 | driver.save_screenshot('image_booklist.jpg') 269 | print 'turning to the mylib...' 270 | time.sleep(1)#搜索结果页面停留片刻 271 | 272 | #driver.switch_to_window(driver.window_handles[1]) 273 | #没有跳出新窗口就是在同一页面的! 274 | for i in range(2,30):#这里限定是29本书,一般我们都不会借那么多书的 275 | try: 276 | driver.find_element_by_xpath("/html/body/div[4]/div/div[2]/table/tbody/%s/td[8]/div/input"%('tr[%s]'%i)).click() 277 | print 'renewing...the %d\'th book renewed '%(i-1) 278 | except: 279 | print '%d books have been renewed !'%(i-2) 280 | a=i-2 281 | time.sleep(4) 282 | driver.save_screenshot('image_done.jpg') 283 | print 'the picture is opening...please wait...' 284 | break 285 | time.sleep(2) 286 | driver.close() 287 | driver.quit() 288 | 289 | def show_img(): 290 | im_check=Image.open('image_booklist.jpg') 291 | im_check.show() 292 | im_done =Image.open('image_done.jpg') 293 | im_done.show() 294 | ``` 295 | > 然后是程序入口 296 | 297 | ``` 298 | # -*- coding: utf-8 -*- 299 | #Author:哈士奇说喵 300 | 301 | from Tkinter import * 302 | import tkMessageBox 303 | 304 | def check_renew(): 305 | print 'checking and renewing...' 306 | tkMessageBox.showinfo('提示','执行速度取决于网速和电脑,能等着就按"确定"\n(请允许phantomjs.exe访问网络)\nBTW 你现在按啥都不好使,程序照样执行(*゜Д゜)σ凸') 307 | from Selenium_PhantomJS_lib import show_img 308 | show_img()#show一下预约前和预约后截图,好确认 309 | tkMessageBox.showinfo('提示','已执行成功!\n(若没有弹出图片则请自行打开程序目录)') 310 | 311 | #主框架部分 312 | root = Tk() 313 | root.title('图书馆查询续约(哈尔滨工程大学专版)--by 哈士奇说喵') 314 | label=Label(root,text=' 图书馆一键查询与续约cmd版本 (✪ω✪) ') 315 | button_check=Button(root,text='查询书单并续期━Σ(゚Д゚|||)━开启cmd无形装逼模式 ',background='green',command=check_renew) 316 | 317 | label.pack() 318 | button_check.pack() 319 | root.mainloop() 320 | ``` 321 | > 之后启动的画面应该是这样的 322 | 323 | ![这里写图片描述](http://img.blog.csdn.net/20160717084827560) 324 | 325 | > 最后完成的画面应该是这样的,截图,确认框,cmd窗口,一个都不少; 326 | 327 | ![这里写图片描述](http://img.blog.csdn.net/20160717084911670) 328 | 329 | ---------- 330 | 331 | 332 | 333 | 原理和上篇并没有什么区别,只是调用了一个`phantomjs.exe`文件而已,实际上的处理都是这个exe在进行处理的,所以,在进行打包的时候,打包出来的exe需要和此文件在一个文件夹下才可以,就像这样 334 | 335 | ![这里写图片描述](http://img.blog.csdn.net/20160717085228015) 336 | 337 | 而如何用pyinstaller进行打包操作请见[如何将python程序打包成exe](http://blog.csdn.net/mrlevo520/article/details/51840217) 338 | 339 | ---------- 340 | 341 | 遇到问题和解决方案 342 | ---------- 343 | 344 | 1.找不到执行文件,phantomjs.exe 345 | 1.解决方案:把phantomjs.exe添加到工作路径下,最方便的方法就是,你的工程在哪,直接添加到工程文件夹下就可以了 346 | 347 | ---------- 348 | 2.截图的图片没有显示出来,或者提示”在禁用UAC时无法激活此应用“ 349 | 2.解决方案:图片有没有显示,可以看有没有调用show方法,如果调用了,那在自己电脑测试肯定是没有问题的,我在测试别的电脑的时候遇到UAC问题,直接启用就可以了,一般没有问题的,如果不想麻烦启动,那就直接去工作文件夹下手动打开看,截图已保存在本地的工作路径下的。 350 | 351 | ---------- 352 | 353 | 最后 354 | -- 355 | 这个程序是可以打包在别的电脑进行运行的,不过账号和密码我都直接输在程序里面了,而且也只是我自己学校的专版,主要还是自己用,如果有哈尔滨工程大学的小伙伴想用,可联系我,或者你只要自己改个账号密码参数就可以了,前提是你有完整的python开发环境。 356 | 357 | ---------- 358 | 359 | 致谢 360 | ---- 361 | - [@崔庆才--Python爬虫实战七之计算大学本学期绩点](http://cuiqingcai.com/997.html) 362 | - [@崔庆才--Python爬虫利器五之Selenium的用法](http://cuiqingcai.com/2599.html) 363 | - [@崔庆才--Python爬虫利器四之PhantomJS的用法](http://cuiqingcai.com/2577.html) 364 | - [@milkty--webdriver(python)学习笔记一 ](http://www.cnblogs.com/kongzhongqijing/p/3532082.html?utm_source=tuicool&utm_medium=referral) 365 | - [@buptlrw--Python抓取网页动态数据——selenium webdriver的使用 ](http://blog.csdn.net/buptlrw/article/details/48828201) 366 | 367 | -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/007-Firefox_Set01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/007-Firefox_Set01.jpg -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/007-Firefox_Set02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/007-Firefox_Set02.jpg -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/qingwalv-AutodownloadCSVfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Author:qingwalv 4 | 5 | import os 6 | from selenium import webdriver 7 | import selenium.webdriver.support.ui as ui 8 | import time 9 | import random 10 | 11 | #载入Firefox浏览器的本机Profile打开需要的网页。 12 | PostUrl = "https://www.printos.com/pb/#/printBeatHomeView" #填写需要下载的页面地址 13 | #指定本机Profile的路径,也可以driver.set_preference()单独设置多个默认参数,包括浏览器插件。 14 | profile = webdriver.FirefoxProfile(r'C:\Users\cuiyan\AppData\Roaming\Mozilla\Firefox\Profiles\yn80ouvt.default') 15 | driver = webdriver.Firefox(profile) #火狐浏览器使用本机的Profile,提前设置好需要的参数,这里主要用于默认下载路径和默认下载方式。 16 | driver.get(PostUrl) 17 | time.sleep(random.randint(10, 12)) 18 | 19 | #选择HP indigo Press Performance卡片 20 | click_wbic = driver.find_element_by_xpath("html/body/gz-body-wrap/div/div/md-content/gz-view-container/ng-transclude/ui-view/div/div/div/div[2]/devices-toolbar/div/div[2]/div/div[3]").click() 21 | print('Device...') 22 | time.sleep(random.randint(3, 6)) 23 | 24 | #等刷出元素 25 | wait1 = ui.WebDriverWait(driver,20) 26 | wait1.until(lambda driver: driver.find_element_by_xpath("html/body/gz-body-wrap/div/div/md-content/gz-view-container/ng-transclude/ui-view/div/div/div/div[1]/div[1]/md-content/div/md-tabs/md-tabs-content-wrapper/md-tab-content[2]/div/md-content/press-state-distribution/div/div[1]/div/div[1]/div[1]/div[2]/pb-custom-export/md-menu/div[1]/i")) 27 | 28 | #点击下载按钮 29 | click_dbtn = driver.find_element_by_xpath("html/body/gz-body-wrap/div/div/md-content/gz-view-container/ng-transclude/ui-view/div/div/div/div[1]/div[1]/md-content/div/md-tabs/md-tabs-content-wrapper/md-tab-content[2]/div/md-content/press-state-distribution/div/div[1]/div/div[1]/div[1]/div[2]/pb-custom-export/md-menu/div[1]/i").click() 30 | print('Start...') 31 | time.sleep(random.randint(2, 4)) 32 | 33 | #在弹出的菜单中选择需要下载的文件 34 | driver.switch_to_window(driver.window_handles[0]) #定位弹出的第一个页面,也就是当前页面 35 | click_markdown = driver.find_element_by_xpath("html/body/div[3]/md-menu-content/md-menu-item[4]").click() 36 | print('Downloading...') 37 | time.sleep(random.randint(2, 4)) 38 | 39 | #关闭浏览器 40 | time.sleep(random.randint(20, 25)) 41 | driver.close() 42 | -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/furtherProject/qingwalv-AutodownloadCSVfile/qingwalv-AutodownloadCSVfiles.md: -------------------------------------------------------------------------------- 1 | # Auto Download CSV files 2 | 3 | - 项目作用:自动下载文件,比如定期更新的文件。 4 | - 自动下载,计划未来做成每日定时下载。 5 | - 载入Firefox本机Profile可以免登录。 6 | - 环境: 7 | - python3.6+win7 8 | - Firefox 55.0.3 9 | - 效果图: 10 | - 由于网速太慢,效果图GIF太长, 11 | - 增加了两张图片,就是下载默认设置。这部分也可以在python代码中用driver.set_preference()实现。 12 | - 遇到的问题及解决方案: 13 | - 有下载链接的不需要这种方式,主要是点击后有菜单的下载按钮。 14 | - 下载中选择载入profile的方式来解决弹出下载框的问题,结果载入后发现不需要登录了,但是这里要注意,一般网页登录后和登录前的Xpath是不太一样的。 15 | - 未来需要添加csv文件合并及处理。 16 | -------------------------------------------------------------------------------- /MrLevo520-自动续借图书馆书籍/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/Tutorial_豆瓣数据爬取与分析.md: -------------------------------------------------------------------------------- 1 | ***From [Python自定义豆瓣电影种类,排行,点评的爬取与存储(初级)](http://blog.csdn.net/mrlevo520/article/details/51966992)*** 2 | 3 | > 更多豆瓣系列文章请看→_→[豆瓣数据爬取与分析及gui的制作](http://blog.csdn.net/column/details/16324.html) 4 | 5 | - Python 2.7 6 | - IDE Pycharm 5.0.3 7 | - Firefox 47.0.1 8 | 9 | > 具体Selenium和PhantomJS配置及使用请看[调用PhantomJS.exe自动续借图书馆书籍](http://blog.csdn.net/mrlevo520/article/details/51930741) 10 | 11 | 12 | 13 | ---------- 14 | 网上一溜豆瓣TOP250---有意思么? 15 | ---------- 16 | 17 | 起因 18 | -- 19 | > 就是想写个豆瓣电影的爬取,给我电影荒的同学。。。。当然自己也练手啦 20 | 21 | ---------- 22 | 23 | 24 | 目的 25 | -- 26 | 1. 根据用户输入,列出豆瓣高分TOP(用户自定义)的电影,链接,及热评若干。 27 | 2. 制作不需要Python环境可运行的exe,但由于bug未修复,需要火狐浏览器支持 28 | 29 | ---------- 30 | 31 | 方案 32 | -- 33 | > 使用PhantomJS+Selenium+Firefox实现 34 | 35 | ---------- 36 | 37 | 实现过程 38 | ---- 39 | 1. get到首页后,根据选择,点击种类,然后根据输入需求,进行排序 40 | 2. 抓取每个电影及超链接,进入超链接后,抓取当前电影的热评及长评 41 | 3. 当用户所要求TOP数目大于第一页的20个时候,点击加载更多,再出现20个电影,重复2操作 42 | 43 | > 以豆瓣高分,然后按评分排序的点击过程(其余操作一致,先种类后排序选择,再爬) 44 | 45 | ![这里写图片描述](http://img.blog.csdn.net/20160719165615514) 46 | 47 | ---------- 48 | 49 | 50 | 实现代码 51 | ---- 52 | 53 | ``` 54 | # -*- coding: utf-8 -*- 55 | #Author:哈士奇说喵 56 | #爬豆瓣高分电影及hot影评 57 | 58 | from selenium import webdriver 59 | import selenium.webdriver.support.ui as ui 60 | import time 61 | 62 | 63 | print "---------------system loading...please wait...---------------" 64 | SUMRESOURCES = 0 #全局变量 65 | driver_detail = webdriver.PhantomJS(executable_path="phantomjs.exe") 66 | #driver_item=webdriver.PhantomJS(executable_path="phantomjs.exe") 67 | driver_item=webdriver.Firefox() 68 | url="https://movie.douban.com/" 69 | #等待页面加载方法 70 | wait = ui.WebDriverWait(driver_item,15) 71 | wait1 = ui.WebDriverWait(driver_detail,15) 72 | 73 | 74 | #获取URL和文章标题 75 | 76 | def getURL_Title(): 77 | global SUMRESOURCES 78 | 79 | ############################################################################## 80 | #需要键入想要获取的信息,比如种类,排序方式,想看多少内容 81 | ############################################################################## 82 | 83 | print "please select:" 84 | kind=input("1-Hot\n2-Newest\n3-Classics\n4-Playable\n5-High Scores\n6-Wonderful but not popular\n7-Chinese film\n8-Hollywood\n9-Korea\n10-Japan\n11-Action movies\n12-Comedy\n13-Love story\n14-Science fiction\n15-Thriller\n16-Horror film\n17-Cartoon\nplease select:") 85 | print "--------------------------------------------------------------------------" 86 | sort=input("1-Sort by hot\n2-Sort by time\n3-Sort by score\nplease select:") 87 | print "--------------------------------------------------------------------------" 88 | number = input("TOP ?:") 89 | print "--------------------------------------------------------------------------" 90 | ask_long=input("don't need long-comments,enter 0,i like long-comments enter 1:") 91 | print "--------------------------------------------------------------------------" 92 | global save_name 93 | save_name=raw_input("save_name (xx.txt):") 94 | print "---------------------crawling...---------------------" 95 | 96 | driver_item.get(url) 97 | 98 | ############################################################################## 99 | #进行网页get后,先进行电影种类选择的模拟点击操作,然后再是排序方式的选择 100 | #最后等待一会,元素都加载完了,才能开始爬电影,不然元素隐藏起来,不能被获取 101 | #wait.until是等待元素加载完成! 102 | ############################################################################## 103 | 104 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div/div/label[%s]"%kind)) 105 | driver_item.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div/div/label[%s]"%kind).click() 106 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div[3]/div/label[%s]"%sort)) 107 | driver_item.find_element_by_xpath("//div[@class='fliter-wp']/div/form/div[3]/div/label[%s]"%sort).click() 108 | 109 | num=number+1#比如输入想看的TOP22,那需要+1在进行操作,细节问题 110 | time.sleep(2) 111 | 112 | #打开几次“加载更多” 113 | num_time = num/20+1 114 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']")) 115 | 116 | for times in range(1,num_time): 117 | time.sleep(1) 118 | driver_item.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']").click() 119 | time.sleep(1) 120 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]"%num)) 121 | #print '点击\'加载更多\'一次' 122 | 123 | #使用wait.until使元素全部加载好能定位之后再操作,相当于try/except再套个while把 124 | 125 | for i in range(1,num): 126 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]"%num)) 127 | list_title=driver_item.find_element_by_xpath("//div[@class='list']/a[%d]"%i) 128 | print '----------------------------------------------'+'NO' + str(SUMRESOURCES +1)+'----------------------------------------------' 129 | print u'电影名: ' + list_title.text 130 | print u'链接: ' + list_title.get_attribute('href') 131 | #print unicode码自动转换为utf-8的 132 | 133 | 134 | #写入txt中部分1 135 | list_title_wr=list_title.text.encode('utf-8')#unicode码,需要重新编码再写入txt 136 | list_title_url_wr=list_title.get_attribute('href') 137 | 138 | Write_txt('\n----------------------------------------------'+'NO' + str(SUMRESOURCES +1)+'----------------------------------------------','',save_name) 139 | Write_txt(list_title_wr,list_title_url_wr,save_name) 140 | 141 | SUMRESOURCES = SUMRESOURCES +1 142 | 143 | try:#获取具体内容和评论。href是每个超链接也就是资源单独的url 144 | getDetails(str(list_title.get_attribute('href')),ask_long) 145 | except: 146 | print 'can not get the details!' 147 | 148 | 149 | ############################################################################## 150 | #当选择一部电影后,进入这部电影的超链接,然后才能获取 151 | #同时别忽视元素加载的问题 152 | #在加载长评论的时候,注意模拟点击一次小三角,不然可能会使内容隐藏 153 | ############################################################################## 154 | def getDetails(url,ask_long): 155 | 156 | driver_detail.get(url) 157 | wait1.until(lambda driver: driver.find_element_by_xpath("//div[@id='link-report']/span")) 158 | drama = driver_detail.find_element_by_xpath("//div[@id='link-report']/span") 159 | print u"剧情简介:"+drama.text 160 | drama_wr=drama.text.encode('utf-8') 161 | Write_txt(drama_wr,'',save_name) 162 | print "--------------------------------------------Hot comments TOP----------------------------------------------" 163 | for i in range(1,5):#四个短评 164 | try: 165 | comments_hot = driver_detail.find_element_by_xpath("//div[@id='hot-comments']/div[%s]/div/p"%i) 166 | print u"最新热评:"+comments_hot.text 167 | comments_hot_wr=comments_hot.text.encode('utf-8') 168 | Write_txt("--------------------------------------------Hot comments TOP%d----------------------------------------------"%i,'',save_name) 169 | Write_txt(comments_hot_wr,'',save_name) 170 | except: 171 | print 'can not caught the comments!' 172 | 173 | 174 | #加载长评 175 | if ask_long==1: 176 | try: 177 | driver_detail.find_element_by_xpath("//img[@class='bn-arrow']").click() 178 | #wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='review-bd']/div[2]/div/div")) 179 | time.sleep(1) 180 | #解决加载长评会提示剧透问题导致无法加载 181 | comments_get = driver_detail.find_element_by_xpath("//div[@class='review-bd']/div[2]/div") 182 | if comments_get.text.encode('utf-8')=='提示: 这篇影评可能有剧透': 183 | comments_deep=driver_detail.find_element_by_xpath("//div[@class='review-bd']/div[2]/div[2]") 184 | else: 185 | comments_deep = comments_get 186 | print "--------------------------------------------long-comments---------------------------------------------" 187 | print u"深度长评:"+comments_deep.text 188 | comments_deep_wr=comments_deep.text.encode('utf-8') 189 | Write_txt("--------------------------------------------long-comments---------------------------------------------\n",'',save_name) 190 | Write_txt(comments_deep_wr,'',save_name) 191 | except: 192 | print 'can not caught the deep_comments!' 193 | 194 | 195 | ############################################################################## 196 | #将print输出的写入txt中查看,也可以在cmd中查看,换行符是为了美观 197 | ############################################################################## 198 | def Write_txt(text1='',text2='',title='douban.txt'): 199 | 200 | with open(title,"a") as f: 201 | for i in text1: 202 | f.write(i) 203 | f.write("\n") 204 | for j in text2: 205 | f.write(j) 206 | f.write("\n") 207 | 208 | def main(): 209 | 210 | getURL_Title() 211 | driver_item.quit() 212 | 213 | main() 214 | 215 | ``` 216 | 217 | 上面的代码是可以实现的,但需要Firefox的配合,因为我其中一个引擎调用了Firefox,另一个抓评论的用了PhantomJS。 218 | 219 | ---------- 220 | 221 | 222 | 实现效果 223 | ---- 224 | 这里直接上传打包成exe后的形式,如何打包exe请看[将python打包成exe](http://blog.csdn.net/mrlevo520/article/details/51840217) 225 | 226 | ![这里写图片描述](http://img.blog.csdn.net/20160720171437296) 227 | 228 | > 存入的txt文件 229 | 230 | ![这里写图片描述](http://img.blog.csdn.net/20160720171618984) 231 | 232 | > 因为打包成exe必须是中文的键入,所以没办法,我改成英文来着,不然会出现这种情况。。。 233 | 234 | ![这里写图片描述](http://img.blog.csdn.net/20160720171716187) 235 | 236 | > 输出内容是没有问题的。。。。。。 237 | 238 | ---------- 239 | 240 | 241 | 问题及解决方案 242 | ---- 243 | 244 | Q: 使用PhantomJS和Firefox出现不同效果的问题,第21个回到起点。 245 | 246 | A: 解决方案,暂且我也没有找到,只有调用Firefox然后完事后再关闭,分析请见[伪解决Selenium中调用PhantomJS无法模拟点击(click)操作 ](http://blog.csdn.net/mrlevo520/article/details/51958161) 247 | 248 | ---------- 249 | Q: 在对unicode输出在txt出现的问题,但是在print可以直接中文输出的。 250 | 251 | A: 解决方案:详见[Python输出(print)内容写入txt中保存 ](http://blog.csdn.net/mrlevo520/article/details/51967311) 252 | 253 | ---------- 254 | 255 | 256 | Pay Attention 257 | ------------- 258 | > 这里和上篇[ 伪解决Selenium中调用PhantomJS无法模拟点击(click)操作 ](http://blog.csdn.net/mrlevo520/article/details/51958161) 259 | 260 | 这里解决的问题和昨天的Pay Attention是一样的,本来程序也是增强性补充而已,所以重复了。 261 | 262 | Q: 元素无法定位问题 263 | 264 | A: 首先查看是不是隐藏元素,其次再看自己的规则有没有写错,还有就是是不是页面加载未完成,详见[解决网页元素无法定位(NoSuchElementException: Unable to locate element)的几种方法 ](http://blog.csdn.net/mrlevo520/article/details/51954203) 265 | 266 | ---------- 267 | Q: 只采集自己需要的数据,剔除无用数据,比如说,刚开始我用 268 | 269 | ``` 270 | driver_detail.find_elements_by_xpath 271 | ``` 272 | 273 | 然后写个取出list中元素的方法,但是这样的话,一个便签下内容未必太多,并不是我想要的如图: 274 | 275 | ![这里写图片描述](http://img.blog.csdn.net/20160719172858533) 276 | 277 | 比如说,我只想要红色的部分,那么,采取elements就不太好处理。 278 | 279 | A: 我采用的方法是格式化字符串!这个方法我在[Selenium+PhantomJS自动续借图书馆书籍(下)](http://blog.csdn.net/mrlevo520/article/details/51930741)也用过,根据元素的特性,可以发现,每个热评的正文标签不一样的,其余标签一样,只要格式化正文标签即可,像这样 280 | 281 | ``` 282 | for i in range(1,5):#取了前四条热评 283 | try: 284 | comments = driver_detail.find_element_by_xpath("//div[@id='hot-comments']/div[%s]/div/p"%i) 285 | print u"最新热评:"+comments.text 286 | except: 287 | print 'can not caught comments!' 288 | ``` 289 | 290 | ---------- 291 | Q: 一个引擎干有个事!我现在没办法,只有将第一个需要处理的页面用Firefox来处理,之后评论用PhantomJS来抓取,之后可以用quit来关闭浏览器,但是启动浏览器还是会耗费好多资源,而且挺慢,虽然PhantomJS也很慢,我12G内存都跑完了。。。。。。看样子是给我买8x2 16G双通道的借口啊。 292 | 293 | ---------- 294 | Q: 备注不标准也会导致程序出错,这个是我没想到的,我一直以为在'''备注'''之间的都可以随便来,结果影响程序运行了,之后分模块测试才注意到这个问题,也是以前没有遇到过的,切记!需要规范自己代码,特别是像Python这样缩进是灵魂的语言。。。。 295 | 296 | ---------- 297 | Q: 补充,**长评论的抓取** 298 | 299 | ![这里写图片描述](http://img.blog.csdn.net/20160720174033625) 300 | 301 | 这是点击之后的图,可以看到元素定位也是不一样的,注意 302 | 303 | ![这里写图片描述](http://img.blog.csdn.net/20160720174050807) 304 | 305 | ---------- 306 | 307 | 致谢 308 | -- 309 | - [@MrLevo520--伪解决Selenium中调用PhantomJS无法模拟点击(click)操作](http://blog.csdn.net/mrlevo520/article/details/51958161) 310 | - [@MrLevo520--Python输出(print)内容写入txt中保存](http://blog.csdn.net/mrlevo520/article/details/51967311) 311 | - [@MrLevo520--解决网页元素无法定位(NoSuchElementException: Unable to locate element)的几种方法 ](http://blog.csdn.net/mrlevo520/article/details/51954203) 312 | - [@Eastmount--[Python爬虫] Selenium+Phantomjs动态获取CSDN下载资源信息和评论 ](http://blog.csdn.net/eastmount/article/details/47907341) 313 | - [@Eastmount--[Python爬虫] 在Windows下安装PIP+Phantomjs+Selenium ](http://blog.csdn.net/eastmount/article/details/47785123) 314 | - [@MrLevo520--解决Selenium弹出新页面无法定位元素问题(Unable to locate element)](http://blog.csdn.net/mrlevo520/article/details/51926145) 315 | 316 | 317 | 318 | 319 | 320 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/ZLFlyApple-douban/README.md: -------------------------------------------------------------------------------- 1 | - Python 3.6 2 | - Jupyter Notebook 5.0 3 | - ECharts 4 | 5 | ---------- 6 | 7 | 起因 8 | -- 9 | > DT君Python精英群第9次作业,使用selenium爬取豆瓣电影-冷门佳片Top400(按热度排序)的豆瓣评分及相关Tag(文件 douban400MovieDetail)。 10 | 11 | ---------- 12 | 13 | 目的 14 | -- 15 | 1. 对冷门佳片按Tag进行分组统计 16 | 2. 使用ECharts进行结果展示 17 | 3. 学习Pandas的基本使用 18 | 19 | ---------- 20 | 21 | 方案 22 | -- 23 | > 使用 Jupyter Notebook + Pandas + ECharts 为开发、统计、结果展示组合。 24 | 25 | ---------- 26 | 27 | 实现过程 28 | ---- 29 | 1. 将爬取的电影数据按格式存入Pandas 的DataFrame中。 30 | 2. 对感兴趣的方向、内容进行统计。 31 | 3. 根据ECharts相关图表的数据存储结构进行统计结果输出、展示。 32 | 33 | > 以豆瓣高分,然后按评分排序的点击过程(其余操作一致,先种类后排序选择,再爬) 34 | 35 | ![这里写图片描述](http://img.blog.csdn.net/20160719165615514) 36 | 37 | ---------- 38 | 39 | 40 | 实现代码 41 | ---- 42 | 43 | ```python 44 | # -*- coding: utf-8 -*- 45 | #Author:FlyApple 46 | #分析豆瓣电影冷门 Top400 47 | 48 | import re 49 | import pandas as pd 50 | #################################################### 51 | #读取原始文件,分别存储list 52 | #################################################### 53 | movies = [] 54 | movie = [] 55 | sum = 0 #记录读取电影数量 56 | with open('C:/Users/FlyApple/douban400MovieDetail',encoding = 'ansi') as f: 57 | for line in f: 58 | #找到数据头,开始处理 59 | if '-----NO' in line: 60 | movie.clear() 61 | sum = sum +1 62 | count = 0 #对原始数据文件按行计数 63 | point = 0 #处理“导演”、“语言”等项目的数据缺失问题 64 | no = int(re.findall("\d+",line)[0]) 65 | movie.append(int(no)) 66 | ml = list(movie) #没有这句,movies中的元素都相同 67 | print(ml,movie) 68 | movies.append(ml) 69 | else: 70 | if count == 0: 71 | name = (line.split(' ')[0]).strip() 72 | ml.append(name) 73 | elif count ==1: 74 | year = int(line.split(':')[-1]) 75 | ml.append(year) 76 | elif count ==3: 77 | score = float(line) 78 | ml.append(score) 79 | elif count ==4: 80 | people = int(re.findall("\d+",line)[0]) 81 | ml.append(people) 82 | elif "导演" in line: 83 | dire = (line.split(': ')[-1]).strip() 84 | point += 1 85 | ml.append(dire) 86 | elif "类型" in line: 87 | dire = (line.split(': ')[-1]).strip() 88 | if point<1: 89 | point+=1 90 | ml.append('') 91 | point += 1 92 | ml.append(dire) 93 | elif "国家" in line: 94 | dire = (line.split(': ')[-1]).strip() 95 | if point <1: 96 | point+=1 97 | ml.append('') 98 | if point <2: 99 | point+=1 100 | ml.append('') 101 | point += 1 102 | ml.append(dire) 103 | elif "语言" in line: 104 | dire = (line.split(': ')[-1]).strip() 105 | if point <1: 106 | point+=1 107 | ml.append('') 108 | if point <2: 109 | point+=1 110 | ml.append('') 111 | if point <3: 112 | point+=1 113 | ml.append('') 114 | point += 1 115 | ml.append(dire) 116 | elif "片长" in line: 117 | length = int(re.findall("\d+",line)[0]) 118 | if point <1: 119 | point+=1 120 | ml.append('') 121 | if point <2: 122 | point+=1 123 | ml.append('') 124 | if point <3: 125 | point+=1 126 | ml.append('') 127 | if point <4: 128 | point+=1 129 | ml.append('') 130 | point += 1 131 | ml.append(length) 132 | count += 1 133 | #################################################### 134 | #将将list格式数据转为pandas.DataFrame数据并处理 135 | #################################################### 136 | data1 = pd.DataFrame(movies, columns=['排名', '片名', '上映年份', '豆瓣评分','打分人数','导演','类型','国家','语言','片长']) 137 | 138 | #################################################### 139 | #统计处理1: 140 | #按“上映年份”等Tag简单计数 141 | #################################################### 142 | 143 | kkk = data1.groupby(['上映年份']).size() 144 | #以下按ECharts条形图数据格式输出 145 | a = ((kkk.values)) 146 | b = ((kkk.index)) 147 | for kk in a: 148 | print(str(kk)+",") 149 | for kk in b: 150 | print("'"+str(kk)+"',") 151 | 152 | 153 | #################################################### 154 | #统计处理2: 155 | #按“上映年份”等按区间分组计数(桶分析) 156 | #################################################### 157 | 158 | #按year划分区间,含左不含右,例如[1920,1930) 159 | year = [1920,1930,1940,1950,1960,1970,1980,1990,2000,2010,2020] 160 | yearcats = pd.cut(data1.loc[:,'上映年份'],year,right=False) 161 | grouped = data1.groupby(yearcats) 162 | kkk=grouped.describe() 163 | 164 | a = ((kkk.values)) 165 | b = ((kkk.index)) 166 | for kk in a: 167 | print(str(kk[25])+",") 168 | for kk in b: 169 | print("'"+str(kk)+"',") 170 | 171 | 172 | #################################################### 173 | #统计处理3: 174 | #统计1980-2010期间电影的评分Top15 175 | #################################################### 176 | 177 | data2 = data1[(data1['上映年份'] > 1979) & (data1['上映年份'] < 2011)] 178 | aa=data2.sort_values(by = '豆瓣评分',ascending=False)#按评分进行排序 179 | aa[:15] 180 | 181 | 182 | #################################################### 183 | #统计处理4: 184 | #对于形如 喜剧 / 动画 / 奇幻 属于多个分类的项目, 185 | #首先进行元素拆分并分别统计 186 | #################################################### 187 | 188 | data2 = data1 189 | Name = "导演" 190 | genre_iter = (set(k.strip() for k in x.split("/")) for x in data2.loc[:,Name]) 191 | genres = sorted(set.union(*genre_iter)) 192 | 193 | dummies = pd.DataFrame(np.zeros((len(data2),len(genres))),columns=genres) 194 | for i,gen in enumerate(data2.loc[:,Name]): 195 | for k in gen.split("/"): 196 | dummies.ix[i,k.strip()]=1 #包含则记为1 197 | 198 | movies_windic = data2.join(dummies) 199 | 200 | result = movies_windic.sum()[10:].sort_values(ascending=False) 201 | #将结果按ECharts表格所需格式输出 202 | ``` 203 | 204 | ---------- 205 | 206 | 207 | 实现效果 208 | ---- 209 | ![DTTest](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%AF%BC%E6%BC%94%E4%BD%9C%E5%93%81%E5%9B%BE.png) 210 | 211 | ![](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87top400%E5%9B%BD%E5%AE%B6%E5%88%86%E5%B8%83.png) 212 | 213 | 214 | ![DTTest](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87top400%E5%B9%B4%E4%BB%A3%E5%88%86%E5%B8%83.png) 215 | 216 | ![DTTest](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87top400%E8%AF%AD%E8%A8%80%E5%88%86%E5%B8%83.png) 217 | 218 | ![1980-2010 Top15](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/1980-2010top15.PNG) 219 | 220 | 221 | 222 | ---------- 223 | 224 | 225 | 问题及解决方案 226 | ---- 227 | 228 | Q: movie中的更新与movies绑定? 229 | 230 | A: 列表嵌套还不太熟悉,目前的解决方法为设置ml = List(movie),movies.append(ml)。 231 | 232 | ---------- 233 | 234 | 参考 235 | ---- 236 | - 统计处理3、4部分主要参照《利用Python进行数据分析》相关部分。 237 | 238 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/README.md: -------------------------------------------------------------------------------- 1 | ## 环境说明 2 | 3 | - Python 2.7(代码根据py2.7编写) 4 | - selenium(别忘记配置Chrome driver 以及PhantomJS driver) 5 | - echarts(项目内已包含,无需特别配置) 6 | 7 | 8 | ----- 9 | # 项目目的 10 | 1. 使用selenium获取数据(基本元素定位,句柄切换等) 11 | 2. 数据基本处理(目前使用python文本处理) 12 | 3. 使用echarts进行数据可视化 13 | 14 | 15 | 16 | 17 | ## 快速开始 18 | 19 | ### 克隆(clone项目到本地)通过调试已有的代码熟悉基本功能 20 | 21 | 1.运行work9文件夹中的work9.py,进行数据扒取 22 | 23 | 预期:进入豆瓣电影页面→选择冷门电影→选择高分排序→逐个对影片信息进行扒取并在文件夹下生成douban.txt(记录扒取信息) 24 | 25 | 2.运行work9文件夹中的Demo1.html并观察其代码(熟悉echarts) 26 | 27 | 观察点:echarts元素以及数据的基本应用(坐标轴、折线图、饼图、排版布局) 28 | 29 | ## 代码简述 30 | 31 | 1. work9.py 32 | 33 | ![](https://raw.githubusercontent.com/cowboy231/Mini-Python-Project/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/163851.png) 34 | 35 | 36 | 37 | 2. echarts 38 | 39 | 图表由1个折线图和一个饼图组成 40 | 41 | ![2017-09-04_195451](https://raw.githubusercontent.com/cowboy231/Mini-Python-Project/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/195451.png) 42 | 43 | 折线图部分,坐标轴 44 | 45 | ![2017-09-09_170851](https://github.com/cowboy231/Mini-Python-Project/blob/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/170851.png) 46 | 47 | 数据填充 48 | 49 | ![2017-09-09_171821](https://raw.githubusercontent.com/cowboy231/Mini-Python-Project/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/171821.png) 50 | 51 | 饼图部分 52 | 53 | ​ ![2017-09-09_172311](https://raw.githubusercontent.com/cowboy231/Mini-Python-Project/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/172311.png) 54 | 55 | 使用echarts的图表工具转换数据 56 | 57 | ​ ![2017-09-09_174243](https://github.com/cowboy231/Mini-Python-Project/blob/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/174243.png) 58 | 59 | ## 延伸 60 | 61 | 目前的脚本功能仍不完善,以下简单列举几个可以延伸的点 62 | 63 | 1. selenium操作:可以补充登录操作 64 | 2. 数据处理:目前只是用split命令进行简单提取,可以使用正则表达式进行优化 65 | 3. 数据展示:折线图只填充了3组数据不能定位到具体影片,可以把片名和跳转链接嵌入进去 66 | 67 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163642.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163642.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163804.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163804.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163851.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/163851.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/170851.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/170851.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/171821.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/171821.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/172311.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/172311.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/174243.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/174243.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/195451.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/img/195451.png -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/work9/Demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ECharts 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/work9/douban.txt: -------------------------------------------------------------------------------- 1 | 走向共和|9.6|中国大陆|24033|2003-04-12 2 | 大明王朝1566|9.6|中国大陆|31098|2007-01-08 3 | 毛骗|9.6|中国大陆|14301|2015-09-10 4 | 红楼梦|9.5|中国大陆|59520|1987-05-02 5 | 罗小黑战记|9.5|中国大陆|12942|2011-03-18 6 | 走向共和|9.6|中国大陆|24033|2003-04-12 7 | 大明王朝1566|9.6|中国大陆|31098|2007-01-08 8 | 毛骗|9.6|中国大陆|14301|2015-09-10 9 | 红楼梦|9.5|中国大陆|59520|1987-05-02 10 | 罗小黑战记|9.5|中国大陆|12942|2011-03-18 11 | 西游记|9.4|中国大陆|79660|1986 12 | 曼食慢语|9.4|中国大陆|440|集数: 116 13 | 生存之民工|9.4|中国大陆|4796|2005-02-20 14 | 小明和王猫|9.4|中国大陆|951|2000 15 | 大盗贼|9.3|中国大陆|27836|1989 16 | 武林外传|9.3|中国大陆|111553|2006-01-02 17 | 毛骗|9.3|中国大陆|7831|2011-10-20 18 | 一路向南|9.3|英语|4824|20 19 | 福贵|9.2|中国大陆|2782|2005-11-25 20 | 红色|9.2|中国大陆|44375|2014-08-28 21 | 外乡人|9.2|中国大陆|2707|2009-10-20 22 | 三国演义|9.2|中国大陆|40411|1994-10-23 23 | 大宅门|9.2|中国大陆|51867|2001-04-15 24 | 走向共和|9.6|中国大陆|24136|2003-04-12 25 | 大明王朝1566|9.6|中国大陆|31316|2007-01-08 26 | 毛骗|9.6|中国大陆|14484|2015-09-10 27 | 红楼梦|9.5|中国大陆|59664|1987-05-02 28 | 罗小黑战记|9.5|中国大陆|12982|2011-03-18 29 | 西游记|9.4|中国大陆|79912|1986 30 | 曼食慢语|9.4|中国大陆|448|集数: 116 31 | 生存之民工|9.4|中国大陆|4814|2005-02-20 32 | 小明和王猫|9.4|中国大陆|955|2000 33 | 大盗贼|9.3|中国大陆|27853|1989 34 | 武林外传|9.3|中国大陆|112033|2006-01-02 35 | 毛骗|9.3|中国大陆|7935|2011-10-20 36 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/cowboy231-DouBan/work9/work9.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from selenium import webdriver 3 | import time 4 | import selenium.webdriver.support.ui as ui 5 | #shift-tab多行缩进(左) 6 | print 'please wait...system loading...' 7 | PostUrl = "https://www.douban.com/" 8 | driver=webdriver.Chrome()#用浏览器实现访问 9 | driver_detail=webdriver.PhantomJS() 10 | wait = ui.WebDriverWait(driver,15) 11 | wait1 = ui.WebDriverWait(driver_detail,15) 12 | driver.get(PostUrl) 13 | 14 | def getDetails(url): 15 | 16 | driver_detail.get(url) 17 | wait1.until(lambda driver: driver.find_element_by_xpath("//div[@id='link-report']/span")) 18 | 19 | def Write_txt(text1, text2,text3,text4,text5,title='douban.txt'): 20 | 21 | with open(title, "a") as f: 22 | print '开写' 23 | # print type(text1) 24 | # print type(text2) 25 | # print type(text3) 26 | # print type(text4) 27 | temp=str(text1)+"|"+text2+"|"+text3+"|"+text4+'|'+text5 28 | f.write(temp) 29 | f.write("\n") 30 | 31 | def get_info(start): 32 | x=[] 33 | for i in range(20): 34 | x.append(start) 35 | start=start+1 36 | for i in x: 37 | #wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]" % num)) 38 | list_title = driver.find_element_by_xpath("//div[@class='list']/a[%d]" % i) 39 | print '----------------------------------------------' + 'NO' + str( 40 | i) + '----------------------------------------------' 41 | 42 | name = list_title.text 43 | name = name.split(' ', 1) 44 | name = name[0].encode('utf-8') 45 | 46 | print u'电影名: ' 47 | print name 48 | print u'链接: ' + list_title.get_attribute('href') 49 | 50 | try: # 获取具体内容和评论。href是每个超链接也就是资源单独的url 51 | getDetails(str(list_title.get_attribute('href'))) 52 | strong = driver_detail.find_element_by_xpath('//*[@id="interest_sectl"]/div/div[2]/strong').text 53 | print u"分数" + strong 54 | PL = driver_detail.find_element_by_xpath('//*[@id="interest_sectl"]/div[1]/div[2]/div/div[2]/a').text 55 | PL = PL[:-3] 56 | print PL 57 | 58 | info = driver_detail.find_element_by_xpath('//*[@id="info"]').text 59 | info = info.split('\n', ) 60 | print u"信息" 61 | Gz = info[4] 62 | Sj = info[6] 63 | Gz = Gz.split(" ") 64 | Sj = Sj.split(" ") 65 | Sj = Sj[1] 66 | if Gz[0] == u"官方网站:": 67 | Gz = info[5] 68 | Gz = Gz.split(" ") 69 | Sj = info[7] 70 | try: 71 | Sj = Sj.split('(') 72 | Sj = Sj[0] 73 | except: 74 | print '只有一个上映日期' 75 | Gz = Gz[1] 76 | 77 | Sj = Sj.encode("utf-8") 78 | Gz = Gz.encode("utf-8") 79 | PL = PL.encode("utf-8") 80 | strong = strong.encode("utf-8") 81 | print type(name) 82 | print type(Gz) 83 | Write_txt(name, strong, Gz, PL, Sj) 84 | except Exception as e: 85 | print e 86 | print 'can not get the details!' 87 | 88 | print '---------------------------' 89 | driver.find_element_by_xpath('//*[@id="anony-nav"]/div[1]/ul/li[2]/a').click() 90 | print driver.window_handles 91 | driver.switch_to_window(driver.window_handles[1]) 92 | time.sleep(1) 93 | driver.find_element_by_xpath('//*[@id="db-nav-movie"]/div[2]/div/ul/li[3]/a').click() 94 | time.sleep(1) 95 | print driver.window_handles 96 | wait.until(lambda driver: driver.find_element_by_xpath('//*[@id="content"]/div/div[1]/div/div[2]/div[1]/form/div[1]/div[1]/label[6]')) 97 | driver.find_element_by_xpath('//*[@id="content"]/div/div[1]/div/div[2]/div[1]/form/div[1]/div[1]/label[6]').click() 98 | #选择高分排序 99 | driver.find_element_by_xpath('//*[@id="content"]/div/div[1]/div/div[2]/div[1]/form/div[3]/div[1]/label[3]/input').click() 100 | #开始爬取 101 | num=600 102 | # 打开几次“加载更多” 103 | num_time = num / 20 + 1 104 | wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']")) 105 | starttime=1 106 | for times in range(1, num_time): 107 | try: 108 | time.sleep(1) 109 | driver.find_element_by_xpath("//div[@class='list-wp']/a[@class='more']").click() 110 | time.sleep(1) 111 | t=num_time*20 112 | print '本次抓取的部分' 113 | print starttime, t 114 | get_info(starttime) 115 | starttime=starttime+20 116 | except Exception as e: 117 | print num_time 118 | print e 119 | break 120 | #wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='list']/a[%d]" % num)) -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/love-apple-豆瓣冷门电影分析/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/love-apple-豆瓣冷门电影分析/1.PNG -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/love-apple-豆瓣冷门电影分析/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrLevo520/Mini-Python-Project/951719ab33977d3812e74475cdfd63e337d3553a/MrLevo520-豆瓣数据爬取与分析/furtherProject/love-apple-豆瓣冷门电影分析/2.PNG -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/love-apple-豆瓣冷门电影分析/README.md: -------------------------------------------------------------------------------- 1 | ## **环境** 2 | > win10 + Python 3.6 + PyCharm 3 | 4 | --- 5 | ## **目的** 6 | > * 对爬取到的豆瓣冷门佳片做制片国家的空间分布图 7 | > * 分析片长与得分之间的关系 8 | 9 | --- 10 | ## **实现步骤** 11 | 1. 按照豆瓣得分爬取冷门佳片top200的相关信息并存储到csv中; 12 | 2. 调用有道在线翻译页面,逐个把“制片国家”中的中文信息翻译成英文,存储为csv中新的列; 13 | 3. 利用csv中的信息做数据分析。 14 | 15 | --- 16 | ## **实现代码** 17 | 18 | # 数据分析部分2017.09.01 19 | import pandas as pd 20 | from pyecharts import Map, Scatter 21 | 22 | 23 | #数据读取 24 | d = pd.read_csv(r"movie_new.csv", sep=",",encoding='gb18030') #自己的链接 25 | nation = d['trans_nation'].values 26 | new_nation = '/'.join(nation).split('/') #有些影片属于多个国家,使用这种方法将多个国家拆开 27 | print(new_nation) 28 | 29 | 30 | #new_nation中的很多国家的名称不规范,为了画出世界地图,统一替换成 pycharts中的使用的英文名称 31 | replace_rule = {'The United States':'United States', 'America':'United States', 'Us':'United States', 32 | 'West Germany':'Germany', 'former west Germany':'Germany', 'German':'Germany', 33 | 'The French':'France', 34 | 'The British':'United Kingdom', 'UK':'United Kingdom', 35 | 'South Korea':'Korea', 36 | 'Hong Kong':'China', 'The Chinese mainland':'China', 'Taiwan':'China', 37 | 'The Soviet union':'Russia', 38 | 'Czechoslovakia':'Czech Rep.', 39 | 'The Danish':'Denmark'} 40 | #rep = ['United States' if x == 'The United States' else x for x in new_nation] 41 | replace = [replace_rule[x] if x in replace_rule else x for x in new_nation] 42 | 43 | 44 | #统计值的个数,存储到字典中 45 | a = {} 46 | for i in replace: 47 | if replace.count(i) > 1: 48 | a[i] = replace.count(i) 49 | print(a) 50 | 51 | 52 | 53 | #画地区分布图,并根据影片数量多少做颜色渲染 54 | map = Map("豆瓣冷门佳片TOP200地区分布", subtitle="074——作业九", width=1200, height=600) 55 | attr,value = map.cast(a) 56 | print(map.cast(a)) 57 | map.add("", attr, value, maptype="world", is_visualmap=True, visual_text_color='#000', visual_range= [0,46]) 58 | map.show_config() 59 | map.render(r"D:\persenol\python\movie_map.html") 60 | 61 | 62 | #画片长与得分的散点图 63 | scatter = Scatter("豆瓣冷门佳片TOP200_片长与得分关系图", subtitle="074——作业九", width=1200, height=600) 64 | scatter.add("", d['time'].values, d['score'].values, yaxis_max= '10', yaxis_min= '7.5') 65 | scatter.show_config() 66 | scatter.render(r"D:\persenol\python\movie_score.html") 67 | 68 | --- 69 | # 实现效果 70 | ![豆瓣冷门佳片TOP200地区分布](1.PNG) 71 | ![豆瓣冷门佳片TOP200\_片长与得分关系图](2.PNG) 72 | --- 73 | # 所遇问题 74 | 1. 写入csv中文乱码:gb18030是更宽泛的unicode,可以防止中文乱码; 75 | 2. 写入csv有空行:设置参数dialect='unix',可以防止有空行。 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /MrLevo520-豆瓣数据爬取与分析/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /MrLevo520-验证码自动识别/Tutorial_验证码自动识别.md: -------------------------------------------------------------------------------- 1 | **From [Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录](http://blog.csdn.net/mrlevo520/article/details/51901579)** 2 | 3 | - Python 2.7 4 | - IDE Pycharm 5.0.3 5 | - Firefox浏览器:47.0.1 6 | - Selenium:[Selenium的介绍及使用,强烈推荐@ Eastmount的博客](http://blog.csdn.net/eastmount/article/details/47825633) 7 | - PIL : Pillow-3.3.0-cp27-cp27m-win_amd64.whl [PIL第三方库的下载](http://www.lfd.uci.edu/~gohlke/pythonlibs/#pil),[win下安装whl文件](http://www.cnblogs.com/2589-spark/p/4501816.html) 8 | - Pytesser:依赖于PIL ,Tesseract [了解pytesser及基本使用](http://blog.sina.com.cn/s/blog_5d56279201017fta.html) 9 | - Tesseract:3.0.2 [tesseract下载及安装](http://blog.csdn.net/wanghui2008123/article/details/37694307) 10 | 11 | 12 | ------ 13 | 14 | # 前言 15 | 16 | > 自动登陆时候遇到验证码,采用Tesseract+PIL进行识别和自动填充,不让验证码成为我们自动化登录的阻碍,哈哈哈 17 | 18 | ------ 19 | 20 | 21 | 22 | # Talk is cheap, Show me the code 23 | 24 | > **自动识别验证码模拟登陆,注意是自动**,一键登录,不是那种扫出验证码,然后手动输入登录!首先来代码实现吧! 25 | 26 | ``` 27 | # -*- coding: utf-8 -*- 28 | #Author:哈士奇说喵 29 | from selenium import webdriver 30 | import os 31 | import pytesser 32 | import sys,time 33 | from PIL import Image,ImageEnhance 34 | 35 | #shift+tab多行缩进(左) 36 | reload(sys) 37 | PostUrl = "http://yjsymis.hrbeu.edu.cn/gsmis/indexAction.do" 38 | 39 | driver=webdriver.Firefox() 40 | driver.get(PostUrl) 41 | 42 | 43 | i=0 44 | while 1:#sb登录系统,即使输对所有消息还是登不进去的,需要登录两次及以上 45 | 46 | i=i+1 47 | try: 48 | elem_user = driver.find_element_by_name('id') 49 | elem_psw = driver.find_element_by_name('password') 50 | elem_code = driver.find_element_by_name('checkcode') 51 | except: 52 | break 53 | #-------------------对验证码进行区域截图,好吧,这方法有点low------------------ 54 | driver.get_screenshot_as_file('C:\Users\MrLevo\image1.jpg')#比较好理解 55 | im =Image.open('C:\Users\MrLevo\image1.jpg') 56 | box = (516,417,564,437) #设置要裁剪的区域 57 | region = im.crop(box) #此时,region是一个新的图像对象。 58 | #region.show()#显示的话就会被占用,所以要注释掉 59 | region.save("e:/image_code.jpg") 60 | 61 | #------------------------------------------------------------------- 62 | 63 | #--------------ImageGrab.grab()直接可以区域截图,但是有bug,截图不全------- 64 | ''' 65 | bbox = (780, 0, 1020, 800) 66 | img = ImageGrab.grab() 67 | img.save("E:\image_code.jpg") 68 | img.show() 69 | ''' 70 | #-------------------------手动输入验证码:适用范围更广,但不够方便------------------------------ 71 | ''' 72 | response = opener.open(CaptchaUrl) 73 | picture = response.read() 74 | with open('e:/image.jpg', 'wb') as local: 75 | local.write(picture) 76 | # 保存验证码到本地 77 | 78 | #------------对于不能用pytesser+ocr进行识别,手动打开图片手动输入-------- 79 | # 打开保存的验证码图片 输入 80 | #SecretCode = raw_input('please enter the code: ') 81 | #---------------------------------------------------------------------- 82 | ''' 83 | 84 | #--------------------图片增强+自动识别简单验证码----------------------------- 85 | #time.sleep(3)防止由于网速,可能图片还没保存好,就开始识别 86 | def image_file_to_string(file): 87 | cwd = os.getcwd() 88 | try : 89 | os.chdir("C:\Users\MrLevo\Anaconda2\Lib") 90 | return pytesser.image_file_to_string(file) 91 | finally: 92 | os.chdir(cwd) 93 | im=Image.open("E:\\image_code.jpg") 94 | imgry = im.convert('L')#图像加强,二值化 95 | sharpness =ImageEnhance.Contrast(imgry)#对比度增强 96 | sharp_img = sharpness.enhance(2.0) 97 | sharp_img.save("E:\\image_code.jpg") 98 | #http://www.cnblogs.com/txw1958/archive/2012/02/21/2361330.html 99 | #imgry.show()#这是分布测试时候用的,整个程序使用需要注释掉 100 | #imgry.save("E:\\image_code.jpg") 101 | 102 | code= pytesser.image_file_to_string("E:\\image_code.jpg")#code即为识别出的图片数字str类型 103 | print code 104 | #打印code观察是否识别正确 105 | 106 | 107 | #---------------------------------------------------------------------- 108 | if i <= 2: # 根据自己登录特性,我这里是验证码失败一次,重填所有,失败两次,重填验证码 109 | elem_user.send_keys('S315080092') 110 | elem_psw.send_keys('xxxxxxxxxx') 111 | 112 | elem_code.send_keys(code) 113 | click_login = driver.find_element_by_xpath("//img[@src='main_images/images/loginbutton.gif']") 114 | click_login.click() 115 | 116 | 117 | #time.sleep(5)#搜索结果页面停留片刻 118 | #driver.save_screenshot('C:\Users\MrLevo\image.jpg') 119 | #driver.close() 120 | #driver.quit() 121 | ``` 122 | 123 | # Show Gif ( : 124 | 125 | > 第一次放动图,心理还有点小激动~ 126 | 127 | ![这里写图片描述](http://img.blog.csdn.net/20160713193103690) 128 | 129 | # 遇到问题及解决方法 130 | 131 | 1:验证码取得问题,因为每次刷新之后验证码动态刷新,所以如果不采用cookie的话(我还不太会用cookie),根本捉不到元素,这个我在下篇文章中采用cookie来登录的,但不是调用浏览器,这个跑远了,下次说。 132 | 1:解决方案:用了`driver.get_screenshot_as_file`方法,机智的进行全截图,然后采用PIL中的crop进行再截图操作,可能有人会说,为什么不采用`ImageGrab.grab()`函数来做,好吧,因为这个函数在win10上尽然!截不了全图!!自己试了才知道,btw,我的分辨率1920x1080,难道和分辨率有关?反正这个我截了好久都没有成功,到最后才想到,截全部看看,结果,tmd只有一半,我说怎么都找不到要截图的部分! 133 | 134 | ------ 135 | 136 | 2:验证码验证错误率高问题 137 | 2:解决方案,采用PIL强大的图像处理功能,我先将图片二值化,本来是蓝色字体的,,然后再进行对比度强化来锐化图片,然后再调用Tesseract.exe进行处理,提高的识别精度不是一点两点:看图比较,左1是用cookie抓的原图,右边是全景截图,再定位截图,再进行二值化和锐化处理的图,本来我想着用matlab做图像识别的,但是想想还要调用,感觉有点麻烦。。。 138 | 139 | ![这里写图片描述](http://img.blog.csdn.net/20160713202718096) 140 | 141 | ------ 142 | 143 | 3:调用Tesseract.exe问题 144 | 3:解决方案因为程序执行图像识别需要调用Tesseract.exe,所以必须把路径切到有这个exe的路径下,刚开始,以为和包依赖,结果根本没有识别出任何图!折腾一个多小时才写好验证码识别的问题----单独测试的确很重要,记一笔! 145 | ![这里写图片描述](http://img.blog.csdn.net/20160713211043265) 146 | 147 | ------ 148 | 149 | 4:登录失败问题--mdzz学校教务系统二次验证 150 | 4:解决方案,写了一个while循环,把主程序很大部分都扔进去了,目的也很明确,如果第一次登录失败,再重复进行登录,注意采用try试探元素是否仍然存在,except来抛出break结束循环,因为登录成功后,比如说`driver.find_element_by_name('id')`是不存在的!所以当这个元素在登陆后的界面找不到时,那就说明登录成功,ok,跳出循环,进行下一步操作。 151 | 152 | ------ 153 | 154 | 5:明明图片已截取,为什么没有识别 155 | 5:解决方案,这个我真的没想到,我一直以为可能因为save时候还没下载好,导致库中没有这张图,那就不能识别,但是我用time.sleep函数让它停下来缓缓,还是不行,我就很无语了,想了半天,可能是因为图片被占用!因为我有一个img.show()函数,为了检测有没有截取到标准的图,然后show之后这个图像就被占用了!就像你在编辑word时候,是无法删除word文档一样!果然在注释掉show之后,一切可行,真是差错查了小半天啊!! 156 | ![这里写图片描述](http://img.blog.csdn.net/20160713211120437) 157 | 158 | ------ 159 | 160 | 6:元素一切就位,为什么不执行操作 161 | 6:解决方案,这个有点脑残了,不过的确是我遇到的,还是记上一笔,然后骂自己一遍sb,没有click()你让它怎么处理!!!就像用cookie登录时候还有个ENTRY呢! 162 | 163 | ------ 164 | 165 | 7:两次验证失败后,用户名重复累加 166 | 7:解决方案,直接加了个变量,计数循环次数,观察到只要超过两次没有登录上,就会累加登录名和用户密码,直接写了个if进行判断,完事! 167 | 168 | ------ 169 | 170 | 8:im.crop(box)裁剪区域选择困难症 171 | 8:解决方案,多试几次,反正我是试出来的。。。。当然,你点击图片进行审查元素时候,可以看到图片大小,那么,你就可以知道横纵坐标差值多少,但是大范围区域还得自己试,如有更好的办法,请告知,以下为我截图实验次数,次数30+ 172 | ![这里写图片描述](http://img.blog.csdn.net/20160713204507521) 173 | 174 | ------ 175 | 176 | 9:导入不了Image,ImageEnhance 177 | 9:解决方案,因为PIL用的是第三方库,所以,采用的导入方式是这样的,多看看官方文档就可以,官方描述如下 178 | `Use `from PIL import Image` instead of `import Image`. ` 179 | 180 | ------ 181 | 182 | 10:找不到应该键入的元素 183 | 10:这个问题,请单击要输入的空白处右键,审查元素,就可以看到,然后根据`driver.find_element_by_`各种方法来定位元素,如果输入进行了隐藏,在当前页面找不到怎么办,就像如下图,需要先点击我的图书馆,才能看到输入的账户和密码,那么先找我的图书馆的元素,进行click操作,之后再找元素,一句话,把自己想成浏览器,阿不,把python想成浏览器。。。。。 184 | 185 | ![这里写图片描述](http://img.blog.csdn.net/20160713193212394) 186 | 187 | 上图的代码我也放上,大同小异,比有验证码的简单,但是多了一个click操作。 188 | 189 | ``` 190 | # -*- coding: utf-8 -*- 191 | #Author:哈士奇说喵 192 | from selenium import webdriver 193 | import time 194 | import sys 195 | 196 | 197 | #shift+tab多行缩进(左) 198 | reload(sys) 199 | PostUrl = "http://lib.hrbeu.edu.cn/#" 200 | driver=webdriver.Firefox() 201 | driver.get(PostUrl) 202 | 203 | elem_user = driver.find_element_by_name('number') 204 | elem_psw = driver.find_element_by_name('passwd') 205 | 206 | #选择我的图书馆,点击后才能看到输入账号密码 207 | click_first = driver.find_element_by_xpath("//ul[@id='imgmenu']/li[4]") 208 | click_first.click() 209 | elem_user.send_keys('S315080092') 210 | elem_psw.send_keys('xxxxxxxx') 211 | 212 | #点击登录 213 | click_second = driver.find_element_by_name('submit') 214 | click_second.click() 215 | 216 | time.sleep(5) 217 | #登陆后选择 218 | click_third = driver.find_element_by_xpath("//*[@id='mainbox']/div/div/ul/li/a") 219 | click_third.click() 220 | 221 | time.sleep(5)#搜索结果页面停留片刻 222 | #driver.save_screenshot('C:\Users\MrLevo\image.jpg') 223 | 224 | driver.close() 225 | driver.quit() 226 | ``` 227 | 228 | 229 | 230 | ------ 231 | 232 | 233 | 234 | # 致谢 235 | 236 | 237 | 238 | - [tesseract-ocr识别英文和中文图片文字以及扫描图片实例讲解 ](http://blog.csdn.net/wanghui2008123/article/details/37694307) 239 | - [用pytesser作图片验证码识别 ](http://blog.sina.com.cn/s/blog_5d56279201017fta.html) 240 | - [ [Python爬虫] Selenium实现自动登录163邮箱和Locating Elements介绍 ](http://blog.csdn.net/eastmount/article/details/47825633) 241 | - [ [Python爬虫] Selenium自动访问Firefox和Chrome并实现搜索截图 ](http://blog.csdn.net/eastmount/article/details/47799865) 242 | - [初试PIL及基本操作 ](http://blog.csdn.net/yockie/article/details/8498301) 243 | - [Python爬虫模拟登录带验证码网站_手动输入验证码版本](http://www.jb51.net/article/78498.htm) 244 | - [selenium-webdriver(python) (十五) -- 鼠标事件](http://www.cnblogs.com/fnng/p/3288444.html) -------------------------------------------------------------------------------- /MrLevo520-验证码自动识别/furtherProject/twolun-刷暴京东验证码/开发说明.md: -------------------------------------------------------------------------------- 1 | ## 前言 2 | 没有学校图书馆帐号的人被鄙视呀,尤其是什么哈士奇。偶然发现,某东居然有个验证码供我玩玩,而且这个验证码不是太难。 3 | 隧,决定搞它一搞 4 | 5 | #### 1. 搭建环境,python3.x 6 | ```python 7 | # 自己动手吧,自已动手丰衣足食 8 | from selenium import webdriver 9 | import time 10 | import os 11 | import pytesseract 12 | from PIL import ImageEnhance, Image 13 | 14 | # 根据引入包,自己安装吧, 15 | ``` 16 | 17 | #### 2. 启动浏览器驱动 18 | ```python 19 | dist_url = 'https://www.jd.com/' 20 | driver = webdriver.Chrome(executable_path='C:\Program Files (x86)\Google\Chrome\Application\chromedriver') 21 | driver.get(dist_url) 22 | 23 | # 此处我有话讲, 24 | # 小弟一直在用chrome浏览器,没有遵循哈士奇的要求使用Firfox,请多见识 25 | # 使用chrome最主要的是要配置chromedriver,这个也是百度一大堆 26 | ``` 27 | 28 | #### 3. 对页面上验证码部分进行截图 29 | ```python 30 | def crop_image_code(box=(510,512,569,537)): 31 | driver.get_screenshot_as_file('./screen.jpg') 32 | im = Image.open('./screen.jpg') 33 | # box = (510,512,569,537) # 设置要裁剪的区域 34 | region = im.crop(box) 35 | region.save('./image_code.png') 36 | return 'image_code.png' 37 | 38 | # 此处是主要的注意点是要手动测量截图区域, 39 | # 这个现在也好办的很,找个截图工具随便一测量就ok 40 | 41 | ``` 42 | 43 | #### 4. win上识别图片需要用到`Tesseract-OCR` 44 | ```python 45 | # Tesseract-OCR请百度自行安装,不要问我为什么要用它,我也不知道的 46 | 47 | def image_file_to_string(file): 48 | time.sleep(1) 49 | cwd = os.getcwd() 50 | try: 51 | os.chdir('C:\\Tesseract-OCR') 52 | imgfile = Image.open(file) 53 | return pytesseract.image_to_string(imgfile) 54 | finally: 55 | os.chdir(cwd) 56 | ``` 57 | #### 5. 识别验证码最主要的部分,暴力识别,不识别出势不罢休,非要刷死你 58 | ```python 59 | # 这里自己要找一个终止条件,否则识别正确了,再刷新验证码你就完蛋了 60 | # 其实最简单的也就是当前页面元素找不到的时候也就是页面已经跳转了,也即是成功了 61 | while True: 62 | time.sleep(1) 63 | try: 64 | elem_submit = driver.find_element_by_id('order-submit') 65 | elem_code = driver.find_element_by_id('checkcodeTxt') 66 | 67 | # 如果验证码已经有了,则跳过,等待页面跳转 68 | if getattr(elem_code, 'text'): 69 | continue 70 | # 终于到验证码的问题了 71 | img_code_path = crop_image_code(box=(740,101, 926,146)) 72 | img_code_2 = sharpness_image_code('./' + img_code_path) 73 | code = image_file_to_string('D:\\python-learning\learning-group\library-auto\\' + img_code_2) 74 | if code: 75 | # 填入验证码,并提交 76 | elem_code.send_keys(code) 77 | elem_submit.click() 78 | else: 79 | # 刷新验证码 80 | driver.find_element_by_id('orderCheckCodeImg').click() 81 | except Exception: 82 | if driver.window_handles[1]: 83 | driver.switch_to.window(driver.window_handles[1]) 84 | driver.get_screenshot_as_file('./success.jpg') 85 | break; 86 | ``` 87 | 88 | #### 6. 最后需要说明的一点 89 | ```python 90 | # 将页面滚动底部,识图验证码和提交 91 | time.sleep(2) 92 | js = 'window.scrollTo(0,document.body.scrollHeight)' 93 | driver.execute_script(js) 94 | ``` 95 | 96 | ## 我最后要说的话 97 | 1. 我为了满足客代表尽快收到pr的愿望,教程写的不是很仔细 98 | 2. 不知道是不是我刷京东验证的原因,现在京东提交订单时的验证码不在了 99 | 100 | 101 | [完整源代码](https://github.com/twolun/python-learning/blob/master/learning-group/library-auto/login-libray.py) 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /MrLevo520-验证码自动识别/furtherProject/提交方式.md: -------------------------------------------------------------------------------- 1 | ## 提交方式 2 | 3 | - 提交教程请看:[**Tutorial_使用Git对自己项目进行管理**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86/Tutorial_%E4%BD%BF%E7%94%A8Git%E5%AF%B9%E8%87%AA%E5%B7%B1%E9%A1%B9%E7%9B%AE%E8%BF%9B%E8%A1%8C%E7%AE%A1%E7%90%86.md) 4 | - 文件夹请以 githubname-projectname 进行提交 5 | - 例如 MrLevo520 - 验证码识别 6 | - 文件夹中内容包括(当然最好以markdown的格式进行类似tutorial的形式进行提) 7 | - 项目作用 8 | - 代码(注明语言版本) 9 | - 效果图(最好有,gif更好,注意先上传服务器才能挂上图) 10 | - 遇到的问题及解决方案 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome Mini Python Projects For Freshman 2 | 3 | Table of Contents 4 | ----------------- 5 | 6 | * [Awesome Mini Python Projects For Freshman](#awesome-mini-python-projects-for-freshman) 7 | * [What's this?](#whats-this) 8 | * [MrLevo520-MySQL加密存储](#mrlevo520-mysql加密存储) 9 | * [MrLevo520-有道翻译小软件](#mrlevo520-有道翻译小软件) 10 | * [MrLevo520-简易GUI计算器](#mrlevo520-简易gui计算器) 11 | * [MrLevo520-网易云音乐爬取分析](#mrlevo520-网易云音乐爬取分析) 12 | * [MrLevo520-自动化一键登录](#mrlevo520-自动化一键登录) 13 | * [MrLevo520-自动续借图书馆书籍](#mrlevo520-自动续借图书馆书籍) 14 | * [MrLevo520-豆瓣数据爬取与分析](#mrlevo520-豆瓣数据爬取与分析) 15 | * [MrLevo520-618电商价格分析](#mrlevo520-618电商价格分析) 16 | * [MrLevo520-验证码自动识别](#mrlevo520-验证码自动识别) 17 | * [MrLevo520-N-gram概括全文](#mrlevo520-N-gram概括全文) 18 | * [MrLevo520-使用Git对自己项目进行管理](#MrLevo520-使用Git对自己项目进行管理) 19 | * [About Python&IDE](#about-pythonide) 20 | * [Join Us](#join-us) 21 | * [About Tutorial Project](#about-tutorial-project) 22 | * [About Further Project](#about-further-project) 23 | * [twolun-刷暴京东验证码](#twolun-刷暴京东验证码) 24 | * [Wwivywwivy-51job简历自动刷新](#wwivywwivy-51job简历自动刷新) 25 | * [ZLFlyApple-豆瓣冷门电影分析](#zlflyapple-豆瓣冷门电影分析) 26 | * [qingwalv-浏览器下载文件](#qingwalv-浏览器下载文件) 27 | * [zxylina-网易云音乐分析](#zxylina-网易云音乐分析) 28 | * [love-apple-豆瓣冷门电影分析](#love-apple-豆瓣冷门电影分析2) 29 | * [cowboy231-豆瓣冷门电影分析3](#cowboy231-豆瓣冷门电影分析3) 30 | 31 | ## What's this? 32 | 33 | ​ **这是一个学习python上手的项目集合,会挑选出好玩有趣的python小项目进行练手,进行实际项目从0到1的开发,每个项目都会有其目的和意义,从中可以发掘更多有趣的项目,欢迎尝试改造!** 34 | 35 | ​ ***每个project中都会有一篇tutorial来告诉你如何进行这个项目,当然我也会将项目同步更新在自己的博客中,如有疑问请issue或者博客留言→_→[mrlevo的博客](http://blog.csdn.net/mrlevo520/article),目前项目包含如下,我会根据项目进行更新列表*** 36 | ### [**MrLevo520-MySQL加密存储**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-MySQL%E5%8A%A0%E5%AF%86%E5%AD%98%E5%82%A8) 37 | 38 | ![加密数据库](ImageStore/加密数据库.jpg) 39 | 40 | ### [**MrLevo520-有道翻译小软件**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E6%9C%89%E9%81%93%E7%BF%BB%E8%AF%91%E5%B0%8F%E8%BD%AF%E4%BB%B6) 41 | 42 | ![翻译软件](ImageStore/翻译软件.png) 43 | 44 | ### [**MrLevo520-简易GUI计算器**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E7%AE%80%E6%98%93GUI%E8%AE%A1%E7%AE%97%E5%99%A8) 45 | 46 | ![计算器](ImageStore/计算器.png) 47 | 48 | ### [**MrLevo520-N-gram概括全文**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-N-gram%E6%A6%82%E6%8B%AC%E5%85%A8%E6%96%87/Tutorial_N-gram%E6%A6%82%E6%8B%AC%E5%86%85%E5%AE%B9.md) 49 | 50 | ![n-gram](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-N-gram%E6%A6%82%E6%8B%AC%E5%85%A8%E6%96%87/save.png?raw=true) 51 | 52 | ### [**MrLevo520-网易云音乐爬取分析**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90) 53 | 54 | ![一键登录](ImageStore/网易云音乐.png) 55 | 56 | ### [**MrLevo520-自动化一键登录**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%80%E9%94%AE%E7%99%BB%E5%BD%95) 57 | 58 | ![一键登录](ImageStore/一键登录.gif) 59 | 60 | ### [**MrLevo520-自动续借图书馆书籍**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%87%AA%E5%8A%A8%E7%BB%AD%E5%80%9F%E5%9B%BE%E4%B9%A6%E9%A6%86%E4%B9%A6%E7%B1%8D) 61 | 62 | ![图书馆续借](ImageStore/图书馆续借.gif) 63 | 64 | ### [**MrLevo520-豆瓣数据爬取与分析**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90) 65 | 66 | ![豆瓣电影](ImageStore/豆瓣电影.jpeg) 67 | 68 | ### [**Mrlevo520-618电商价格分析**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-618%E7%94%B5%E5%95%86%E4%BB%B7%E6%A0%BC%E5%88%86%E6%9E%90) 69 | 70 | - 动态渲染请点击 [618价格对比](https://mrlevo520.github.io/Mini-Python-Project/MrLevo520-618%E7%94%B5%E5%95%86%E4%BB%B7%E6%A0%BC%E5%88%86%E6%9E%90/showData/618echarts_show.html) 71 | 72 | ![618价格](ImageStore/618价格.png) 73 | 74 | ### [**MrLevo520-验证码自动识别**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E9%AA%8C%E8%AF%81%E7%A0%81%E8%87%AA%E5%8A%A8%E8%AF%86%E5%88%AB) 75 | 76 | ![验证码识别](ImageStore/验证码识别.gif) 77 | 78 | - 关于纯小白,没有一点语言基础的童鞋,希望你好好看完[廖雪峰的教程](https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000),这是一个非常好的入门教程 79 | 80 | 81 | 82 | 83 | ## About Python&IDE 84 | 85 | - 项目tutorial中采用的都为python 2.7的版本,测试机器从win到mac乃至Ubuntu的ESC服务器,这些在首行环境中都会有注意项(如有同学有python3版本的,可以和我联系,我会进行填充) 86 | - IED建议安装Pycharm+Anaconda2,实际操作过程中如果会使用到numpy,pandas一些科学计数包,可以省去各类依赖包的烦恼 87 | - 测试请使用Jupyter,如果安装了anaconda,那么已默认安装jupyter,如果没有,那么请自行安装,这是一个web的ipython,你用就好了,走过很多弯路最后聚齐这三个宝贝Pycharm+anaconda+jupyter,请直接进行尝试 88 | 89 | 90 | 91 | 92 | ## Join Us 93 | 94 | ### About Tutorial Project 95 | 96 | - 这是一个开源项目,只要你有想法,并且付诸实践,欢迎进行tutorial的提交,我会以最短的时间内进行回复,希望能遇到各路大神和有趣的项目共同学习 97 | - 提交方式见:[CONTRIBUTING.md](https://github.com/MrLevo520/Mini-Python-Project/blob/master/CONTRIBUTING.md) 98 | 99 | ### About Further Project 100 | 101 | - 对于想对项目进行练习并且展示的童鞋,你可以在该项目的子类furtherProject下提交自己的项目文件夹,欢迎对项目进行延伸和加入自己的想法并提交,给更多的童鞋展示你不一样的想法~ 102 | - 提交方式见:[CONTRIBUTING.md](https://github.com/MrLevo520/Mini-Python-Project/blob/master/CONTRIBUTING.md) 103 | 104 | ***目前提交的furtherProject如下*** 105 | 106 | #### 1. [**twolun-刷暴京东验证码**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E9%AA%8C%E8%AF%81%E7%A0%81%E8%87%AA%E5%8A%A8%E8%AF%86%E5%88%AB/furtherProject/twolun-%E5%88%B7%E6%9A%B4%E4%BA%AC%E4%B8%9C%E9%AA%8C%E8%AF%81%E7%A0%81) 107 | 108 | #### 2. [**Wwivywwivy-51job简历自动刷新**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%80%E9%94%AE%E7%99%BB%E5%BD%95/furtherProject/wwivywwivy-51job) 109 | 110 | #### 3. [**ZLFlyApple-豆瓣冷门电影分析1**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/ZLFlyApple-douban) 111 | 112 | ![](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%86%B7%E9%97%A8%E4%BD%B3%E7%89%87top400%E5%B9%B4%E4%BB%A3%E5%88%86%E5%B8%83.png) 113 | 114 | #### 4. [**love-apple-豆瓣冷门电影分析2**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/love-apple-%E8%B1%86%E7%93%A3%E5%86%B7%E9%97%A8%E7%94%B5%E5%BD%B1%E5%88%86%E6%9E%90) 115 | 116 | ![](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/love-apple-%E8%B1%86%E7%93%A3%E5%86%B7%E9%97%A8%E7%94%B5%E5%BD%B1%E5%88%86%E6%9E%90/1.PNG) 117 | 118 | #### 5. [**cowboy231-豆瓣冷门电影分析3**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan) 119 | 120 | ![](https://raw.githubusercontent.com/cowboy231/Mini-Python-Project/master/MrLevo520-%E8%B1%86%E7%93%A3%E6%95%B0%E6%8D%AE%E7%88%AC%E5%8F%96%E4%B8%8E%E5%88%86%E6%9E%90/furtherProject/cowboy231-DouBan/img/195451.png) 121 | 122 | #### 6. [**qingwalv-浏览器下载文件**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-%E8%87%AA%E5%8A%A8%E7%BB%AD%E5%80%9F%E5%9B%BE%E4%B9%A6%E9%A6%86%E4%B9%A6%E7%B1%8D/furtherProject/qingwalv-AutodownloadCSVfile) 123 | 124 | #### 7. [**zxylina-网易云音乐分析**](https://github.com/MrLevo520/Mini-Python-Project/blob/master/MrLevo520-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/furtherProject/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90/zxylina-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E7%88%AC%E5%8F%96%E5%88%86%E6%9E%90.md) 125 | 126 | ![网易云音乐further](ImageStore/网易云音乐further.png) 127 | 128 | #### 8. [**ZLFlyApple-奥巴马演讲稿分析**](https://github.com/MrLevo520/Mini-Python-Project/tree/master/MrLevo520-N-gram%E6%A6%82%E6%8B%AC%E5%85%A8%E6%96%87/furtherProject/ZLFlyApple-ngram) 129 | ![奥巴马演讲稿分析](https://raw.githubusercontent.com/ZLFlyApple/DTTest/master/%E5%90%AC%E4%BC%97%E5%8F%8D%E6%98%A0%E8%AE%A1%E6%95%B0.png) 130 | --------------------------------------------------------------------------------