├── .gitattributes ├── README.md ├── __pycache__ └── mysqls.cpython-36.pyc ├── 大众点评爬虫 ├── CRAW_IP.py ├── README.md ├── main.py ├── mysqls.py ├── proxies.txt └── xuchuan.txt └── 文本分析挖掘 ├── .ipynb_checkpoints ├── Untitled-checkpoint.ipynb ├── Untitled1-checkpoint.ipynb ├── 探索性数据分析-checkpoint.ipynb └── 文本挖掘&情感分析-checkpoint.ipynb ├── data.csv ├── msyh.ttc ├── source ├── data_head.png ├── dianpu.png ├── len.png ├── stars.png ├── time.png └── wordcloud.png ├── stopwords.txt ├── 探索性数据分析.ipynb └── 文本挖掘&情感分析.ipynb /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 大众点评评论文本挖掘 2 | 3 | [TOC] 4 | 5 | ## 一、爬虫 6 | 7 | ### 整体思路 8 | 9 | 爬取大众点评十大热门糖水店的评论,爬取网页后从html页面中把需要的字段信息(顾客id、评论时间、评分、评论内容、口味、环境、服务、店铺ID)提取出来并存储到MYSQL数据库中。 10 | 11 | ### 网页爬取和解析 12 | 13 | 链接格式为"http://www.dianping.com/shop/" + shopID + "/review_all/" + pi,如:http://www.dianping.com/shop/518986/review_all/p1 ,一页评论有20条。我们使用for循环构造链接URL,使用requests库发起请求并把html页面爬取下来,通过BeautifulSoup和re库解析页面提取信息。 14 | 15 | 我们发现完整的评论都存储在'div','main-review'中,且部分页面口味、环境、服务并不是每一页都有,因此需要使用try...except...防止程序中断,BeautifulSoup部分代码如下: 16 | 17 | ``` python 18 | for item in soup('div','main-review'): 19 | cus_id = item.find('a','name').text.strip() 20 | comment_time = item.find('span','time').text.strip() 21 | comment_star = item.find('span',re.compile('sml-rank-stars')).get('class')[1] 22 | cus_comment = item.find('div',"review-words").text.strip() 23 | scores = str(item.find('span','score')) 24 | try: 25 | kouwei = re.findall(r'口味:([\u4e00-\u9fa5]*)',scores)[0] 26 | huanjing = re.findall(r'环境:([\u4e00-\u9fa5]*)',scores)[0] 27 | fuwu = re.findall(r'服务:([\u4e00-\u9fa5]*)',scores)[0] 28 | except: 29 | kouwei = huanjing = fuwu = '无' 30 | ``` 31 | 32 | ### 数据存储 33 | 34 | 我们使用MYSQL数据库,安装教程参考[菜鸟教程](http://www.runoob.com/mysql/mysql-install.html),python连接MYSQL数据推荐使用pymysql,同样是推荐菜鸟教程[菜鸟教程](http://www.runoob.com/python3/python3-mysql.html)。我们需要先建立一个数据库和表,然后连接并定义游标,然后写对应的sql语句,最后执行事务,存储部分的代码如下: 35 | 36 | ``` python 37 | #连接MYSQL数据库 38 | db = pymysql.connect("localhost","root","","TESTDB" ) 39 | cursor = db.cursor() 40 | #存储爬取到的数据 41 | def save_data(data_dict): 42 | sql = '''INSERT INTO DZDP(cus_id, comment_time, comment_star, cus_comment, kouwei, huanjing, fuwu, shopID) VALUES(%s,%s,%s,%s,%s,%s,%s,%s)''' 43 | value_tup = (data_dict['cus_id'] 44 | ,data_dict['comment_time'] 45 | ,data_dict['comment_star'] 46 | ,data_dict['cus_comment'] 47 | ,data_dict['kouwei'] 48 | ,data_dict['huanjing'] 49 | ,data_dict['fuwu'] 50 | ,data_dict['shopID'] 51 | ) 52 | try: 53 | cursor.execute(sql,value_tup) 54 | db.commit() 55 | except: 56 | print('数据库写入失败') 57 | return 58 | ``` 59 | 60 | ### 反爬虫对抗 61 | 62 | 1. **修改请求头中浏览器信息**:使用fake_useragent第三方库,修改request中的headers参数,用法如下: 63 | 64 | ``` python 65 | from fake_useragent import UserAgent 66 | ua = UserAgent() 67 | headers = {'User-Agent':ua.random} 68 | ``` 69 | 70 | 2. **设置跳转路径**:在访问评论时,一般的浏览行为是从某一页跳转到下一页这样的,而不是直接通过连接访问,为了更好的伪装成一个正常的访问,我们需要设置一下跳转的路径,修改headers中的Referer参数 71 | 72 | ``` python 73 | headers = { 74 | 'User-Agent':ua.random, 75 | 'Cookie':cookie, 76 | 'Referer': 'http://www.dianping.com/shop/518986/review_all' 77 | } 78 | ``` 79 | 80 | 3. **设置Cookies**:评论数据需要登录后才能获取,下面介绍一种非常简单方便的绕过登录的方法。 81 | 82 | - 在网页上进行登录 83 | - 使用Chrome浏览器的开发者工具,查询当前请求的cookie 84 | - 复制浏览器中的cookie,使用此cookie对我们的请求进行伪装 85 | 86 | 4. **使用IP代理池**:这里使用西刺代理的免费代理,构建一个爬虫爬取西刺代理的ip,然后进行验证,筛掉不可用的ip,构建出ip池供后续调用,代码来自网络。但是经过测试,大众点评对一个账号不同ip访问监控非常严格,使用IP代理池不更换账号的话,死的更快,封你账号,然而构建账号池比较麻烦,我们先暂缓。 87 | 88 | 5. **降低爬取频率**:一个简单又有效的方法就是降低爬取频率,毕竟高频率的爬取对服务器也是一个考验,如果对速度的要求不是很高的话,建议把频率放慢一点,你好我好大家好! 89 | 90 | ``` python 91 | import random 92 | import time 93 | time.sleep(6*random.random() + 4) 94 | ``` 95 | 96 | 6. **设置断点续传**:即使降低了爬取频率,有时还是会被美团的网络工程师抓到的,小哥哥饶命啊~。因此我们需要一个断点续传的小功能,避免每次都从头开始爬。思路是建一个文本文件,存储当前爬取的进度,每次运行程序时都出当前进度开始,详见代码~ 97 | 98 | ## 二、探索性分析与文本数据预处理 99 | 100 | ### 探索性分析 101 | 102 | 1. 查看数据大小以及基础信息 ,浏览数据 103 | 104 | ![data_head](文本分析挖掘/source/data_head.png) 105 | 106 | 2. 样本分布 107 | 108 | ![data_head](文本分析挖掘/source/stars.png) 109 | 110 | 3. 各店铺评分分布 111 | 112 | ![data_head](文本分析挖掘/source/dianpu.png) 113 | 114 | 4. 点评数的的时间分布 115 | 116 | ![data_head](文本分析挖掘/source/time.png) 117 | 118 | 5. 查看评论长度对结果影响 119 | 120 | ![data_head](文本分析挖掘/source/len.png) 121 | ### 数据预处理 122 | 123 | 1. **去除非文本数据**:可以看出,爬虫获取的数据非常多类似“\xa0”的非文本数据,而且都还有一些无意义的干扰数据,如结尾的“收起评论” 124 | 125 | ``` python 126 | #除去非文本数据和无意义文本 127 | data['cus_comment'] = data['cus_comment'].str.replace(r'[^\u4e00-\u9fa5]','').str.replace('收起评论','') 128 | ``` 129 | 130 | 2. **中文分词**:中文文本数据处理,怎么能离开中文分词呢,我们使用jieba库,简单又好用。这里我们把文本字符串处理为以空格区隔的分词字符串 131 | ``` python 132 | #中文分词 133 | import jieba 134 | data['cus_comment'] = data['cus_comment'].apply(lambda x:' '.join(jieba.cut(x))) 135 | ``` 136 | 137 | 3. **去除停用词**:文本中有很多无效的词,比如“着”,“和”,还有一些标点符号,这些我们不想在文本分析的时候引入,因此需要去掉,因为wordcloud和TF-IDF都支持停用词,因此就不额外处理了 138 | 139 | ### 词云展示 140 | 141 | ![wordcloud](文本分析挖掘/source/wordcloud.png) 142 | 143 | ## 三、文本的情感分析 144 | 145 | 先上结果: 146 | 147 | | 糖水店的评论文本 | 模型预测的情感评分 | 148 | | :------------------------------------------- | :----------------- | 149 | | '糖水味道不错,滑而不腻,赞一个,下次还会来' | 0.91 | 150 | | '味道一般,没啥特点' | 0.52 | 151 | | '排队老半天,环境很差,味道一般般' | 0.05 | 152 | 153 | 模型的效果还可以的样子,yeah~接下来我们好好讲讲怎么做的哈,我们通过爬虫爬取了大众点评广州8家最热门糖水店的3W条评论信息以及评分作为训练数据,前面的分析我们得知*样本很不均衡*。接下来我们的整体思路就是:文本特征提取(TF-IDF)—机器学习建模—模型评价。 154 | 155 | 我们先不处理样本不均衡问题,直接建模后查看结果,接下来我们再按照两种方法处理样本不均衡,对比结果。 156 | 157 | ### 文本特征提取(TF-IDF) 158 | 159 | 模型不能直接处理文本数据,因此需要先把文本数据转为向量,方法有词库表示法、TF-IDF、word2vec等,推荐一篇文章,总结得不错 https://zhuanlan.zhihu.com/p/44917421。 160 | 161 | ``` python 162 | #使用TF-IDF进行文本转向量处理 163 | from sklearn.feature_extraction.text import TfidfVectorizer 164 | tv = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2)) 165 | tv.fit(x_train) 166 | ``` 167 | 168 | ### 机器学习建模 169 | 170 | 这里我们使用文本分类的经典算法朴素贝叶斯算法,而且朴素贝叶斯算法的计算量较少。特征值是评论文本经过TF-IDF处理的向量,标签值评论的分类共两类,好评是1,差评是0。情感评分为分类器预测分类1的概率值。 171 | 172 | ``` python 173 | #计算分类效果的准确率 174 | from sklearn.naive_bayes import MultinomialNB 175 | from sklearn.metrics import roc_auc_score, f1_score 176 | classifier = MultinomialNB() 177 | classifier.fit(tv.transform(x_train), y_train) 178 | classifier.score(tv.transform(x_test), y_test) 179 | 180 | >>>0.9275308869629356 181 | ``` 182 | 183 | 可以看出,准确率非常不错的样子 184 | 185 | ``` python 186 | #从大众点评网找两条评论来测试一下 187 | test1 = '很好吃,环境好,所有员工的态度都很好,上菜快,服务也很好,味道好吃,都是用蒸馏水煮的,推荐,超好吃' #5星好评 188 | test2 = '糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。' #1星差评 189 | print('好评实例的模型预测情感得分为{}\n差评实例的模型预测情感得分为{}'.format(ceshi(classifier,test1),ceshi(classifier,test2))) 190 | 191 | >>>好评实例的模型预测情感得分为0.8638082706675478 192 | >>>差评实例的模型预测情感得分为0.7856544482460911 193 | ``` 194 | 195 | 点评网上的实际测试中,5星好评模型预测出来了,1星差评缺预测错误。为什么呢?我们查看一下**混淆矩阵** 196 | 197 | ``` 198 | [ 46, 385] 199 | [ 8, 4984] 200 | ``` 201 | 202 | 可以看出,**负类的预测非常不准**,433单准确预测为负类的只有15.7%,应该是由于**数据不平衡**导致的,模型的默认阈值为输出值的中位数。比如逻辑回归的输出范围为[0,1],当某个样本的输出大于0.5就会被划分为正例,反之为反例。在数据的类别不平衡时,采用默认的分类阈值可能会导致输出全部为正例,产生虚假的高准确度,导致分类失败。 203 | 204 | 处理样本不均衡问题的方法,首先可以选择调整阈值,使得模型对于较少的类别更为敏感,或者选择合适的评估标准,比如ROC或者F1,而不是准确度(accuracy)。另外一种方法就是通过采样(sampling)来调整数据的不平衡。其中欠采样抛弃了大部分正例数据,从而弱化了其影响,可能会造成偏差很大的模型,同时,数据总是宝贵的,抛弃数据是很奢侈的。另外一种是过采样,下面我们就使用过采样方法来调整。 205 | 206 | ### 样本数据不平衡 207 | 208 | 最简单的过采样方法,就是简单复制法。但单纯的重复了反例,会过分强调已有的反例。如果其中部分点标记错误或者是噪音,那么错误也容易被成倍的放大。因此最大的风险就是对反例过拟合。 209 | 210 | ``` python 211 | #把0类样本复制10次,构造训练集 212 | index_tmp = y_train==0 213 | y_tmp = y_train[index_tmp] 214 | x_tmp = x_train[index_tmp] 215 | x_train2 = pd.concat([x_train,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp]) 216 | y_train2 = pd.concat([y_train,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp]) 217 | 218 | #使用过采样样本(简单复制)进行模型训练,并查看准确率 219 | clf2 = MultinomialNB() 220 | clf2.fit(tv.transform(x_train2), y_train2) 221 | y_pred2 = clf2.predict_proba(tv.transform(x_test))[:,1] 222 | roc_auc_score(y_test,y_pred2) 223 | 224 | >>>0.9049699937533463 225 | ``` 226 | 227 | 查看此时的混淆矩阵 228 | 229 | ``` 230 | [ 331, 100] 231 | [ 637, 4355] 232 | ``` 233 | 234 | 可以看出,即使是简单粗暴的复制样本来处理样本不平衡问题,负样本的识别率大幅上升了,变为77%,满满的幸福感呀。还有SMOTE过采样算法,SMOTE是在局部区域通过K-近邻生成了新的反例。相较于简单的过采样,SMOTE降低了过拟合风险,但同时运算开销加大,详细请看具体代码~ 235 | 236 | ### 模型评估测试 237 | 238 | 我们把3W条数据都拿来训练,数据量变多了,模型效果应该会更好 239 | 240 | ``` python 241 | def fenxi(strings): 242 | strings_fenci = fenci(pd.Series([strings])) 243 | return float(clf.predict_proba(tv2.transform(strings_fenci))[:,1]) 244 | 245 | #到网上找一条差评来测试一下 246 | fenxi('糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。') 247 | 248 | >>>0.28900092243477077 249 | ``` 250 | 251 | 只用到了简单的机器学习,就做出了不错的情感分析效果,知识的力量真是强大呀,666~ 252 | 253 | ## 四、拓展应用及后续方向 254 | 255 | - 使用更复杂的机器学习模型如神经网络、支持向量机等 256 | - 模型的调参 257 | - 行业词库的构建 258 | - 增加数据量 259 | - 优化情感分析的算法 260 | - 增加标签提取等 261 | - 项目部署到服务器上,更好地分享和测试模型的效果 -------------------------------------------------------------------------------- /__pycache__/mysqls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/__pycache__/mysqls.cpython-36.pyc -------------------------------------------------------------------------------- /大众点评爬虫/CRAW_IP.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from bs4 import BeautifulSoup 4 | 5 | import lxml 6 | 7 | from multiprocessing import Process, Queue 8 | 9 | import random 10 | 11 | import json 12 | 13 | import time 14 | 15 | import requests 16 | 17 | 18 | class Proxies(object): 19 | """docstring for Proxies""" 20 | 21 | def __init__(self, page=3): 22 | 23 | self.proxies = [] 24 | 25 | self.verify_pro = [] 26 | 27 | self.page = page 28 | 29 | self.headers = { 30 | 31 | 'Accept': '*/*', 32 | 33 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36', 34 | 35 | 'Accept-Encoding': 'gzip, deflate, sdch', 36 | 37 | 'Accept-Language': 'zh-CN,zh;q=0.8' 38 | 39 | } 40 | 41 | self.get_proxies() 42 | 43 | self.get_proxies_nn() 44 | 45 | def get_proxies(self): 46 | 47 | page = random.randint(1, 10) 48 | 49 | page_stop = page + self.page 50 | 51 | while page < page_stop: 52 | 53 | url = 'http://www.xicidaili.com/nt/%d' % page 54 | 55 | html = requests.get(url, headers=self.headers).content 56 | 57 | soup = BeautifulSoup(html, 'lxml') 58 | 59 | ip_list = soup.find(id='ip_list') 60 | 61 | for odd in ip_list.find_all(class_='odd'): 62 | protocol = odd.find_all('td')[5].get_text().lower() + '://' 63 | 64 | self.proxies.append(protocol + ':'.join([x.get_text() for x in odd.find_all('td')[1:3]])) 65 | 66 | page += 1 67 | 68 | def get_proxies_nn(self): 69 | 70 | page = random.randint(1, 10) 71 | 72 | page_stop = page + self.page 73 | 74 | while page < page_stop: 75 | 76 | url = 'http://www.xicidaili.com/nn/%d' % page 77 | 78 | html = requests.get(url, headers=self.headers).content 79 | 80 | soup = BeautifulSoup(html, 'lxml') 81 | 82 | ip_list = soup.find(id='ip_list') 83 | 84 | for odd in ip_list.find_all(class_='odd'): 85 | protocol = odd.find_all('td')[5].get_text().lower() + '://' 86 | 87 | self.proxies.append(protocol + ':'.join([x.get_text() for x in odd.find_all('td')[1:3]])) 88 | 89 | page += 1 90 | 91 | def verify_proxies(self): 92 | 93 | # 没验证的代理 94 | 95 | old_queue = Queue() 96 | 97 | # 验证后的代理 98 | 99 | new_queue = Queue() 100 | 101 | print('verify proxy........') 102 | 103 | works = [] 104 | 105 | for _ in range(15): 106 | works.append(Process(target=self.verify_one_proxy, args=(old_queue, new_queue))) 107 | 108 | for work in works: 109 | work.start() 110 | 111 | for proxy in self.proxies: 112 | old_queue.put(proxy) 113 | 114 | for work in works: 115 | old_queue.put(0) 116 | 117 | for work in works: 118 | work.join() 119 | 120 | self.proxies = [] 121 | 122 | while 1: 123 | 124 | try: 125 | 126 | self.proxies.append(new_queue.get(timeout=1)) 127 | 128 | except: 129 | 130 | break 131 | 132 | print('verify_proxies done!') 133 | 134 | def verify_one_proxy(self, old_queue, new_queue): 135 | 136 | while 1: 137 | 138 | proxy = old_queue.get() 139 | 140 | if proxy == 0: break 141 | 142 | protocol = 'https' if 'https' in proxy else 'http' 143 | 144 | proxies = {protocol: proxy} 145 | 146 | try: 147 | 148 | if requests.get('http://www.baidu.com', proxies=proxies, timeout=2).status_code == 200: 149 | print('success %s' % proxy) 150 | 151 | new_queue.put(proxy) 152 | 153 | except: 154 | 155 | print('fail %s' % proxy) 156 | 157 | 158 | if __name__ == '__main__': 159 | 160 | a = Proxies() 161 | 162 | a.verify_proxies() 163 | 164 | print(a.proxies) 165 | 166 | proxie = a.proxies 167 | 168 | with open('proxies.txt', 'a') as f: 169 | 170 | for proxy in proxie: 171 | f.write(proxy + '\n') 172 | 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /大众点评爬虫/README.md: -------------------------------------------------------------------------------- 1 | ## 大众点评评论爬虫脚本使用指南 2 | 3 | ### 爬取前的准备 4 | 5 | - mysql数据库安装、打开服务 6 | - 修改mysqls.py程序中数据库的用户名密码等,并创建对应的database和table,可以使用mysqls.creat_table()函数 7 | - 登录大众点评官网,通过谷歌开发者工具等获取到当前的cookie,修改main.py中的cookie变量 8 | - 查看爬取的店铺的店铺ID以及评论的页数,修改main.py 中对应的位置 9 | - 如果有xuchuan.txt(保存当前点评爬取进度),请在爬取前删除(每换一个店铺要删除一次) 10 | 11 | ### 爬取过程中 12 | 13 | - 由于大概每爬取100页左右,需要进行一次验证,当发现获取评论为0条或者异常时,请用浏览器打开点评页面,滑动滑块解锁,然后重启程序,有断点续传,不虚~ 14 | - 当更换店铺时,需要把店铺ID进行替换,还有评论的页数也要替换,还要删除xuchuan.txt 15 | 16 | ### 爬取结束后 17 | 18 | 数据存储于MYSQL数据库中,可以使用各种方法读取,啦啦啦啦~ -------------------------------------------------------------------------------- /大众点评爬虫/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jul 9 16:42:52 2018 4 | 5 | @author: bin 6 | """ 7 | 8 | #目标爬取店铺的评论 9 | 10 | import requests 11 | from bs4 import BeautifulSoup 12 | import time, random 13 | import mysqls 14 | import re 15 | from fake_useragent import UserAgent 16 | import os 17 | 18 | ua = UserAgent() 19 | 20 | #设置cookies 21 | cookie = "_lxsdk_cuid=162760423dfc8-0801f141cb0731-3b60490d-e1000-162760423dfc8; _lxsdk=162760423dfc8-0801f141cb0731-3b60490d-e1000-162760423dfc8; _hc.v=af7219c3-2b99-8bb8-f9b2-7b1d9be7f29e.1522398406; s_ViewType=10; ua=%E4%BB%A4%E7%8B%90%E5%86%B2; ctu=029e953356caf94d20233d299a70d285a03cb64585c371690b17d3e59c4c075c; cye=guangzhou; Hm_lvt_e6f449471d3527d58c46e24efb4c343e=1531964746; cy=4; dper=8c6ae023e893759ea57ce154028f1800be56b69450806b893b9cf5c6b6c3e3ba3c986c9a603bcbf9a7fb18dcd2038cf704b3e3baba3532bc7dffec965fe5e6c3b2479ca21c6577a1f5636088acbba8936df6ac994e02a923a907907a938559f9; ll=7fd06e815b796be3df069dec7836c3df; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; _lxsdk_s=1661889a264-50e-66f-22a%7C%7C276" 22 | 23 | #修改请求头 24 | headers = { 25 | 'User-Agent':ua.random, 26 | 'Cookie':cookie, 27 | 'Connection':'keep-alive', 28 | 'Host':'www.dianping.com', 29 | 'Referer': 'http://www.dianping.com/shop/521698/review_all/p6' 30 | } 31 | 32 | #从ip代理池中随机获取ip 33 | #ips = open('proxies.txt','r').read().split('\n') 34 | # 35 | #def get_random_ip(): 36 | # ip = random.choice(ips) 37 | # pxs = {ip.split(':')[0]:ip} 38 | # return pxs 39 | 40 | #获取html页面 41 | def getHTMLText(url,code="utf-8"): 42 | try: 43 | time.sleep(random.random()*6 + 2) 44 | r=requests.get(url, timeout = 5, headers=headers, 45 | # proxies=get_random_ip() 46 | ) 47 | r.raise_for_status() 48 | r.encoding = code 49 | return r.text 50 | except: 51 | print("产生异常") 52 | return "产生异常" 53 | 54 | #因为评论中带有emoji表情,是4个字符长度的,mysql数据库不支持4个字符长度,因此要进行过滤 55 | def remove_emoji(text): 56 | try: 57 | highpoints = re.compile(u'[\U00010000-\U0010ffff]') 58 | except re.error: 59 | highpoints = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]') 60 | return highpoints.sub(u'',text) 61 | 62 | #从html中提起所需字段信息 63 | def parsePage(html,shpoID): 64 | infoList = [] #用于存储提取后的信息,列表的每一项都是一个字典 65 | soup = BeautifulSoup(html, "html.parser") 66 | 67 | for item in soup('div','main-review'): 68 | cus_id = item.find('a','name').text.strip() 69 | comment_time = item.find('span','time').text.strip() 70 | try: 71 | comment_star = item.find('span',re.compile('sml-rank-stars')).get('class')[1] 72 | except: 73 | comment_star = 'NAN' 74 | cus_comment = item.find('div',"review-words").text.strip() 75 | scores = str(item.find('span','score')) 76 | try: 77 | kouwei = re.findall(r'口味:([\u4e00-\u9fa5]*)',scores)[0] 78 | huanjing = re.findall(r'环境:([\u4e00-\u9fa5]*)',scores)[0] 79 | fuwu = re.findall(r'服务:([\u4e00-\u9fa5]*)',scores)[0] 80 | except: 81 | kouwei = huanjing = fuwu = '无' 82 | 83 | infoList.append({'cus_id':cus_id, 84 | 'comment_time':comment_time, 85 | 'comment_star':comment_star, 86 | 'cus_comment':remove_emoji(cus_comment), 87 | 'kouwei':kouwei, 88 | 'huanjing':huanjing, 89 | 'fuwu':fuwu, 90 | 'shopID':shpoID}) 91 | return infoList 92 | 93 | #构造每一页的url,并且对爬取的信息进行存储 94 | def getCommentinfo(shop_url, shpoID, page_begin, page_end): 95 | for i in range(page_begin, page_end): 96 | try: 97 | url = shop_url + 'p' + str(i) 98 | html = getHTMLText(url) 99 | infoList = parsePage(html,shpoID) 100 | print('成功爬取第{}页数据,有评论{}条'.format(i,len(infoList))) 101 | for info in infoList: 102 | mysqls.save_data(info) 103 | #断点续传中的断点 104 | if (html != "产生异常") and (len(infoList) != 0): 105 | with open('xuchuan.txt','a') as file: 106 | duandian = str(i)+'\n' 107 | file.write(duandian) 108 | else: 109 | print('休息60s...') 110 | time.sleep(60) 111 | except: 112 | print('跳过本次') 113 | continue 114 | return 115 | 116 | def xuchuan(): 117 | if os.path.exists('xuchuan.txt'): 118 | file = open('xuchuan.txt','r') 119 | nowpage = int(file.readlines()[-1]) 120 | file.close() 121 | else: 122 | nowpage = 0 123 | return nowpage 124 | 125 | #根据店铺id,店铺页码进行爬取 126 | def craw_comment(shopID='521698',page = 53): 127 | shop_url = "http://www.dianping.com/shop/" + shopID + "/review_all/" 128 | #读取断点续传中的续传断点 129 | nowpage = xuchuan() 130 | getCommentinfo(shop_url, shopID, page_begin=nowpage+1, page_end=page+1) 131 | mysqls.close_sql() 132 | return 133 | 134 | if __name__ == "__main__": 135 | craw_comment() 136 | -------------------------------------------------------------------------------- /大众点评爬虫/mysqls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jul 24 15:45:05 2018 4 | 5 | @author: bin 6 | """ 7 | 8 | import pymysql 9 | 10 | #连接MYSQL数据库 11 | db = pymysql.connect("localhost","root","","TESTDB" ) 12 | cursor = db.cursor() 13 | 14 | #在数据库建表 15 | def creat_table(): 16 | cursor.execute("DROP TABLE IF EXISTS DZDP") 17 | sql = '''CREATE TABLE DZDP( 18 | cus_id varchar(100), 19 | comment_time varchar(55), 20 | comment_star varchar(55), 21 | cus_comment text(5000), 22 | kouwei varchar(55), 23 | huanjing varchar(55), 24 | fuwu varchar(55), 25 | shopID varchar(55) 26 | );''' 27 | cursor.execute(sql) 28 | return 29 | 30 | #存储爬取到的数据 31 | def save_data(data_dict): 32 | sql = '''INSERT INTO DZDP(cus_id,comment_time,comment_star,cus_comment,kouwei,huanjing,fuwu,shopID) VALUES(%s,%s,%s,%s,%s,%s,%s,%s)''' 33 | value_tup = (data_dict['cus_id'] 34 | ,data_dict['comment_time'] 35 | ,data_dict['comment_star'] 36 | ,data_dict['cus_comment'] 37 | ,data_dict['kouwei'] 38 | ,data_dict['huanjing'] 39 | ,data_dict['fuwu'] 40 | ,data_dict['shopID'] 41 | ) 42 | try: 43 | cursor.execute(sql,value_tup) 44 | db.commit() 45 | except: 46 | print('数据库写入失败') 47 | return 48 | 49 | #关闭数据库 50 | def close_sql(): 51 | db.close() 52 | -------------------------------------------------------------------------------- /大众点评爬虫/proxies.txt: -------------------------------------------------------------------------------- 1 | https://59.37.18.243:3128 2 | https://183.129.207.74:14823 3 | https://49.73.6.90:3128 4 | https://115.239.255.190:3128 5 | https://203.86.26.9:3128 6 | https://120.92.74.189:3128 7 | http://183.62.196.10:3128 8 | https://183.129.244.17:10010 9 | https://171.221.239.11:808 10 | https://14.29.32.106:53281 11 | https://218.60.8.83:3129 12 | https://183.129.207.80:21776 13 | https://203.130.46.108:9090 14 | https://183.21.81.58:40539 15 | https://182.18.13.149:53281 16 | https://114.113.126.83:80 17 | https://118.212.95.34:53281 18 | https://114.113.126.82:80 19 | https://183.129.207.78:18118 20 | https://211.101.136.86:8080 21 | https://114.249.112.16:9000 22 | https://163.125.68.149:8888 23 | https://111.202.37.195:8080 24 | https://61.145.203.234:38695 25 | https://119.254.94.92:48494 26 | https://27.46.20.55:888 27 | https://175.6.2.174:8088 28 | https://59.72.126.3:8123 29 | https://59.37.26.226:8080 30 | https://120.27.14.125:80 31 | https://61.140.108.57:54689 32 | https://58.240.220.86:53281 33 | https://183.30.201.8:9797 34 | https://111.170.156.182:53281 35 | https://218.15.25.157:8088 36 | https://180.173.152.33:9000 37 | https://117.35.51.77:53281 38 | https://119.90.126.106:7777 39 | https://121.228.125.27:3128 40 | https://218.89.222.110:9999 41 | https://61.155.112.228:61591 42 | https://171.37.30.82:9797 43 | https://125.123.122.59:9000 44 | https://125.123.143.171:9000 45 | https://60.191.57.79:3128 46 | https://163.125.19.43:9999 47 | https://112.65.19.122:8080 48 | https://163.125.17.241:8888 49 | https://163.125.17.238:8888 50 | https://180.213.181.96:8118 51 | https://114.86.227.164:33657 52 | https://118.187.50.154:8080 53 | https://118.190.217.182:80 54 | https://118.190.217.61:80 55 | http://183.129.244.13:10800 56 | https://125.123.127.24:9000 57 | https://124.237.83.14:53281 58 | https://163.125.74.243:9797 59 | https://61.175.172.216:8123 60 | https://175.152.223.235:8123 61 | https://123.165.115.55:9797 62 | https://223.245.127.165:44765 63 | https://59.78.1.5:1080 64 | https://118.25.177.187:1080 65 | https://59.39.196.122:55637 66 | https://119.4.172.217:8118 67 | https://116.30.123.148:9000 68 | https://112.74.207.50:3128 69 | https://14.149.68.120:1080 70 | https://58.251.233.122:9797 71 | https://182.88.187.149:9797 72 | https://182.150.63.89:46073 73 | https://163.125.70.70:9999 74 | https://58.251.234.137:9797 75 | https://101.132.122.230:3128 76 | https://119.129.98.65:45522 77 | https://112.81.143.172:8118 78 | https://220.184.129.224:3128 79 | https://112.250.109.173:53281 80 | https://116.196.92.155:1080 81 | https://14.20.235.117:808 82 | https://182.88.187.83:9797 83 | https://110.52.8.171:53281 84 | https://159.226.170.42:3128 85 | https://121.9.199.70:32431 86 | https://113.118.201.133:9797 87 | https://58.250.23.210:1080 88 | https://119.250.26.39:9000 89 | https://171.36.179.27:9797 90 | https://175.25.185.57:3128 91 | https://118.190.155.23:80 92 | https://114.119.116.93:61066 93 | https://171.36.210.248:9797 94 | https://112.193.130.123:8118 95 | https://123.183.11.166:53386 96 | https://118.186.2.210:8080 97 | https://112.64.38.161:51099 98 | https://222.186.45.146:63756 99 | https://183.14.76.165:9797 100 | https://163.125.19.88:9999 101 | https://218.6.16.233:8118 102 | https://180.168.210.132:80 103 | https://61.164.39.69:53281 104 | https://61.130.9.249:3128 105 | https://122.143.117.8:8080 106 | https://180.162.34.149:9797 107 | https://115.231.50.10:53281 108 | https://112.95.205.63:8888 109 | https://112.95.205.71:8888 110 | https://115.151.4.6:53128 111 | https://110.73.40.17:8123 112 | https://121.207.0.115:808 113 | https://118.180.85.201:8123 114 | https://61.157.206.182:60460 115 | https://124.200.104.234:47076 116 | https://61.157.206.170:42379 117 | https://221.234.192.10:8010 118 | https://59.32.37.7:3128 119 | https://1.183.163.137:53077 120 | https://59.49.22.231:30151 121 | https://27.22.104.28:39560 122 | https://61.160.233.214:39522 123 | https://59.32.37.246:8010 124 | https://115.46.79.110:8123 125 | https://110.73.10.53:8123 126 | https://110.73.43.173:8123 127 | https://183.63.17.253:54174 128 | https://121.9.199.51:59134 129 | https://123.163.20.37:35249 130 | https://61.158.187.118:56524 131 | https://61.157.206.187:37667 132 | https://203.93.125.238:51108 133 | https://223.203.0.14:8080 134 | https://221.224.62.243:51941 135 | https://114.225.169.161:53128 136 | https://124.77.92.239:31307 137 | https://27.153.128.207:8010 138 | https://110.188.0.64:35137 139 | https://115.238.105.108:808 140 | https://61.133.245.70:35652 141 | https://60.211.192.54:40700 142 | https://171.37.155.232:8123 143 | https://221.232.193.223:8010 144 | https://27.190.26.57:8118 145 | https://221.224.212.11:23500 146 | https://180.118.240.51:61234 147 | https://113.106.97.148:38257 148 | https://119.97.23.87:8123 149 | https://1.183.163.101:52524 150 | https://61.157.206.172:59656 151 | https://121.205.254.201:8010 152 | https://61.157.206.178:34692 153 | https://115.46.74.160:8123 154 | https://120.5.162.224:32290 155 | https://61.154.49.38:59675 156 | https://61.160.233.215:48478 157 | https://119.123.77.41:31425 158 | https://114.225.170.217:53128 159 | https://113.17.36.96:47399 160 | https://114.112.70.150:57871 161 | https://123.207.30.131:80 162 | https://119.254.94.97:41697 163 | https://115.46.73.129:8123 164 | https://115.221.112.122:25903 165 | https://115.211.231.66:8010 166 | https://221.232.192.206:8010 167 | https://182.88.166.78:8123 168 | https://115.46.67.43:8123 169 | https://121.205.254.192:808 170 | https://175.148.73.231:1133 171 | https://183.129.153.122:36839 172 | https://139.196.111.17:42589 173 | https://60.12.214.184:33507 174 | https://117.85.86.73:53128 175 | https://115.46.77.225:8123 176 | https://121.31.177.217:8123 177 | https://110.73.42.191:8123 178 | https://222.85.22.167:8010 179 | https://119.48.97.137:80 180 | https://218.79.113.92:30366 181 | https://101.236.55.145:8866 182 | https://116.235.75.177:61922 183 | https://220.248.125.82:8118 184 | https://121.60.76.28:8010 185 | https://116.17.236.52:8010 186 | https://115.223.114.224:8010 187 | https://122.246.51.176:8010 188 | https://59.45.27.245:50858 189 | https://171.37.153.33:8123 190 | https://121.225.26.218:3128 191 | https://180.118.243.93:61234 192 | https://115.46.78.208:8123 193 | https://175.148.76.72:1133 194 | https://223.244.252.58:45744 195 | https://115.223.117.127:8010 196 | https://59.46.112.34:43858 197 | https://117.114.144.195:35070 198 | https://180.118.243.52:61234 199 | https://180.110.7.46:3128 200 | https://106.42.208.201:8010 201 | https://42.236.151.226:37848 202 | https://221.2.207.205:51030 203 | https://114.80.216.171:54408 204 | https://119.254.94.95:43150 205 | https://121.31.153.170:8123 206 | https://113.121.242.173:808 207 | https://122.138.16.158:80 208 | https://182.88.129.168:8123 209 | https://113.200.27.10:53281 210 | -------------------------------------------------------------------------------- /大众点评爬虫/xuchuan.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 6 7 | 7 8 | 8 9 | 9 10 | 10 11 | 11 12 | 12 13 | 13 14 | 14 15 | 15 16 | 16 17 | 17 18 | 18 19 | 19 20 | 20 21 | 21 22 | 22 23 | 23 24 | 24 25 | 25 26 | 26 27 | 27 28 | 28 29 | 29 30 | 30 31 | 31 32 | 32 33 | 33 34 | 34 35 | 35 36 | 36 37 | 37 38 | 38 39 | 39 40 | 40 41 | 41 42 | 42 43 | 43 44 | 44 45 | 45 46 | 46 47 | 47 48 | 48 49 | 49 50 | 50 51 | 51 52 | 52 53 | 53 54 | -------------------------------------------------------------------------------- /文本分析挖掘/.ipynb_checkpoints/Untitled-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 51, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import pandas as pd\n", 12 | "from matplotlib import pyplot as plt\n", 13 | "import jieba" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 52, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "text/html": [ 24 | "
\n", 25 | "\n", 38 | "\n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 43 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | "
cus_idcomment_timecomment_starcus_commentkouweihuanjingfuwushopIDstarsyearmonthweekdayhourcomment_len
0迷糊泰迪2018-09-20 06:48:00sml-str40南信 算是 广州 著名 甜品店吧 ,好几个 时间段 路过 ,都是 座无虚席。  看着 餐单 ...非常好518986.04.02018.09.03.06.0184.0
1稱霸幼稚園2018-09-22 21:49:00sml-str40中午吃完了所谓的早茶 回去放下行李 休息了会  就来吃下午茶了[服务]两层楼 楼下只能收现金...很好很好很好518986.04.02018.09.05.021.0266.0
2爱吃的美美侠2018-09-22 22:16:00sml-str40【VIP冲刺王者战队】【吃遍蓉城战队】【VIP有特权】五月份和好朋友毕业旅行来了广州。我们都...很好很好很好518986.04.02018.09.05.022.0341.0
3姜姜会吃胖2018-09-19 06:36:00sml-str40都说来广州吃糖水就要来南信招牌姜撞奶,红豆双皮奶牛三星,云吞面一楼现金,二楼微信支付宝位置不...非常好很好很好518986.04.02018.09.02.06.0197.0
4forevercage2018-08-24 17:58:00sml-str50一直很期待也最爱吃甜品,广州的甜品很丰富很多样,来之前就一直想着一定要过来吃到腻,今天总算实...非常好很好很好518986.05.02018.08.04.017.0261.0
\n", 146 | "
" 147 | ], 148 | "text/plain": [ 149 | " cus_id comment_time comment_star \\\n", 150 | "0 迷糊泰迪 2018-09-20 06:48:00 sml-str40 \n", 151 | "1 稱霸幼稚園 2018-09-22 21:49:00 sml-str40 \n", 152 | "2 爱吃的美美侠 2018-09-22 22:16:00 sml-str40 \n", 153 | "3 姜姜会吃胖 2018-09-19 06:36:00 sml-str40 \n", 154 | "4 forevercage 2018-08-24 17:58:00 sml-str50 \n", 155 | "\n", 156 | " cus_comment kouwei huanjing fuwu \\\n", 157 | "0 南信 算是 广州 著名 甜品店吧 ,好几个 时间段 路过 ,都是 座无虚席。  看着 餐单 ... 非常好 好 好 \n", 158 | "1 中午吃完了所谓的早茶 回去放下行李 休息了会  就来吃下午茶了[服务]两层楼 楼下只能收现金... 很好 很好 很好 \n", 159 | "2 【VIP冲刺王者战队】【吃遍蓉城战队】【VIP有特权】五月份和好朋友毕业旅行来了广州。我们都... 很好 很好 很好 \n", 160 | "3 都说来广州吃糖水就要来南信招牌姜撞奶,红豆双皮奶牛三星,云吞面一楼现金,二楼微信支付宝位置不... 非常好 很好 很好 \n", 161 | "4 一直很期待也最爱吃甜品,广州的甜品很丰富很多样,来之前就一直想着一定要过来吃到腻,今天总算实... 非常好 很好 很好 \n", 162 | "\n", 163 | " shopID stars year month weekday hour comment_len \n", 164 | "0 518986.0 4.0 2018.0 9.0 3.0 6.0 184.0 \n", 165 | "1 518986.0 4.0 2018.0 9.0 5.0 21.0 266.0 \n", 166 | "2 518986.0 4.0 2018.0 9.0 5.0 22.0 341.0 \n", 167 | "3 518986.0 4.0 2018.0 9.0 2.0 6.0 197.0 \n", 168 | "4 518986.0 5.0 2018.0 8.0 4.0 17.0 261.0 " 169 | ] 170 | }, 171 | "execution_count": 52, 172 | "metadata": {}, 173 | "output_type": "execute_result" 174 | } 175 | ], 176 | "source": [ 177 | "data = pd.read_csv('data.csv')\n", 178 | "data.head()" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 53, 184 | "metadata": { 185 | "collapsed": true 186 | }, 187 | "outputs": [], 188 | "source": [ 189 | "#构建特征值\n", 190 | "def zhuanhuan(score):\n", 191 | " if score > 3:\n", 192 | " return 1\n", 193 | " elif score < 3:\n", 194 | " return 0\n", 195 | " else:\n", 196 | " return None" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 70, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "#特征值转换\n", 206 | "data['target'] = data['stars'].map(lambda x:zhuanhuan(x))\n", 207 | "data_model = data.dropna()" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 76, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "#切分测试集、训练集\n", 217 | "from sklearn.model_selection import train_test_split\n", 218 | "x_train, x_test, y_train, y_test = train_test_split(data_model['cus_comment'], data_model['target'], random_state=1, test_size=0.25)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 77, 224 | "metadata": { 225 | "collapsed": true 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "#引入停用词\n", 230 | "infile = open(\"stopwords.txt\",encoding='utf-8')\n", 231 | "stopwords_lst = infile.readlines()\n", 232 | "stopwords = [x.strip() for x in stopwords_lst]" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 78, 238 | "metadata": {}, 239 | "outputs": [ 240 | { 241 | "data": { 242 | "text/plain": [ 243 | "16464 简单 的 店面 , 甜品 种类 超多 , 选择 困难 , 点 了 红豆 薏米 芋头 , 还 ...\n", 244 | "28132 打算 去 长隆 玩水 在 番禺 附近 搜到 的 店 , 离住 的 地方 很近 , 大热天 走...\n", 245 | "14213 甜品 第一站 当然 是 选 在 百花 甜品 了 , 光听 这个 名字 就 可以 想象 种类 ...\n", 246 | "24829 在 大众 点评 看到 推荐 的 同福路 美食 , 刚刚 和 朋友 闲逛 来到 同福路 , 决...\n", 247 | "31055 几乎 每次 去 广州 都 会 去 这间 店 吃 甜品 , 非常 不错 的 一间 店 , 环境...\n", 248 | "Name: cus_comment, dtype: object" 249 | ] 250 | }, 251 | "execution_count": 78, 252 | "metadata": {}, 253 | "output_type": "execute_result" 254 | } 255 | ], 256 | "source": [ 257 | "#中文分词\n", 258 | "def fenci(train_data):\n", 259 | " words_df = train_data.apply(lambda x:' '.join(jieba.cut(x)))\n", 260 | " return words_df\n", 261 | " \n", 262 | "x_train_fenci = fenci(x_train)\n", 263 | "x_train_fenci[:5]" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": 79, 269 | "metadata": {}, 270 | "outputs": [ 271 | { 272 | "data": { 273 | "text/plain": [ 274 | "TfidfVectorizer(analyzer='word', binary=False, decode_error='strict',\n", 275 | " dtype=, encoding='utf-8', input='content',\n", 276 | " lowercase=True, max_df=1.0, max_features=3000, min_df=1,\n", 277 | " ngram_range=(1, 2), norm='l2', preprocessor=None, smooth_idf=True,\n", 278 | " stop_words=['!', '\"', '#', '$', '%', '&', \"'\", '(', ')', '*', '+', ',', '-', '--', '.', '..', '...', '......', '...................', './', '.一', '记者', '数', '年', '月', '日', '时', '分', '秒', '/', '//', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '://', '::', ';', '<', '=', '>', '>>', '?', '@'...3', '94', '95', '96', '97', '98', '99', '100', '01', '02', '03', '04', '05', '06', '07', '08', '09'],\n", 279 | " strip_accents=None, sublinear_tf=False,\n", 280 | " token_pattern='(?u)\\\\b\\\\w\\\\w+\\\\b', tokenizer=None, use_idf=True,\n", 281 | " vocabulary=None)" 282 | ] 283 | }, 284 | "execution_count": 79, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "from sklearn.feature_extraction.text import TfidfVectorizer\n", 291 | "tv = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 292 | "tv.fit(x_train_fenci)" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 80, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "data": { 302 | "text/plain": [ 303 | "0.93196902654867253" 304 | ] 305 | }, 306 | "execution_count": 80, 307 | "metadata": {}, 308 | "output_type": "execute_result" 309 | } 310 | ], 311 | "source": [ 312 | "from sklearn.naive_bayes import MultinomialNB\n", 313 | "classifier = MultinomialNB()\n", 314 | "classifier.fit(tv.transform(fenci(x_train)), y_train)\n", 315 | "classifier.score(tv.transform(fenci(x_test)), y_test)" 316 | ] 317 | }, 318 | { 319 | "cell_type": "code", 320 | "execution_count": 81, 321 | "metadata": { 322 | "scrolled": true 323 | }, 324 | "outputs": [ 325 | { 326 | "data": { 327 | "text/plain": [ 328 | "0.89689292921253627" 329 | ] 330 | }, 331 | "execution_count": 81, 332 | "metadata": {}, 333 | "output_type": "execute_result" 334 | } 335 | ], 336 | "source": [ 337 | "from sklearn.metrics import roc_auc_score\n", 338 | "y_pred = classifier.predict_proba(tv.transform(fenci(x_test)))[:,1]\n", 339 | "roc_auc_score(y_test,y_pred)" 340 | ] 341 | }, 342 | { 343 | "cell_type": "markdown", 344 | "metadata": {}, 345 | "source": [ 346 | "查看混淆矩阵" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 92, 352 | "metadata": { 353 | "collapsed": true 354 | }, 355 | "outputs": [], 356 | "source": [ 357 | "y_predict = classifier.predict(tv.transform(fenci(x_test)))" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 94, 363 | "metadata": { 364 | "scrolled": true 365 | }, 366 | "outputs": [ 367 | { 368 | "data": { 369 | "text/plain": [ 370 | "array([[ 68, 365],\n", 371 | " [ 4, 4987]])" 372 | ] 373 | }, 374 | "execution_count": 94, 375 | "metadata": {}, 376 | "output_type": "execute_result" 377 | } 378 | ], 379 | "source": [ 380 | "from sklearn.metrics import confusion_matrix\n", 381 | "cm = confusion_matrix(y_test, y_predict)\n", 382 | "cm" 383 | ] 384 | }, 385 | { 386 | "cell_type": "markdown", 387 | "metadata": {}, 388 | "source": [ 389 | "可以看出,负类的预测非常不准,433单准确预测为负类的只有15.7%,应该是由于数据不平衡导致的,接下来我们就解决一下这个问题" 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": 118, 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "data2 = data.loc[(data['stars'] == 1)|(data['stars'] == 2)].copy()\n", 399 | "data3 = pd.concat([data_model,data2,data2,data2,data2,data2,data2,data2,data2,data2,data2],axis=0)\n", 400 | "x_train, x_test, y_train, y_test = train_test_split(data3['cus_comment'], data3['target'], random_state=1, test_size=0.25)" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": 119, 406 | "metadata": {}, 407 | "outputs": [ 408 | { 409 | "ename": "ValueError", 410 | "evalue": "np.nan is an invalid document, expected byte or unicode string.", 411 | "output_type": "error", 412 | "traceback": [ 413 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 414 | "\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", 415 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mtv2\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mTfidfVectorizer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mstop_words\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstopwords\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmax_features\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m3000\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mngram_range\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mtv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata3\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'cus_comment'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 3\u001b[0m \u001b[0mclf2\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mMultinomialNB\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mclf2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtv2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfenci\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata3\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'cus_comment'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdata3\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'target'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 416 | "\u001b[1;32mD:\\SOFTWARE\\Anaconda\\lib\\site-packages\\sklearn\\feature_extraction\\text.py\u001b[0m in \u001b[0;36mfit\u001b[1;34m(self, raw_documents, y)\u001b[0m\n\u001b[0;32m 1330\u001b[0m \u001b[0mself\u001b[0m \u001b[1;33m:\u001b[0m \u001b[0mTfidfVectorizer\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1331\u001b[0m \"\"\"\n\u001b[1;32m-> 1332\u001b[1;33m \u001b[0mX\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mTfidfVectorizer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfit_transform\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mraw_documents\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1333\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_tfidf\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mX\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1334\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 417 | "\u001b[1;32mD:\\SOFTWARE\\Anaconda\\lib\\site-packages\\sklearn\\feature_extraction\\text.py\u001b[0m in \u001b[0;36mfit_transform\u001b[1;34m(self, raw_documents, y)\u001b[0m\n\u001b[0;32m 837\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 838\u001b[0m vocabulary, X = self._count_vocab(raw_documents,\n\u001b[1;32m--> 839\u001b[1;33m self.fixed_vocabulary_)\n\u001b[0m\u001b[0;32m 840\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 841\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbinary\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 418 | "\u001b[1;32mD:\\SOFTWARE\\Anaconda\\lib\\site-packages\\sklearn\\feature_extraction\\text.py\u001b[0m in \u001b[0;36m_count_vocab\u001b[1;34m(self, raw_documents, fixed_vocab)\u001b[0m\n\u001b[0;32m 760\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mdoc\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mraw_documents\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 761\u001b[0m \u001b[0mfeature_counter\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 762\u001b[1;33m \u001b[1;32mfor\u001b[0m \u001b[0mfeature\u001b[0m \u001b[1;32min\u001b[0m \u001b[0manalyze\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdoc\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 763\u001b[0m \u001b[1;32mtry\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 764\u001b[0m \u001b[0mfeature_idx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvocabulary\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mfeature\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 419 | "\u001b[1;32mD:\\SOFTWARE\\Anaconda\\lib\\site-packages\\sklearn\\feature_extraction\\text.py\u001b[0m in \u001b[0;36m\u001b[1;34m(doc)\u001b[0m\n\u001b[0;32m 239\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 240\u001b[0m return lambda doc: self._word_ngrams(\n\u001b[1;32m--> 241\u001b[1;33m tokenize(preprocess(self.decode(doc))), stop_words)\n\u001b[0m\u001b[0;32m 242\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 243\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", 420 | "\u001b[1;32mD:\\SOFTWARE\\Anaconda\\lib\\site-packages\\sklearn\\feature_extraction\\text.py\u001b[0m in \u001b[0;36mdecode\u001b[1;34m(self, doc)\u001b[0m\n\u001b[0;32m 119\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 120\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mdoc\u001b[0m \u001b[1;32mis\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 121\u001b[1;33m raise ValueError(\"np.nan is an invalid document, expected byte or \"\n\u001b[0m\u001b[0;32m 122\u001b[0m \"unicode string.\")\n\u001b[0;32m 123\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", 421 | "\u001b[1;31mValueError\u001b[0m: np.nan is an invalid document, expected byte or unicode string." 422 | ] 423 | } 424 | ], 425 | "source": [ 426 | "tv2 = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 427 | "tv2.fit(data3['cus_comment'])\n", 428 | "clf2 = MultinomialNB()\n", 429 | "clf2.fit(tv2.transform(fenci(data3['cus_comment'])), data3['target'])" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "metadata": {}, 435 | "source": [ 436 | "使用全部数据进行训练" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": 82, 442 | "metadata": {}, 443 | "outputs": [ 444 | { 445 | "data": { 446 | "text/plain": [ 447 | "MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)" 448 | ] 449 | }, 450 | "execution_count": 82, 451 | "metadata": {}, 452 | "output_type": "execute_result" 453 | } 454 | ], 455 | "source": [ 456 | "tv2 = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 457 | "tv2.fit(data_model['cus_comment'])\n", 458 | "clf = MultinomialNB()\n", 459 | "clf.fit(tv2.transform(fenci(data_model['cus_comment'])), data_model['target'])" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": 83, 465 | "metadata": { 466 | "collapsed": true 467 | }, 468 | "outputs": [], 469 | "source": [ 470 | "def fenxi(strings):\n", 471 | " strings_fenci = fenci(pd.Series([strings]))\n", 472 | " return float(clf.predict_proba(tv2.transform(fenci(strings_fenci)))[:,1])" 473 | ] 474 | } 475 | ], 476 | "metadata": { 477 | "kernelspec": { 478 | "display_name": "Python 3", 479 | "language": "python", 480 | "name": "python3" 481 | }, 482 | "language_info": { 483 | "codemirror_mode": { 484 | "name": "ipython", 485 | "version": 3 486 | }, 487 | "file_extension": ".py", 488 | "mimetype": "text/x-python", 489 | "name": "python", 490 | "nbconvert_exporter": "python", 491 | "pygments_lexer": "ipython3", 492 | "version": "3.6.6" 493 | } 494 | }, 495 | "nbformat": 4, 496 | "nbformat_minor": 2 497 | } 498 | -------------------------------------------------------------------------------- /文本分析挖掘/.ipynb_checkpoints/Untitled1-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 2 6 | } 7 | -------------------------------------------------------------------------------- /文本分析挖掘/.ipynb_checkpoints/文本挖掘&情感分析-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 大众点评评价情感分析~\n", 8 | "先上结果:\n", 9 | "\n", 10 | "| 糖水店的评论文本 | 模型预测的情感评分 |\n", 11 | "| :------------------------------------------- | :----------------- |\n", 12 | "| '糖水味道不错,滑而不腻,赞一个,下次还会来' | 0.91 |\n", 13 | "| '味道一般,没啥特点' | 0.52 |\n", 14 | "| '排队老半天,环境很差,味道一般般' | 0.05 |\n", 15 | "\n", 16 | "模型的效果还可以的样子,yeah~接下来我们好好讲讲怎么做的哈,我们通过爬虫爬取了大众点评广州8家最热门糖水店的3W条评论信息以及评分作为训练数据,前面的分析我们得知*样本很不均衡*。接下来我们的整体思路就是:文本特征处理(分词、去停用词、TF-IDF)—机器学习建模—模型评价。\n", 17 | "\n", 18 | "我们先不处理样本不均衡问题,直接建模后查看结果,接下来我们再按照两种方法处理样本不均衡,对比结果。\n", 19 | "\n", 20 | "### 数据读入和探索" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 26, 26 | "metadata": { 27 | "scrolled": true 28 | }, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/html": [ 33 | "
\n", 34 | "\n", 47 | "\n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | "
cus_idcomment_timecomment_starcus_commentkouweihuanjingfuwushopIDstarsyearmonthweekdayhourcomment_lencus_comment_processed
0迷糊泰迪2018-09-20 06:48:00sml-str40南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ...非常好5189864.02018936184南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ...
1稱霸幼稚園2018-09-22 21:49:00sml-str40中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服...很好很好很好5189864.020189521266中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服...
2爱吃的美美侠2018-09-22 22:16:00sml-str40冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州...很好很好很好5189864.020189522341冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州...
3姜姜会吃胖2018-09-19 06:36:00sml-str40都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ...非常好很好很好5189864.02018926197都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ...
4forevercage2018-08-24 17:58:00sml-str50一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直...非常好很好很好5189865.020188417261一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直...
\n", 161 | "
" 162 | ], 163 | "text/plain": [ 164 | " cus_id comment_time comment_star \\\n", 165 | "0 迷糊泰迪 2018-09-20 06:48:00 sml-str40 \n", 166 | "1 稱霸幼稚園 2018-09-22 21:49:00 sml-str40 \n", 167 | "2 爱吃的美美侠 2018-09-22 22:16:00 sml-str40 \n", 168 | "3 姜姜会吃胖 2018-09-19 06:36:00 sml-str40 \n", 169 | "4 forevercage 2018-08-24 17:58:00 sml-str50 \n", 170 | "\n", 171 | " cus_comment kouwei huanjing fuwu \\\n", 172 | "0 南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ... 非常好 好 好 \n", 173 | "1 中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服... 很好 很好 很好 \n", 174 | "2 冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州... 很好 很好 很好 \n", 175 | "3 都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ... 非常好 很好 很好 \n", 176 | "4 一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直... 非常好 很好 很好 \n", 177 | "\n", 178 | " shopID stars year month weekday hour comment_len \\\n", 179 | "0 518986 4.0 2018 9 3 6 184 \n", 180 | "1 518986 4.0 2018 9 5 21 266 \n", 181 | "2 518986 4.0 2018 9 5 22 341 \n", 182 | "3 518986 4.0 2018 9 2 6 197 \n", 183 | "4 518986 5.0 2018 8 4 17 261 \n", 184 | "\n", 185 | " cus_comment_processed \n", 186 | "0 南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ... \n", 187 | "1 中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服... \n", 188 | "2 冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州... \n", 189 | "3 都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ... \n", 190 | "4 一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直... " 191 | ] 192 | }, 193 | "execution_count": 26, 194 | "metadata": {}, 195 | "output_type": "execute_result" 196 | } 197 | ], 198 | "source": [ 199 | "import pandas as pd\n", 200 | "from matplotlib import pyplot as plt\n", 201 | "import jieba\n", 202 | "data = pd.read_csv('data.csv')\n", 203 | "data.head()" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "### 构建标签值\n", 211 | "\n", 212 | "大众点评的评分分为1-5分,1-2为差评,4-5为好评,3为中评,因此我们把1-2记为0,4-5记为1,3为中评,对我们的情感分析作用不大,丢弃掉这部分数据,但是可以作为训练语料模型的语料。我们的情感评分可以转化为标签值为1的概率值,这样我们就把情感分析问题转为文本分类问题了。" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 27, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "#构建label值\n", 222 | "def zhuanhuan(score):\n", 223 | " if score > 3:\n", 224 | " return 1\n", 225 | " elif score < 3:\n", 226 | " return 0\n", 227 | " else:\n", 228 | " return None\n", 229 | " \n", 230 | "#特征值转换\n", 231 | "data['target'] = data['stars'].map(lambda x:zhuanhuan(x))\n", 232 | "data_model = data.dropna()" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "### 文本特征处理\n", 240 | "\n", 241 | "中文文本特征处理,需要进行中文分词,jieba分词库简单好用。接下来需要过滤停用词,网上能够搜到现成的。最后就要进行文本转向量,有词库表示法、TF-IDF、word2vec等,这篇文章作了详细介绍,推荐一波 https://zhuanlan.zhihu.com/p/44917421\n", 242 | "\n", 243 | "这里我们使用sklearn库的TF-IDF工具进行文本特征提取。" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 48, 249 | "metadata": {}, 250 | "outputs": [ 251 | { 252 | "data": { 253 | "text/plain": [ 254 | "26328 冲着 老字号 去 的 但 真的 不 咋 地 所有 的 东西 都 不是 先 做 的 点 了 煎...\n", 255 | "3457 名过其实 味道 一般 全是 人 价格 也 贵 感觉 碗碟 都 洗 不 干净\n", 256 | "17834 香芋 西米 以前 系 北京路 个边 上班 落 左班 都 会同 同事 去 吃糖 上个星期 去 ...\n", 257 | "7904 南信 的 双皮奶 非常 滑 不是 很甜 可是 很嫩 非常 美味 还有 鲜虾 肠有 好多好多 ...\n", 258 | "3258 环境 真的 是 一般 可能 是 老店 的 关系 但 很 传统 双皮奶 奶味重 甜度 适中 粥...\n", 259 | "Name: cus_comment, dtype: object" 260 | ] 261 | }, 262 | "execution_count": 48, 263 | "metadata": {}, 264 | "output_type": "execute_result" 265 | } 266 | ], 267 | "source": [ 268 | "#切分测试集、训练集\n", 269 | "from sklearn.model_selection import train_test_split\n", 270 | "x_train, x_test, y_train, y_test = train_test_split(data_model['cus_comment'], data_model['target'], random_state=3, test_size=0.25)\n", 271 | "\n", 272 | "#引入停用词\n", 273 | "infile = open(\"stopwords.txt\",encoding='utf-8')\n", 274 | "stopwords_lst = infile.readlines()\n", 275 | "stopwords = [x.strip() for x in stopwords_lst]\n", 276 | "\n", 277 | "#中文分词\n", 278 | "def fenci(train_data):\n", 279 | " words_df = train_data.apply(lambda x:' '.join(jieba.cut(x)))\n", 280 | " return words_df\n", 281 | " \n", 282 | "x_train[:5]" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 49, 288 | "metadata": {}, 289 | "outputs": [ 290 | { 291 | "data": { 292 | "text/plain": [ 293 | "TfidfVectorizer(analyzer='word', binary=False, decode_error='strict',\n", 294 | " dtype=, encoding='utf-8', input='content',\n", 295 | " lowercase=True, max_df=1.0, max_features=3000, min_df=1,\n", 296 | " ngram_range=(1, 2), norm='l2', preprocessor=None, smooth_idf=True,\n", 297 | " stop_words=['!', '\"', '#', '$', '%', '&', \"'\", '(', ')', '*', '+', ',', '-', '--', '.', '..', '...', '......', '...................', './', '.一', '记者', '数', '年', '月', '日', '时', '分', '秒', '/', '//', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '://', '::', ';', '<', '=', '>', '>>', '?', '@'...3', '94', '95', '96', '97', '98', '99', '100', '01', '02', '03', '04', '05', '06', '07', '08', '09'],\n", 298 | " strip_accents=None, sublinear_tf=False,\n", 299 | " token_pattern='(?u)\\\\b\\\\w\\\\w+\\\\b', tokenizer=None, use_idf=True,\n", 300 | " vocabulary=None)" 301 | ] 302 | }, 303 | "execution_count": 49, 304 | "metadata": {}, 305 | "output_type": "execute_result" 306 | } 307 | ], 308 | "source": [ 309 | "#使用TF-IDF进行文本转向量处理\n", 310 | "from sklearn.feature_extraction.text import TfidfVectorizer\n", 311 | "tv = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 312 | "tv.fit(x_train)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": {}, 318 | "source": [ 319 | "### 机器学习建模\n", 320 | "\n", 321 | "特征和标签已经准备好了,接下来就是建模了。这里我们使用文本分类的经典算法朴素贝叶斯算法,而且朴素贝叶斯算法的计算量较少。特征值是评论文本经过TF-IDF处理的向量,标签值评论的分类共两类,好评是1,差评是0。情感评分为分类器预测分类1的概率值。" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": 50, 327 | "metadata": {}, 328 | "outputs": [ 329 | { 330 | "data": { 331 | "text/plain": [ 332 | "0.9275308869629356" 333 | ] 334 | }, 335 | "execution_count": 50, 336 | "metadata": {}, 337 | "output_type": "execute_result" 338 | } 339 | ], 340 | "source": [ 341 | "#计算分类效果的准确率\n", 342 | "from sklearn.naive_bayes import MultinomialNB\n", 343 | "from sklearn.metrics import roc_auc_score, f1_score\n", 344 | "classifier = MultinomialNB()\n", 345 | "classifier.fit(tv.transform(x_train), y_train)\n", 346 | "classifier.score(tv.transform(x_test), y_test)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 51, 352 | "metadata": { 353 | "scrolled": false 354 | }, 355 | "outputs": [ 356 | { 357 | "data": { 358 | "text/plain": [ 359 | "0.8971584233148908" 360 | ] 361 | }, 362 | "execution_count": 51, 363 | "metadata": {}, 364 | "output_type": "execute_result" 365 | } 366 | ], 367 | "source": [ 368 | "#计算分类器的AUC值\n", 369 | "y_pred = classifier.predict_proba(tv.transform(x_test))[:,1]\n", 370 | "roc_auc_score(y_test,y_pred)" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 52, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "#计算一条评论文本的情感评分\n", 380 | "def ceshi(model,strings):\n", 381 | " strings_fenci = fenci(pd.Series([strings]))\n", 382 | " return float(model.predict_proba(tv.transform(strings_fenci))[:,1])" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 53, 388 | "metadata": {}, 389 | "outputs": [ 390 | { 391 | "name": "stdout", 392 | "output_type": "stream", 393 | "text": [ 394 | "好评实例的模型预测情感得分为0.8638082706675478\n", 395 | "差评实例的模型预测情感得分为0.7856544482460911\n" 396 | ] 397 | } 398 | ], 399 | "source": [ 400 | "#从大众点评网找两条评论来测试一下\n", 401 | "test1 = '很好吃,环境好,所有员工的态度都很好,上菜快,服务也很好,味道好吃,都是用蒸馏水煮的,推荐,超好吃' #5星好评\n", 402 | "test2 = '糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。' #1星差评\n", 403 | "print('好评实例的模型预测情感得分为{}\\n差评实例的模型预测情感得分为{}'.format(ceshi(classifier,test1),ceshi(classifier,test2)))" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "可以看出,准确率和AUC值都非常不错的样子,但点评网上的实际测试中,5星好评模型预测出来了,1星差评缺预测错误。为什么呢?我们查看一下**混淆矩阵**" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 54, 416 | "metadata": { 417 | "scrolled": true 418 | }, 419 | "outputs": [ 420 | { 421 | "data": { 422 | "text/plain": [ 423 | "array([[ 46, 385],\n", 424 | " [ 8, 4984]], dtype=int64)" 425 | ] 426 | }, 427 | "execution_count": 54, 428 | "metadata": {}, 429 | "output_type": "execute_result" 430 | } 431 | ], 432 | "source": [ 433 | "from sklearn.metrics import confusion_matrix\n", 434 | "y_predict = classifier.predict(tv.transform(x_test))\n", 435 | "cm = confusion_matrix(y_test, y_predict)\n", 436 | "cm" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "可以看出,**负类的预测非常不准**,433单准确预测为负类的只有15.7%,应该是由于**数据不平衡**导致的,模型的默认阈值为输出值的中位数。比如逻辑回归的输出范围为[0,1],当某个样本的输出大于0.5就会被划分为正例,反之为反例。在数据的类别不平衡时,采用默认的分类阈值可能会导致输出全部为正例,产生虚假的高准确度,导致分类失败。\n", 444 | "\n", 445 | "处理样本不均衡问题的方法,首先可以选择调整阈值,使得模型对于较少的类别更为敏感,或者选择合适的评估标准,比如ROC或者F1,而不是准确度(accuracy)。另外一种方法就是通过采样(sampling)来调整数据的不平衡。其中欠采样抛弃了大部分正例数据,从而弱化了其影响,可能会造成偏差很大的模型,同时,数据总是宝贵的,抛弃数据是很奢侈的。另外一种是过采样,下面我们就使用过采样方法来调整。\n", 446 | "\n", 447 | "### 过采样(单纯复制)\n", 448 | "\n", 449 | "单纯的重复了反例,因此会过分强调已有的反例。如果其中部分点标记错误或者是噪音,那么错误也容易被成倍的放大。因此最大的风险就是对反例过拟合。" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 55, 455 | "metadata": { 456 | "scrolled": true 457 | }, 458 | "outputs": [ 459 | { 460 | "data": { 461 | "text/plain": [ 462 | "1.0 19916\n", 463 | "0.0 1779\n", 464 | "Name: target, dtype: int64" 465 | ] 466 | }, 467 | "execution_count": 55, 468 | "metadata": {}, 469 | "output_type": "execute_result" 470 | } 471 | ], 472 | "source": [ 473 | "data['target'].value_counts()" 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "execution_count": 56, 479 | "metadata": {}, 480 | "outputs": [], 481 | "source": [ 482 | "#把0类样本复制10次,构造训练集\n", 483 | "index_tmp = y_train==0\n", 484 | "y_tmp = y_train[index_tmp]\n", 485 | "x_tmp = x_train[index_tmp]\n", 486 | "x_train2 = pd.concat([x_train,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp])\n", 487 | "y_train2 = pd.concat([y_train,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp])" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": 57, 493 | "metadata": {}, 494 | "outputs": [ 495 | { 496 | "data": { 497 | "text/plain": [ 498 | "0.9049699937533463" 499 | ] 500 | }, 501 | "execution_count": 57, 502 | "metadata": {}, 503 | "output_type": "execute_result" 504 | } 505 | ], 506 | "source": [ 507 | "#使用过采样样本(简单复制)进行模型训练,并查看准确率\n", 508 | "clf2 = MultinomialNB()\n", 509 | "clf2.fit(tv.transform(x_train2), y_train2)\n", 510 | "y_pred2 = clf2.predict_proba(tv.transform(x_test))[:,1]\n", 511 | "roc_auc_score(y_test,y_pred2)" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 58, 517 | "metadata": { 518 | "scrolled": true 519 | }, 520 | "outputs": [ 521 | { 522 | "data": { 523 | "text/plain": [ 524 | "array([[ 331, 100],\n", 525 | " [ 637, 4355]], dtype=int64)" 526 | ] 527 | }, 528 | "execution_count": 58, 529 | "metadata": {}, 530 | "output_type": "execute_result" 531 | } 532 | ], 533 | "source": [ 534 | "#查看此时的混淆矩阵\n", 535 | "y_predict2 = clf2.predict(tv.transform(x_test))\n", 536 | "cm = confusion_matrix(y_test, y_predict2)\n", 537 | "cm" 538 | ] 539 | }, 540 | { 541 | "cell_type": "markdown", 542 | "metadata": {}, 543 | "source": [ 544 | "可以看出,即使是简单粗暴的复制样本来处理样本不平衡问题,负样本的识别率大幅上升了,变为77%,满满的幸福感呀~我们自己写两句评语来看看" 545 | ] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": 59, 550 | "metadata": { 551 | "scrolled": false 552 | }, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/plain": [ 557 | "0.3520907656917545" 558 | ] 559 | }, 560 | "execution_count": 59, 561 | "metadata": {}, 562 | "output_type": "execute_result" 563 | } 564 | ], 565 | "source": [ 566 | "ceshi(clf2,'排队人太多,环境不好,口味一般')" 567 | ] 568 | }, 569 | { 570 | "cell_type": "markdown", 571 | "metadata": {}, 572 | "source": [ 573 | "可以看出把0类别的识别出来了,太棒了~" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "### 过采样(SMOTE算法)\n", 581 | "\n", 582 | "SMOTE(Synthetic minoritye over-sampling technique,SMOTE),是在局部区域通过K-近邻生成了新的反例。相较于简单的过采样,SMOTE降低了过拟合风险,但同时运算开销加大\n", 583 | "\n", 584 | "对SMOTE感兴趣的同学可以看下这篇文章https://www.jianshu.com/p/ecbc924860af" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": 60, 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "#使用SMOTE进行样本过采样处理\n", 594 | "from imblearn.over_sampling import SMOTE\n", 595 | "oversampler=SMOTE(random_state=0)\n", 596 | "x_train_vec = tv.transform(x_train)\n", 597 | "x_resampled, y_resampled = oversampler.fit_sample(x_train_vec, y_train)" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 61, 603 | "metadata": {}, 604 | "outputs": [ 605 | { 606 | "data": { 607 | "text/plain": [ 608 | "1.0 14920\n", 609 | "0.0 1348\n", 610 | "Name: target, dtype: int64" 611 | ] 612 | }, 613 | "execution_count": 61, 614 | "metadata": {}, 615 | "output_type": "execute_result" 616 | } 617 | ], 618 | "source": [ 619 | "#原始的样本分布\n", 620 | "y_train.value_counts()" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": 62, 626 | "metadata": { 627 | "scrolled": true 628 | }, 629 | "outputs": [ 630 | { 631 | "data": { 632 | "text/plain": [ 633 | "1.0 14920\n", 634 | "0.0 14920\n", 635 | "dtype: int64" 636 | ] 637 | }, 638 | "execution_count": 62, 639 | "metadata": {}, 640 | "output_type": "execute_result" 641 | } 642 | ], 643 | "source": [ 644 | "#经过SMOTE算法过采样后的样本分布情况\n", 645 | "pd.Series(y_resampled).value_counts()" 646 | ] 647 | }, 648 | { 649 | "cell_type": "markdown", 650 | "metadata": {}, 651 | "source": [ 652 | "我们经过插值,把0类数据也丰富为14923个数据了,这时候正负样本的比例为1:1,接下来我们用平衡后的数据进行训练,效果如何呢,好期待啊~" 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": 63, 658 | "metadata": { 659 | "scrolled": true 660 | }, 661 | "outputs": [ 662 | { 663 | "data": { 664 | "text/plain": [ 665 | "0.9072990102028675" 666 | ] 667 | }, 668 | "execution_count": 63, 669 | "metadata": {}, 670 | "output_type": "execute_result" 671 | } 672 | ], 673 | "source": [ 674 | "#使用过采样样本(SMOTE)进行模型训练,并查看准确率\n", 675 | "clf3 = MultinomialNB()\n", 676 | "clf3.fit(x_resampled, y_resampled)\n", 677 | "y_pred3 = clf3.predict_proba(tv.transform(x_test))[:,1]\n", 678 | "roc_auc_score(y_test,y_pred3)" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": 64, 684 | "metadata": { 685 | "scrolled": false 686 | }, 687 | "outputs": [ 688 | { 689 | "data": { 690 | "text/plain": [ 691 | "array([[ 330, 101],\n", 692 | " [ 601, 4391]], dtype=int64)" 693 | ] 694 | }, 695 | "execution_count": 64, 696 | "metadata": {}, 697 | "output_type": "execute_result" 698 | } 699 | ], 700 | "source": [ 701 | "#查看此时的准确率\n", 702 | "y_predict3 = clf3.predict(tv.transform(x_test))\n", 703 | "cm = confusion_matrix(y_test, y_predict3)\n", 704 | "cm" 705 | ] 706 | }, 707 | { 708 | "cell_type": "code", 709 | "execution_count": 65, 710 | "metadata": { 711 | "scrolled": true 712 | }, 713 | "outputs": [ 714 | { 715 | "data": { 716 | "text/plain": [ 717 | "0.23491893934922978" 718 | ] 719 | }, 720 | "execution_count": 65, 721 | "metadata": {}, 722 | "output_type": "execute_result" 723 | } 724 | ], 725 | "source": [ 726 | "#到网上找一条差评来测试一下情感评分的预测效果\n", 727 | "test3 = '糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。'\n", 728 | "ceshi(clf3,test3)" 729 | ] 730 | }, 731 | { 732 | "cell_type": "markdown", 733 | "metadata": {}, 734 | "source": [ 735 | "可以看出,使用SMOTE插值与简单的数据复制比起来,AUC率略有提高,实际预测效果也挺好\n", 736 | "\n", 737 | "### 模型评估测试\n", 738 | "\n", 739 | "接下来我们把3W条数据都拿来训练,数据量变多了,模型效果应该会更好" 740 | ] 741 | }, 742 | { 743 | "cell_type": "code", 744 | "execution_count": 66, 745 | "metadata": { 746 | "scrolled": true 747 | }, 748 | "outputs": [], 749 | "source": [ 750 | "#词向量训练\n", 751 | "tv2 = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 752 | "tv2.fit(data_model['cus_comment'])\n", 753 | "\n", 754 | "#SMOTE插值\n", 755 | "X_tmp = tv2.transform(data_model['cus_comment'])\n", 756 | "y_tmp = data_model['target']\n", 757 | "sm = SMOTE(random_state=0)\n", 758 | "X,y = sm.fit_sample(X_tmp, y_tmp)\n", 759 | "\n", 760 | "clf = MultinomialNB()\n", 761 | "clf.fit(X, y)\n", 762 | "\n", 763 | "def fenxi(strings):\n", 764 | " strings_fenci = fenci(pd.Series([strings]))\n", 765 | " return float(clf.predict_proba(tv2.transform(strings_fenci))[:,1])" 766 | ] 767 | }, 768 | { 769 | "cell_type": "code", 770 | "execution_count": 67, 771 | "metadata": {}, 772 | "outputs": [ 773 | { 774 | "data": { 775 | "text/plain": [ 776 | "0.28900092243477077" 777 | ] 778 | }, 779 | "execution_count": 67, 780 | "metadata": {}, 781 | "output_type": "execute_result" 782 | } 783 | ], 784 | "source": [ 785 | "#到网上找一条差评来测试一下\n", 786 | "fenxi('糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。')" 787 | ] 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "metadata": {}, 792 | "source": [ 793 | "只用到了简单的机器学习,就做出了不错的情感分析效果,知识的力量真是强大呀,666~\n", 794 | "### 后续优化方向\n", 795 | "\n", 796 | "- 使用更复杂的机器学习模型如神经网络、支持向量机等\n", 797 | "- 模型的调参\n", 798 | "- 行业词库的构建\n", 799 | "- 增加数据量\n", 800 | "- 优化情感分析的算法\n", 801 | "- 增加标签提取等\n", 802 | "- 项目部署到服务器上,更好地分享和测试模型的效果" 803 | ] 804 | } 805 | ], 806 | "metadata": { 807 | "kernelspec": { 808 | "display_name": "Python 3", 809 | "language": "python", 810 | "name": "python3" 811 | }, 812 | "language_info": { 813 | "codemirror_mode": { 814 | "name": "ipython", 815 | "version": 3 816 | }, 817 | "file_extension": ".py", 818 | "mimetype": "text/x-python", 819 | "name": "python", 820 | "nbconvert_exporter": "python", 821 | "pygments_lexer": "ipython3", 822 | "version": "3.6.5" 823 | } 824 | }, 825 | "nbformat": 4, 826 | "nbformat_minor": 2 827 | } 828 | -------------------------------------------------------------------------------- /文本分析挖掘/msyh.ttc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/msyh.ttc -------------------------------------------------------------------------------- /文本分析挖掘/source/data_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/data_head.png -------------------------------------------------------------------------------- /文本分析挖掘/source/dianpu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/dianpu.png -------------------------------------------------------------------------------- /文本分析挖掘/source/len.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/len.png -------------------------------------------------------------------------------- /文本分析挖掘/source/stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/stars.png -------------------------------------------------------------------------------- /文本分析挖掘/source/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/time.png -------------------------------------------------------------------------------- /文本分析挖掘/source/wordcloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/py-bin/dianping_textmining/9abe0f28881bebc442b638c3606ccd7a3c803632/文本分析挖掘/source/wordcloud.png -------------------------------------------------------------------------------- /文本分析挖掘/stopwords.txt: -------------------------------------------------------------------------------- 1 | ! 2 | " 3 | # 4 | $ 5 | % 6 | & 7 | ' 8 | ( 9 | ) 10 | * 11 | + 12 | , 13 | - 14 | -- 15 | . 16 | .. 17 | ... 18 | ...... 19 | ................... 20 | ./ 21 | .一 22 | 记者 23 | 数 24 | 年 25 | 月 26 | 日 27 | 时 28 | 分 29 | 秒 30 | / 31 | // 32 | 0 33 | 1 34 | 2 35 | 3 36 | 4 37 | 5 38 | 6 39 | 7 40 | 8 41 | 9 42 | : 43 | :// 44 | :: 45 | ; 46 | < 47 | = 48 | > 49 | >> 50 | ? 51 | @ 52 | A 53 | Lex 54 | [ 55 | \ 56 | ] 57 | 【 58 | 】 59 | ^ 60 | _ 61 | ` 62 | exp 63 | sub 64 | sup 65 | | 66 | } 67 | ~ 68 | ~~~~ 69 | · 70 | × 71 | ××× 72 | Δ 73 | Ψ 74 | γ 75 | μ 76 | φ 77 | φ. 78 | В 79 | — 80 | —— 81 | ——— 82 | ‘ 83 | ’ 84 | ’‘ 85 | “ 86 | ” 87 | ”, 88 | … 89 | …… 90 | …………………………………………………③ 91 | ′∈ 92 | ′| 93 | ℃ 94 | Ⅲ 95 | ↑ 96 | → 97 | ∈[ 98 | ∪φ∈ 99 | ≈ 100 | ① 101 | ② 102 | ②c 103 | ③ 104 | ③] 105 | ④ 106 | ⑤ 107 | ⑥ 108 | ⑦ 109 | ⑧ 110 | ⑨ 111 | ⑩ 112 | ── 113 | ■ 114 | ▲ 115 |   116 | 、 117 | 。 118 | 〈 119 | 〉 120 | 《 121 | 》 122 | 》), 123 | 」 124 | 『 125 | 』 126 | 〔 127 | 〕 128 | 〕〔 129 | ㈧ 130 | 一 131 | 一. 132 | 一一 133 | 一下 134 | 一个 135 | 一些 136 | 一何 137 | 一切 138 | 一则 139 | 一则通过 140 | 一天 141 | 一定 142 | 一方面 143 | 一旦 144 | 一时 145 | 一来 146 | 一样 147 | 一次 148 | 一片 149 | 一番 150 | 一直 151 | 一致 152 | 一般 153 | 一起 154 | 一转眼 155 | 一边 156 | 一面 157 | 男子 158 | 女子 159 | 七 160 | 万一 161 | 三 162 | 三天两头 163 | 三番两次 164 | 三番五次 165 | 上 166 | 上下 167 | 上升 168 | 上去 169 | 上来 170 | 上述 171 | 上面 172 | 下 173 | 下列 174 | 下去 175 | 下来 176 | 下面 177 | 不 178 | 不一 179 | 不下 180 | 不久 181 | 不了 182 | 不亦乐乎 183 | 不仅 184 | 不仅...而且 185 | 不仅仅 186 | 不仅仅是 187 | 不会 188 | 不但 189 | 不但...而且 190 | 不光 191 | 不免 192 | 不再 193 | 不力 194 | 不单 195 | 不变 196 | 不只 197 | 不可 198 | 不可开交 199 | 不可抗拒 200 | 不同 201 | 不外 202 | 不外乎 203 | 不够 204 | 不大 205 | 不如 206 | 不妨 207 | 不定 208 | 不对 209 | 不少 210 | 不尽 211 | 不尽然 212 | 不巧 213 | 不已 214 | 不常 215 | 不得 216 | 不得不 217 | 不得了 218 | 不得已 219 | 不必 220 | 不怎么 221 | 不怕 222 | 不惟 223 | 不成 224 | 不拘 225 | 不择手段 226 | 不敢 227 | 不料 228 | 不断 229 | 不日 230 | 不时 231 | 不是 232 | 不曾 233 | 不止 234 | 不止一次 235 | 不比 236 | 不消 237 | 不满 238 | 不然 239 | 不然的话 240 | 不特 241 | 不独 242 | 不由得 243 | 不知不觉 244 | 不管 245 | 不管怎样 246 | 不经意 247 | 不胜 248 | 不能 249 | 不能不 250 | 不至于 251 | 不若 252 | 不要 253 | 不论 254 | 不起 255 | 不足 256 | 不过 257 | 不迭 258 | 不问 259 | 不限 260 | 与 261 | 与其 262 | 与其说 263 | 与否 264 | 与此同时 265 | 专门 266 | 且 267 | 且不说 268 | 且说 269 | 两者 270 | 严格 271 | 严重 272 | 个 273 | 个人 274 | 个别 275 | 中小 276 | 中间 277 | 丰富 278 | 串行 279 | 临 280 | 临到 281 | 为 282 | 为主 283 | 为了 284 | 为什么 285 | 为什麽 286 | 为何 287 | 为止 288 | 为此 289 | 为着 290 | 主张 291 | 主要 292 | 举凡 293 | 举行 294 | 乃 295 | 乃至 296 | 乃至于 297 | 么 298 | 之 299 | 之一 300 | 之前 301 | 之后 302 | 之後 303 | 之所以 304 | 之类 305 | 乌乎 306 | 乎 307 | 乒 308 | 乘 309 | 乘势 310 | 乘机 311 | 乘胜 312 | 乘虚 313 | 乘隙 314 | 九 315 | 也 316 | 也好 317 | 也就是说 318 | 也是 319 | 也罢 320 | 了 321 | 了解 322 | 争取 323 | 二 324 | 二来 325 | 二话不说 326 | 二话没说 327 | 于 328 | 于是 329 | 于是乎 330 | 云云 331 | 云尔 332 | 互 333 | 互相 334 | 五 335 | 些 336 | 交口 337 | 亦 338 | 产生 339 | 亲口 340 | 亲手 341 | 亲眼 342 | 亲自 343 | 亲身 344 | 人 345 | 人人 346 | 人们 347 | 人家 348 | 人民 349 | 什么 350 | 什么样 351 | 什麽 352 | 仅 353 | 仅仅 354 | 今 355 | 今后 356 | 今天 357 | 今年 358 | 今後 359 | 介于 360 | 仍 361 | 仍旧 362 | 仍然 363 | 从 364 | 从不 365 | 从严 366 | 从中 367 | 从事 368 | 从今以后 369 | 从优 370 | 从古到今 371 | 从古至今 372 | 从头 373 | 从宽 374 | 从小 375 | 从新 376 | 从无到有 377 | 从早到晚 378 | 从未 379 | 从来 380 | 从此 381 | 从此以后 382 | 从而 383 | 从轻 384 | 从速 385 | 从重 386 | 他 387 | 他人 388 | 他们 389 | 他是 390 | 他的 391 | 代替 392 | 以 393 | 以上 394 | 以下 395 | 以为 396 | 以便 397 | 以免 398 | 以前 399 | 以及 400 | 以后 401 | 以外 402 | 以後 403 | 以故 404 | 以期 405 | 以来 406 | 以至 407 | 以至于 408 | 以致 409 | 们 410 | 任 411 | 任何 412 | 任凭 413 | 任务 414 | 企图 415 | 伙同 416 | 会 417 | 伟大 418 | 传 419 | 传说 420 | 传闻 421 | 似乎 422 | 似的 423 | 但 424 | 但凡 425 | 但愿 426 | 但是 427 | 何 428 | 何乐而不为 429 | 何以 430 | 何况 431 | 何处 432 | 何妨 433 | 何尝 434 | 何必 435 | 何时 436 | 何止 437 | 何苦 438 | 何须 439 | 余外 440 | 作为 441 | 你 442 | 你们 443 | 你是 444 | 你的 445 | 使 446 | 使得 447 | 使用 448 | 例如 449 | 依 450 | 依据 451 | 依照 452 | 依靠 453 | 便 454 | 便于 455 | 促进 456 | 保持 457 | 保管 458 | 保险 459 | 俺 460 | 俺们 461 | 倍加 462 | 倍感 463 | 倒不如 464 | 倒不如说 465 | 倒是 466 | 倘 467 | 倘使 468 | 倘或 469 | 倘然 470 | 倘若 471 | 借 472 | 借以 473 | 借此 474 | 假使 475 | 假如 476 | 假若 477 | 偏偏 478 | 做到 479 | 偶尔 480 | 偶而 481 | 傥然 482 | 像 483 | 儿 484 | 允许 485 | 元/吨 486 | 充其极 487 | 充其量 488 | 充分 489 | 先不先 490 | 先后 491 | 先後 492 | 先生 493 | 光 494 | 光是 495 | 全体 496 | 全力 497 | 全年 498 | 全然 499 | 全身心 500 | 全部 501 | 全都 502 | 全面 503 | 八 504 | 八成 505 | 公然 506 | 六 507 | 兮 508 | 共 509 | 共同 510 | 共总 511 | 关于 512 | 其 513 | 其一 514 | 其中 515 | 其二 516 | 其他 517 | 其余 518 | 其后 519 | 其它 520 | 其实 521 | 其次 522 | 具体 523 | 具体地说 524 | 具体来说 525 | 具体说来 526 | 具有 527 | 兼之 528 | 内 529 | 再 530 | 再其次 531 | 再则 532 | 再有 533 | 再次 534 | 再者 535 | 再者说 536 | 再说 537 | 冒 538 | 冲 539 | 决不 540 | 决定 541 | 决非 542 | 况且 543 | 准备 544 | 凑巧 545 | 凝神 546 | 几 547 | 几乎 548 | 几度 549 | 几时 550 | 几番 551 | 几经 552 | 凡 553 | 凡是 554 | 凭 555 | 凭借 556 | 出 557 | 出于 558 | 出去 559 | 出来 560 | 出现 561 | 分别 562 | 分头 563 | 分期 564 | 分期分批 565 | 切 566 | 切不可 567 | 切切 568 | 切勿 569 | 切莫 570 | 则 571 | 则甚 572 | 刚 573 | 刚好 574 | 刚巧 575 | 刚才 576 | 初 577 | 别 578 | 别人 579 | 别处 580 | 别是 581 | 别的 582 | 别管 583 | 别说 584 | 到 585 | 到了儿 586 | 到处 587 | 到头 588 | 到头来 589 | 到底 590 | 到目前为止 591 | 前后 592 | 前此 593 | 前者 594 | 前进 595 | 前面 596 | 加上 597 | 加之 598 | 加以 599 | 加入 600 | 加强 601 | 动不动 602 | 动辄 603 | 勃然 604 | 匆匆 605 | 十分 606 | 千 607 | 千万 608 | 千万千万 609 | 半 610 | 单 611 | 单单 612 | 单纯 613 | 即 614 | 即令 615 | 即使 616 | 即便 617 | 即刻 618 | 即如 619 | 即将 620 | 即或 621 | 即是说 622 | 即若 623 | 却 624 | 却不 625 | 历 626 | 原来 627 | 去 628 | 又 629 | 又及 630 | 及 631 | 及其 632 | 及时 633 | 及至 634 | 双方 635 | 反之 636 | 反之亦然 637 | 反之则 638 | 反倒 639 | 反倒是 640 | 反应 641 | 反手 642 | 反映 643 | 反而 644 | 反过来 645 | 反过来说 646 | 取得 647 | 取道 648 | 受到 649 | 变成 650 | 古来 651 | 另 652 | 另一个 653 | 另一方面 654 | 另外 655 | 另悉 656 | 另方面 657 | 另行 658 | 只 659 | 只当 660 | 只怕 661 | 只是 662 | 只有 663 | 只消 664 | 只要 665 | 只限 666 | 叫 667 | 叫做 668 | 召开 669 | 叮咚 670 | 叮当 671 | 可 672 | 可以 673 | 可好 674 | 可是 675 | 可能 676 | 可见 677 | 各 678 | 各个 679 | 各人 680 | 各位 681 | 各地 682 | 各式 683 | 各种 684 | 各级 685 | 各自 686 | 合理 687 | 同 688 | 同一 689 | 同时 690 | 同样 691 | 后 692 | 后来 693 | 后者 694 | 后面 695 | 向 696 | 向使 697 | 向着 698 | 吓 699 | 吗 700 | 否则 701 | 吧 702 | 吧哒 703 | 吱 704 | 呀 705 | 呃 706 | 呆呆地 707 | 呐 708 | 呕 709 | 呗 710 | 呜 711 | 呜呼 712 | 呢 713 | 周围 714 | 呵 715 | 呵呵 716 | 呸 717 | 呼哧 718 | 呼啦 719 | 咋 720 | 和 721 | 咚 722 | 咦 723 | 咧 724 | 咱 725 | 咱们 726 | 咳 727 | 哇 728 | 哈 729 | 哈哈 730 | 哉 731 | 哎 732 | 哎呀 733 | 哎哟 734 | 哗 735 | 哗啦 736 | 哟 737 | 哦 738 | 哩 739 | 哪 740 | 哪个 741 | 哪些 742 | 哪儿 743 | 哪天 744 | 哪年 745 | 哪怕 746 | 哪样 747 | 哪边 748 | 哪里 749 | 哼 750 | 哼唷 751 | 唉 752 | 唯有 753 | 啊 754 | 啊呀 755 | 啊哈 756 | 啊哟 757 | 啐 758 | 啥 759 | 啦 760 | 啪达 761 | 啷当 762 | 喀 763 | 喂 764 | 喏 765 | 喔唷 766 | 喽 767 | 嗡 768 | 嗡嗡 769 | 嗬 770 | 嗯 771 | 嗳 772 | 嘎 773 | 嘎嘎 774 | 嘎登 775 | 嘘 776 | 嘛 777 | 嘻 778 | 嘿 779 | 嘿嘿 780 | 四 781 | 因 782 | 因为 783 | 因了 784 | 因此 785 | 因着 786 | 因而 787 | 固 788 | 固然 789 | 在 790 | 在下 791 | 在于 792 | 地 793 | 均 794 | 坚决 795 | 坚持 796 | 基于 797 | 基本 798 | 基本上 799 | 处在 800 | 处处 801 | 处理 802 | 复杂 803 | 多 804 | 多么 805 | 多亏 806 | 多多 807 | 多多少少 808 | 多多益善 809 | 多少 810 | 多年前 811 | 多年来 812 | 多数 813 | 多次 814 | 够瞧的 815 | 大 816 | 大不了 817 | 大举 818 | 大事 819 | 大体 820 | 大体上 821 | 大凡 822 | 大力 823 | 大多 824 | 大多数 825 | 大大 826 | 大家 827 | 大张旗鼓 828 | 大批 829 | 大抵 830 | 大概 831 | 大略 832 | 大约 833 | 大致 834 | 大都 835 | 大量 836 | 大面儿上 837 | 失去 838 | 奇 839 | 奈 840 | 奋勇 841 | 她 842 | 她们 843 | 她是 844 | 她的 845 | 好 846 | 好在 847 | 好的 848 | 好象 849 | 如 850 | 如上 851 | 如上所述 852 | 如下 853 | 如今 854 | 如何 855 | 如其 856 | 如前所述 857 | 如同 858 | 如常 859 | 如是 860 | 如期 861 | 如果 862 | 如次 863 | 如此 864 | 如此等等 865 | 如若 866 | 始而 867 | 姑且 868 | 存在 869 | 存心 870 | 孰料 871 | 孰知 872 | 宁 873 | 宁可 874 | 宁愿 875 | 宁肯 876 | 它 877 | 它们 878 | 它们的 879 | 它是 880 | 它的 881 | 安全 882 | 完全 883 | 完成 884 | 定 885 | 实现 886 | 实际 887 | 宣布 888 | 容易 889 | 密切 890 | 对 891 | 对于 892 | 对应 893 | 对待 894 | 对方 895 | 对比 896 | 将 897 | 将才 898 | 将要 899 | 将近 900 | 小 901 | 少数 902 | 尔 903 | 尔后 904 | 尔尔 905 | 尔等 906 | 尚且 907 | 尤其 908 | 就 909 | 就地 910 | 就是 911 | 就是了 912 | 就是说 913 | 就此 914 | 就算 915 | 就要 916 | 尽 917 | 尽可能 918 | 尽如人意 919 | 尽心尽力 920 | 尽心竭力 921 | 尽快 922 | 尽早 923 | 尽然 924 | 尽管 925 | 尽管如此 926 | 尽量 927 | 局外 928 | 居然 929 | 届时 930 | 属于 931 | 屡 932 | 屡屡 933 | 屡次 934 | 屡次三番 935 | 岂 936 | 岂但 937 | 岂止 938 | 岂非 939 | 川流不息 940 | 左右 941 | 巨大 942 | 巩固 943 | 差一点 944 | 差不多 945 | 己 946 | 已 947 | 已矣 948 | 已经 949 | 巴 950 | 巴巴 951 | 带 952 | 帮助 953 | 常 954 | 常常 955 | 常言说 956 | 常言说得好 957 | 常言道 958 | 平素 959 | 年复一年 960 | 并 961 | 并不 962 | 并不是 963 | 并且 964 | 并排 965 | 并无 966 | 并没 967 | 并没有 968 | 并肩 969 | 并非 970 | 广大 971 | 广泛 972 | 应当 973 | 应用 974 | 应该 975 | 庶乎 976 | 庶几 977 | 开外 978 | 开始 979 | 开展 980 | 引起 981 | 弗 982 | 弹指之间 983 | 强烈 984 | 强调 985 | 归 986 | 归根到底 987 | 归根结底 988 | 归齐 989 | 当 990 | 当下 991 | 当中 992 | 当儿 993 | 当前 994 | 当即 995 | 当口儿 996 | 当地 997 | 当场 998 | 当头 999 | 当庭 1000 | 当时 1001 | 当然 1002 | 当真 1003 | 当着 1004 | 形成 1005 | 彻夜 1006 | 彻底 1007 | 彼 1008 | 彼时 1009 | 彼此 1010 | 往 1011 | 往往 1012 | 待 1013 | 待到 1014 | 很 1015 | 很多 1016 | 很少 1017 | 後来 1018 | 後面 1019 | 得 1020 | 得了 1021 | 得出 1022 | 得到 1023 | 得天独厚 1024 | 得起 1025 | 心里 1026 | 必 1027 | 必定 1028 | 必将 1029 | 必然 1030 | 必要 1031 | 必须 1032 | 快 1033 | 快要 1034 | 忽地 1035 | 忽然 1036 | 怎 1037 | 怎么 1038 | 怎么办 1039 | 怎么样 1040 | 怎奈 1041 | 怎样 1042 | 怎麽 1043 | 怕 1044 | 急匆匆 1045 | 怪 1046 | 怪不得 1047 | 总之 1048 | 总是 1049 | 总的来看 1050 | 总的来说 1051 | 总的说来 1052 | 总结 1053 | 总而言之 1054 | 恍然 1055 | 恐怕 1056 | 恰似 1057 | 恰好 1058 | 恰如 1059 | 恰巧 1060 | 恰恰 1061 | 恰恰相反 1062 | 恰逢 1063 | 您 1064 | 您们 1065 | 您是 1066 | 惟其 1067 | 惯常 1068 | 意思 1069 | 愤然 1070 | 愿意 1071 | 慢说 1072 | 成为 1073 | 成年 1074 | 成年累月 1075 | 成心 1076 | 我 1077 | 我们 1078 | 我是 1079 | 我的 1080 | 或 1081 | 或则 1082 | 或多或少 1083 | 或是 1084 | 或曰 1085 | 或者 1086 | 或许 1087 | 战斗 1088 | 截然 1089 | 截至 1090 | 所 1091 | 所以 1092 | 所在 1093 | 所幸 1094 | 所有 1095 | 所谓 1096 | 才 1097 | 才能 1098 | 扑通 1099 | 打 1100 | 打从 1101 | 打开天窗说亮话 1102 | 扩大 1103 | 把 1104 | 抑或 1105 | 抽冷子 1106 | 拦腰 1107 | 拿 1108 | 按 1109 | 按时 1110 | 按期 1111 | 按照 1112 | 按理 1113 | 按说 1114 | 挨个 1115 | 挨家挨户 1116 | 挨次 1117 | 挨着 1118 | 挨门挨户 1119 | 挨门逐户 1120 | 换句话说 1121 | 换言之 1122 | 据 1123 | 据实 1124 | 据悉 1125 | 据我所知 1126 | 据此 1127 | 据称 1128 | 据说 1129 | 掌握 1130 | 接下来 1131 | 接着 1132 | 接著 1133 | 接连不断 1134 | 放量 1135 | 故 1136 | 故意 1137 | 故此 1138 | 故而 1139 | 敞开儿 1140 | 敢 1141 | 敢于 1142 | 敢情 1143 | 数/ 1144 | 整个 1145 | 断然 1146 | 方 1147 | 方便 1148 | 方才 1149 | 方能 1150 | 方面 1151 | 旁人 1152 | 无 1153 | 无宁 1154 | 无法 1155 | 无论 1156 | 既 1157 | 既...又 1158 | 既往 1159 | 既是 1160 | 既然 1161 | 日复一日 1162 | 日渐 1163 | 日益 1164 | 日臻 1165 | 日见 1166 | 时候 1167 | 昂然 1168 | 明显 1169 | 明确 1170 | 是 1171 | 是不是 1172 | 是以 1173 | 是否 1174 | 是的 1175 | 显然 1176 | 显著 1177 | 普通 1178 | 普遍 1179 | 暗中 1180 | 暗地里 1181 | 暗自 1182 | 更 1183 | 更为 1184 | 更加 1185 | 更进一步 1186 | 曾 1187 | 曾经 1188 | 替 1189 | 替代 1190 | 最 1191 | 最后 1192 | 最大 1193 | 最好 1194 | 最後 1195 | 最近 1196 | 最高 1197 | 有 1198 | 有些 1199 | 有关 1200 | 有利 1201 | 有力 1202 | 有及 1203 | 有所 1204 | 有效 1205 | 有时 1206 | 有点 1207 | 有的 1208 | 有的是 1209 | 有着 1210 | 有著 1211 | 望 1212 | 朝 1213 | 朝着 1214 | 末##末 1215 | 本 1216 | 本人 1217 | 本地 1218 | 本着 1219 | 本身 1220 | 权时 1221 | 来 1222 | 来不及 1223 | 来得及 1224 | 来看 1225 | 来着 1226 | 来自 1227 | 来讲 1228 | 来说 1229 | 极 1230 | 极为 1231 | 极了 1232 | 极其 1233 | 极力 1234 | 极大 1235 | 极度 1236 | 极端 1237 | 构成 1238 | 果然 1239 | 果真 1240 | 某 1241 | 某个 1242 | 某些 1243 | 某某 1244 | 根据 1245 | 根本 1246 | 格外 1247 | 梆 1248 | 概 1249 | 次第 1250 | 欢迎 1251 | 欤 1252 | 正值 1253 | 正在 1254 | 正如 1255 | 正巧 1256 | 正常 1257 | 正是 1258 | 此 1259 | 此中 1260 | 此后 1261 | 此地 1262 | 此处 1263 | 此外 1264 | 此时 1265 | 此次 1266 | 此间 1267 | 殆 1268 | 毋宁 1269 | 每 1270 | 每个 1271 | 每天 1272 | 每年 1273 | 每当 1274 | 每时每刻 1275 | 每每 1276 | 每逢 1277 | 比 1278 | 比及 1279 | 比如 1280 | 比如说 1281 | 比方 1282 | 比照 1283 | 比起 1284 | 比较 1285 | 毕竟 1286 | 毫不 1287 | 毫无 1288 | 毫无例外 1289 | 毫无保留地 1290 | 汝 1291 | 沙沙 1292 | 没 1293 | 没奈何 1294 | 没有 1295 | 沿 1296 | 沿着 1297 | 注意 1298 | 活 1299 | 深入 1300 | 清楚 1301 | 满 1302 | 满足 1303 | 漫说 1304 | 焉 1305 | 然 1306 | 然则 1307 | 然后 1308 | 然後 1309 | 然而 1310 | 照 1311 | 照着 1312 | 牢牢 1313 | 特别是 1314 | 特殊 1315 | 特点 1316 | 犹且 1317 | 犹自 1318 | 独 1319 | 独自 1320 | 猛然 1321 | 猛然间 1322 | 率尔 1323 | 率然 1324 | 现代 1325 | 现在 1326 | 理应 1327 | 理当 1328 | 理该 1329 | 瑟瑟 1330 | 甚且 1331 | 甚么 1332 | 甚或 1333 | 甚而 1334 | 甚至 1335 | 甚至于 1336 | 用 1337 | 用来 1338 | 甫 1339 | 甭 1340 | 由 1341 | 由于 1342 | 由是 1343 | 由此 1344 | 由此可见 1345 | 略 1346 | 略为 1347 | 略加 1348 | 略微 1349 | 白 1350 | 白白 1351 | 的 1352 | 的确 1353 | 的话 1354 | 皆可 1355 | 目前 1356 | 直到 1357 | 直接 1358 | 相似 1359 | 相信 1360 | 相反 1361 | 相同 1362 | 相对 1363 | 相对而言 1364 | 相应 1365 | 相当 1366 | 相等 1367 | 省得 1368 | 看 1369 | 看上去 1370 | 看出 1371 | 看到 1372 | 看来 1373 | 看样子 1374 | 看看 1375 | 看见 1376 | 看起来 1377 | 真是 1378 | 真正 1379 | 眨眼 1380 | 着 1381 | 着呢 1382 | 矣 1383 | 矣乎 1384 | 矣哉 1385 | 知道 1386 | 砰 1387 | 确定 1388 | 碰巧 1389 | 社会主义 1390 | 离 1391 | 种 1392 | 积极 1393 | 移动 1394 | 究竟 1395 | 穷年累月 1396 | 突出 1397 | 突然 1398 | 窃 1399 | 立 1400 | 立刻 1401 | 立即 1402 | 立地 1403 | 立时 1404 | 立马 1405 | 竟 1406 | 竟然 1407 | 竟而 1408 | 第 1409 | 第二 1410 | 等 1411 | 等到 1412 | 等等 1413 | 策略地 1414 | 简直 1415 | 简而言之 1416 | 简言之 1417 | 管 1418 | 类如 1419 | 粗 1420 | 精光 1421 | 紧接着 1422 | 累年 1423 | 累次 1424 | 纯 1425 | 纯粹 1426 | 纵 1427 | 纵令 1428 | 纵使 1429 | 纵然 1430 | 练习 1431 | 组成 1432 | 经 1433 | 经常 1434 | 经过 1435 | 结合 1436 | 结果 1437 | 给 1438 | 绝 1439 | 绝不 1440 | 绝对 1441 | 绝非 1442 | 绝顶 1443 | 继之 1444 | 继后 1445 | 继续 1446 | 继而 1447 | 维持 1448 | 综上所述 1449 | 缕缕 1450 | 罢了 1451 | 老 1452 | 老大 1453 | 老是 1454 | 老老实实 1455 | 考虑 1456 | 者 1457 | 而 1458 | 而且 1459 | 而况 1460 | 而又 1461 | 而后 1462 | 而外 1463 | 而已 1464 | 而是 1465 | 而言 1466 | 而论 1467 | 联系 1468 | 联袂 1469 | 背地里 1470 | 背靠背 1471 | 能 1472 | 能否 1473 | 能够 1474 | 腾 1475 | 自 1476 | 自个儿 1477 | 自从 1478 | 自各儿 1479 | 自后 1480 | 自家 1481 | 自己 1482 | 自打 1483 | 自身 1484 | 臭 1485 | 至 1486 | 至于 1487 | 至今 1488 | 至若 1489 | 致 1490 | 般的 1491 | 良好 1492 | 若 1493 | 若夫 1494 | 若是 1495 | 若果 1496 | 若非 1497 | 范围 1498 | 莫 1499 | 莫不 1500 | 莫不然 1501 | 莫如 1502 | 莫若 1503 | 莫非 1504 | 获得 1505 | 藉以 1506 | 虽 1507 | 虽则 1508 | 虽然 1509 | 虽说 1510 | 蛮 1511 | 行为 1512 | 行动 1513 | 表明 1514 | 表示 1515 | 被 1516 | 要 1517 | 要不 1518 | 要不是 1519 | 要不然 1520 | 要么 1521 | 要是 1522 | 要求 1523 | 见 1524 | 规定 1525 | 觉得 1526 | 譬喻 1527 | 譬如 1528 | 认为 1529 | 认真 1530 | 认识 1531 | 让 1532 | 许多 1533 | 论 1534 | 论说 1535 | 设使 1536 | 设或 1537 | 设若 1538 | 诚如 1539 | 诚然 1540 | 话说 1541 | 该 1542 | 该当 1543 | 说明 1544 | 说来 1545 | 说说 1546 | 请勿 1547 | 诸 1548 | 诸位 1549 | 诸如 1550 | 谁 1551 | 谁人 1552 | 谁料 1553 | 谁知 1554 | 谨 1555 | 豁然 1556 | 贼死 1557 | 赖以 1558 | 赶 1559 | 赶快 1560 | 赶早不赶晚 1561 | 起 1562 | 起先 1563 | 起初 1564 | 起头 1565 | 起来 1566 | 起见 1567 | 起首 1568 | 趁 1569 | 趁便 1570 | 趁势 1571 | 趁早 1572 | 趁机 1573 | 趁热 1574 | 趁着 1575 | 越是 1576 | 距 1577 | 跟 1578 | 路经 1579 | 转动 1580 | 转变 1581 | 转贴 1582 | 轰然 1583 | 较 1584 | 较为 1585 | 较之 1586 | 较比 1587 | 边 1588 | 达到 1589 | 达旦 1590 | 迄 1591 | 迅速 1592 | 过 1593 | 过于 1594 | 过去 1595 | 过来 1596 | 运用 1597 | 近 1598 | 近几年来 1599 | 近年来 1600 | 近来 1601 | 还 1602 | 还是 1603 | 还有 1604 | 还要 1605 | 这 1606 | 这一来 1607 | 这个 1608 | 这么 1609 | 这么些 1610 | 这么样 1611 | 这么点儿 1612 | 这些 1613 | 这会儿 1614 | 这儿 1615 | 这就是说 1616 | 这时 1617 | 这样 1618 | 这次 1619 | 这点 1620 | 这种 1621 | 这般 1622 | 这边 1623 | 这里 1624 | 这麽 1625 | 进入 1626 | 进去 1627 | 进来 1628 | 进步 1629 | 进而 1630 | 进行 1631 | 连 1632 | 连同 1633 | 连声 1634 | 连日 1635 | 连日来 1636 | 连袂 1637 | 连连 1638 | 迟早 1639 | 迫于 1640 | 适应 1641 | 适当 1642 | 适用 1643 | 逐步 1644 | 逐渐 1645 | 通常 1646 | 通过 1647 | 造成 1648 | 逢 1649 | 遇到 1650 | 遭到 1651 | 遵循 1652 | 遵照 1653 | 避免 1654 | 那 1655 | 那个 1656 | 那么 1657 | 那么些 1658 | 那么样 1659 | 那些 1660 | 那会儿 1661 | 那儿 1662 | 那时 1663 | 那末 1664 | 那样 1665 | 那般 1666 | 那边 1667 | 那里 1668 | 那麽 1669 | 部分 1670 | 都 1671 | 鄙人 1672 | 采取 1673 | 里面 1674 | 重大 1675 | 重新 1676 | 重要 1677 | 鉴于 1678 | 针对 1679 | 长期以来 1680 | 长此下去 1681 | 长线 1682 | 长话短说 1683 | 问题 1684 | 间或 1685 | 防止 1686 | 阿 1687 | 附近 1688 | 陈年 1689 | 限制 1690 | 陡然 1691 | 除 1692 | 除了 1693 | 除却 1694 | 除去 1695 | 除外 1696 | 除开 1697 | 除此 1698 | 除此之外 1699 | 除此以外 1700 | 除此而外 1701 | 除非 1702 | 随 1703 | 随后 1704 | 随时 1705 | 随着 1706 | 随著 1707 | 隔夜 1708 | 隔日 1709 | 难得 1710 | 难怪 1711 | 难说 1712 | 难道 1713 | 难道说 1714 | 集中 1715 | 零 1716 | 需要 1717 | 非但 1718 | 非常 1719 | 非徒 1720 | 非得 1721 | 非特 1722 | 非独 1723 | 靠 1724 | 顶多 1725 | 顷 1726 | 顷刻 1727 | 顷刻之间 1728 | 顷刻间 1729 | 顺 1730 | 顺着 1731 | 顿时 1732 | 颇 1733 | 风雨无阻 1734 | 饱 1735 | 首先 1736 | 马上 1737 | 高低 1738 | 高兴 1739 | 默然 1740 | 默默地 1741 | 齐 1742 | ︿ 1743 | ! 1744 | # 1745 | $ 1746 | % 1747 | & 1748 | ' 1749 | ( 1750 | ) 1751 | )÷(1- 1752 | )、 1753 | * 1754 | + 1755 | +ξ 1756 | ++ 1757 | , 1758 | ,也 1759 | - 1760 | -β 1761 | -- 1762 | -[*]- 1763 | . 1764 | / 1765 | 0 1766 | 0:2 1767 | 1 1768 | 1. 1769 | 12% 1770 | 2 1771 | 2.3% 1772 | 3 1773 | 4 1774 | 5 1775 | 5:0 1776 | 6 1777 | 7 1778 | 8 1779 | 9 1780 | : 1781 | ; 1782 | < 1783 | <± 1784 | <Δ 1785 | <λ 1786 | <φ 1787 | << 1788 | = 1789 | =″ 1790 | =☆ 1791 | =( 1792 | =- 1793 | =[ 1794 | ={ 1795 | > 1796 | >λ 1797 | ? 1798 | @ 1799 | A 1800 | LI 1801 | R.L. 1802 | ZXFITL 1803 | 1804 | [*] 1805 | [- 1806 | [] 1807 | ] 1808 | ]∧′=[ 1809 | ][ 1810 | _ 1811 | a] 1812 | b] 1813 | c] 1814 | e] 1815 | f] 1816 | ng昉 1817 | { 1818 | {- 1819 | | 1820 | } 1821 | }> 1822 | ~ 1823 | ~± 1824 | ~+ 1825 | ¥ 1826 | secondly 1827 | all 1828 | whose 1829 | under 1830 | sorry 1831 | four 1832 | we'll 1833 | somewhere 1834 | likely 1835 | even 1836 | above 1837 | ever 1838 | never 1839 | ZZ 1840 | hers 1841 | i'd 1842 | howbeit 1843 | i'm 1844 | theres 1845 | changes 1846 | anyhow 1847 | would 1848 | therefore 1849 | is 1850 | hereby 1851 | must 1852 | me 1853 | my 1854 | indicated 1855 | indicates 1856 | keep 1857 | far 1858 | after 1859 | hereupon 1860 | keeps 1861 | every 1862 | over 1863 | before 1864 | better 1865 | then 1866 | them 1867 | they 1868 | reasonably 1869 | each 1870 | went 1871 | mean 1872 | we'd 1873 | rd 1874 | re 1875 | got 1876 | forth 1877 | you're 1878 | little 1879 | whereupon 1880 | uses 1881 | already 1882 | another 1883 | took 1884 | second 1885 | seen 1886 | seem 1887 | relatively 1888 | thoroughly 1889 | latter 1890 | that 1891 | thorough 1892 | nobody 1893 | definitely 1894 | came 1895 | saying 1896 | specify 1897 | do 1898 | next 1899 | despite 1900 | unfortunately 1901 | twice 1902 | best 1903 | said 1904 | away 1905 | there's 1906 | unto 1907 | hopefully 1908 | seven 1909 | we 1910 | ltd 1911 | here 1912 | against 1913 | com 1914 | ZT 1915 | aren't 1916 | been 1917 | much 1918 | concerning 1919 | wish 1920 | say 1921 | near 1922 | unlikely 1923 | cant 1924 | in 1925 | ie 1926 | if 1927 | containing 1928 | beside 1929 | several 1930 | kept 1931 | whereby 1932 | whoever 1933 | the 1934 | yours 1935 | just 1936 | yes 1937 | yet 1938 | had 1939 | has 1940 | t's 1941 | possible 1942 | apart 1943 | right 1944 | old 1945 | somehow 1946 | for 1947 | everything 1948 | asking 1949 | who 1950 | of 1951 | theirs 1952 | plus 1953 | formerly 1954 | down 1955 | c's 1956 | accordingly 1957 | way 1958 | was 1959 | becoming 1960 | tell 1961 | sometime 1962 | no 1963 | whereas 1964 | nd 1965 | welcome 1966 | let's 1967 | certainly 1968 | a's 1969 | did 1970 | it'll 1971 | says 1972 | appear 1973 | alone 1974 | wherever 1975 | example 1976 | usually 1977 | nowhere 1978 | hither 1979 | regardless 1980 | everybody 1981 | thru 1982 | everywhere 1983 | can 1984 | following 1985 | want 1986 | didn't 1987 | may 1988 | such 1989 | whenever 1990 | maybe 1991 | ones 1992 | so 1993 | seeing 1994 | indeed 1995 | course 1996 | still 1997 | thank 1998 | he's 1999 | selves 2000 | ours 2001 | outside 2002 | non 2003 | within 2004 | thereby 2005 | not 2006 | now 2007 | nor 2008 | entirely 2009 | eg 2010 | ex 2011 | et 2012 | hadn't 2013 | furthermore 2014 | looking 2015 | seriously 2016 | shouldn't 2017 | she 2018 | quite 2019 | besides 2020 | think 2021 | first 2022 | ignored 2023 | awfully 2024 | given 2025 | anyone 2026 | indicate 2027 | gives 2028 | mostly 2029 | than 2030 | here's 2031 | were 2032 | and 2033 | appreciate 2034 | himself 2035 | saw 2036 | any 2037 | downwards 2038 | take 2039 | sure 2040 | especially 2041 | later 2042 | that's 2043 | fifth 2044 | don't 2045 | aside 2046 | only 2047 | going 2048 | get 2049 | truly 2050 | cannot 2051 | nearly 2052 | regarding 2053 | us 2054 | where 2055 | up 2056 | namely 2057 | anyways 2058 | wonder 2059 | behind 2060 | between 2061 | it 2062 | across 2063 | come 2064 | many 2065 | whereafter 2066 | according 2067 | comes 2068 | afterwards 2069 | couldn't 2070 | moreover 2071 | considering 2072 | sensible 2073 | hardly 2074 | wants 2075 | former 2076 | those 2077 | these 2078 | [ 2079 | somebody 2080 | different 2081 | etc 2082 | insofar 2083 | same 2084 | without 2085 | can't 2086 | very 2087 | you've 2088 | among 2089 | being 2090 | we've 2091 | seems 2092 | around 2093 | using 2094 | specified 2095 | on 2096 | ok 2097 | oh 2098 | whence 2099 | it's 2100 | or 2101 | everyone 2102 | your 2103 | her 2104 | there 2105 | amongst 2106 | trying 2107 | with 2108 | they're 2109 | wasn't 2110 | gone 2111 | certain 2112 | am 2113 | an 2114 | as 2115 | at 2116 | again 2117 | serious 2118 | hello 2119 | since 2120 | consider 2121 | causes 2122 | to 2123 | th 2124 | myself 2125 | i'll 2126 | zero 2127 | further 2128 | what 2129 | brief 2130 | seemed 2131 | c'mon 2132 | allows 2133 | followed 2134 | ask 2135 | viz 2136 | contains 2137 | two 2138 | taken 2139 | more 2140 | knows 2141 | ain't 2142 | particular 2143 | known 2144 | none 2145 | nine 2146 | needs 2147 | rather 2148 | [ 2149 | okay 2150 | tried 2151 | tries 2152 | onto 2153 | perhaps 2154 | specifying 2155 | ] 2156 | help 2157 | soon 2158 | through 2159 | its 2160 | seeming 2161 | inward 2162 | actually 2163 | might 2164 | haven't 2165 | someone 2166 | hereafter 2167 | always 2168 | isn't 2169 | beyond 2170 | really 2171 | they'll 2172 | enough 2173 | thereafter 2174 | done 2175 | together 2176 | least 2177 | too 2178 | immediate 2179 | believe 2180 | gotten 2181 | toward 2182 | self 2183 | also 2184 | towards 2185 | most 2186 | nothing 2187 | they'd 2188 | sometimes 2189 | lest 2190 | particularly 2191 | somewhat 2192 | his 2193 | goes 2194 | meanwhile 2195 | during 2196 | him 2197 | greetings 2198 | see 2199 | are 2200 | currently 2201 | please 2202 | various 2203 | probably 2204 | available 2205 | both 2206 | last 2207 | wouldn't 2208 | became 2209 | whole 2210 | liked 2211 | whatever 2212 | except 2213 | throughout 2214 | along 2215 | described 2216 | though 2217 | whom 2218 | beforehand 2219 | what's 2220 | new 2221 | else 2222 | look 2223 | while 2224 | herein 2225 | itself 2226 | wherein 2227 | used 2228 | anybody 2229 | obviously 2230 | thats 2231 | from 2232 | useful 2233 | merely 2234 | follows 2235 | often 2236 | some 2237 | ourselves 2238 | shall 2239 | per 2240 | tends 2241 | either 2242 | be 2243 | by 2244 | anything 2245 | consequently 2246 | into 2247 | appropriate 2248 | we're 2249 | elsewhere 2250 | hasn't 2251 | un 2252 | noone 2253 | associated 2254 | thanks 2255 | having 2256 | once 2257 | edu 2258 | go 2259 | sent 2260 | provides 2261 | yourselves 2262 | they've 2263 | try 2264 | this 2265 | you'd 2266 | yourself 2267 | zz 2268 | zt 2269 | respectively 2270 | let 2271 | others 2272 | until 2273 | weren't 2274 | use 2275 | few 2276 | themselves 2277 | becomes 2278 | anywhere 2279 | something 2280 | six 2281 | allow 2282 | won't 2283 | thence 2284 | willing 2285 | instead 2286 | whither 2287 | doing 2288 | how 2289 | cause 2290 | thereupon 2291 | que 2292 | via 2293 | could 2294 | hence 2295 | third 2296 | doesn't 2297 | their 2298 | exactly 2299 | regards 2300 | herself 2301 | have 2302 | need 2303 | clearly 2304 | i've 2305 | able 2306 | which 2307 | unless 2308 | where's 2309 | eight 2310 | why 2311 | you'll 2312 | normally 2313 | anyway 2314 | one 2315 | should 2316 | mainly 2317 | overall 2318 | qv 2319 | contain 2320 | looks 2321 | neither 2322 | however 2323 | otherwise 2324 | co 2325 | it'd 2326 | corresponding 2327 | thanx 2328 | novel 2329 | value 2330 | will 2331 | almost 2332 | thus 2333 | vs 2334 | when 2335 | gets 2336 | upon 2337 | off 2338 | nevertheless 2339 | well 2340 | less 2341 | presumably 2342 | ought 2343 | who's 2344 | five 2345 | know 2346 | you 2347 | name 2348 | necessary 2349 | like 2350 | become 2351 | therein 2352 | because 2353 | happens 2354 | does 2355 | although 2356 | about 2357 | getting 2358 | own 2359 | three 2360 | inasmuch 2361 | inner 2362 | but 2363 | hi 2364 | he 2365 | whether 2366 | placed 2367 | below 2368 | our 2369 | 上去-- 2370 | inc 2371 | lately 2372 | other 2373 | latterly 2374 | out 2375 | 是什么 2376 | 什么时候 2377 | 是什么意思 2378 | 什么意思 2379 | 多少钱 2380 | 有没有 2381 | 更有趣 2382 | 更有甚者 2383 | 更有效 2384 | 更有意义 2385 | 更远的 2386 | 更重要的是 2387 | 正确 2388 | 错误 2389 | 第二把 2390 | 第二波 2391 | 第二大节 2392 | 第二单元 2393 | 第二关 2394 | 第二行 2395 | 第二集 2396 | 第二讲 2397 | 第二款 2398 | 第二类 2399 | 第二盘 2400 | 第二任 2401 | 第二声 2402 | 第二十 2403 | 第二首 2404 | 第二项 2405 | 第三遍 2406 | 第三册 2407 | 第三层 2408 | 第三产业 2409 | 第三大 2410 | 第三单元 2411 | 第三行 2412 | 第三回 2413 | 第三集 2414 | 第三件 2415 | 第三句 2416 | 第三卷 2417 | 第三课 2418 | 第三类 2419 | 第三篇 2420 | 第三期 2421 | 第三日 2422 | 第三声 2423 | 地三鲜 2424 | 第三项 2425 | 第三站 2426 | 第三张 2427 | 第十八 2428 | 第十次 2429 | 第十二 2430 | 的士高 2431 | 第十集 2432 | 第十届 2433 | 第十九 2434 | 第十六 2435 | 第十名 2436 | 第十三 2437 | 第十四 2438 | 第十天 2439 | 第十一 2440 | 第十一个 2441 | 第四版 2442 | 第四册 2443 | 第四场 2444 | 第四代 2445 | 第四单元 2446 | 第四集 2447 | 第四届 2448 | 第四年 2449 | 第四期 2450 | 第四声 2451 | 第四套 2452 | 第四位 2453 | 第四张 2454 | 第四者 2455 | 第四种 2456 | 第五部 2457 | 第五大道 2458 | 第五单元 2459 | 第五集 2460 | 第五卷 2461 | 第五课 2462 | 第五年 2463 | 第五期 2464 | 第五位 2465 | 第五元素 2466 | 第五组 2467 | 召唤 2468 | 最后一班 2469 | 最后一遍 2470 | 最后一关 2471 | 最后一集 2472 | 最后一科 2473 | 最后一颗子弹 2474 | 最后一派 2475 | 最后一题 2476 | 最后一眼 2477 | 最后一页 2478 | 10 2479 | 11 2480 | 12 2481 | 35 2482 | 25 2483 | 2016 2484 | 2015 2485 | 2014 2486 | 又为什么 2487 | 有问题吗 2488 | 有问题么 2489 | 又喜欢 2490 | 有喜欢 2491 | 又小 2492 | 又笑 2493 | 有笑 2494 | 有效地 2495 | 有一百 2496 | 又一遍 2497 | 有一部 2498 | 又一城 2499 | 又一村 2500 | 有一道 2501 | 有意的 2502 | 有一堆 2503 | 有一对 2504 | 有一方 2505 | 有一根 2506 | 有一会了 2507 | 有一批 2508 | 有一片 2509 | 有一期 2510 | 有一起 2511 | 有一群 2512 | 又又 2513 | 由由 2514 | 财新网 2515 | 上午 2516 | 下午 2517 | NULL 2518 | 新华社 2519 | 消息 2520 | 13 2521 | 14 2522 | 15 2523 | 16 2524 | 17 2525 | 18 2526 | 19 2527 | 20 2528 | 21 2529 | 22 2530 | 23 2531 | 24 2532 | 26 2533 | 27 2534 | 28 2535 | 29 2536 | 30 2537 | 31 2538 | 32 2539 | 33 2540 | 34 2541 | 36 2542 | 37 2543 | 38 2544 | 39 2545 | 40 2546 | 41 2547 | 42 2548 | 43 2549 | 44 2550 | 45 2551 | 46 2552 | 47 2553 | 48 2554 | 49 2555 | 50 2556 | 51 2557 | 52 2558 | 53 2559 | 54 2560 | 55 2561 | 56 2562 | 57 2563 | 58 2564 | 59 2565 | 60 2566 | 61 2567 | 62 2568 | 63 2569 | 64 2570 | 65 2571 | 66 2572 | 67 2573 | 68 2574 | 69 2575 | 70 2576 | 71 2577 | 72 2578 | 73 2579 | 74 2580 | 75 2581 | 76 2582 | 77 2583 | 78 2584 | 79 2585 | 80 2586 | 81 2587 | 82 2588 | 83 2589 | 84 2590 | 85 2591 | 86 2592 | 87 2593 | 88 2594 | 89 2595 | 90 2596 | 91 2597 | 92 2598 | 93 2599 | 94 2600 | 95 2601 | 96 2602 | 97 2603 | 98 2604 | 99 2605 | 100 2606 | 01 2607 | 02 2608 | 03 2609 | 04 2610 | 05 2611 | 06 2612 | 07 2613 | 08 2614 | 09 2615 | -------------------------------------------------------------------------------- /文本分析挖掘/文本挖掘&情感分析.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 大众点评评价情感分析~\n", 8 | "先上结果:\n", 9 | "\n", 10 | "| 糖水店的评论文本 | 模型预测的情感评分 |\n", 11 | "| :------------------------------------------- | :----------------- |\n", 12 | "| '糖水味道不错,滑而不腻,赞一个,下次还会来' | 0.91 |\n", 13 | "| '味道一般,没啥特点' | 0.52 |\n", 14 | "| '排队老半天,环境很差,味道一般般' | 0.05 |\n", 15 | "\n", 16 | "模型的效果还可以的样子,yeah~接下来我们好好讲讲怎么做的哈,我们通过爬虫爬取了大众点评广州8家最热门糖水店的3W条评论信息以及评分作为训练数据,前面的分析我们得知*样本很不均衡*。接下来我们的整体思路就是:文本特征处理(分词、去停用词、TF-IDF)—机器学习建模—模型评价。\n", 17 | "\n", 18 | "我们先不处理样本不均衡问题,直接建模后查看结果,接下来我们再按照两种方法处理样本不均衡,对比结果。\n", 19 | "\n", 20 | "### 数据读入和探索" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 26, 26 | "metadata": { 27 | "scrolled": true 28 | }, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/html": [ 33 | "
\n", 34 | "\n", 47 | "\n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | "
cus_idcomment_timecomment_starcus_commentkouweihuanjingfuwushopIDstarsyearmonthweekdayhourcomment_lencus_comment_processed
0迷糊泰迪2018-09-20 06:48:00sml-str40南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ...非常好5189864.02018936184南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ...
1稱霸幼稚園2018-09-22 21:49:00sml-str40中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服...很好很好很好5189864.020189521266中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服...
2爱吃的美美侠2018-09-22 22:16:00sml-str40冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州...很好很好很好5189864.020189522341冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州...
3姜姜会吃胖2018-09-19 06:36:00sml-str40都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ...非常好很好很好5189864.02018926197都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ...
4forevercage2018-08-24 17:58:00sml-str50一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直...非常好很好很好5189865.020188417261一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直...
\n", 161 | "
" 162 | ], 163 | "text/plain": [ 164 | " cus_id comment_time comment_star \\\n", 165 | "0 迷糊泰迪 2018-09-20 06:48:00 sml-str40 \n", 166 | "1 稱霸幼稚園 2018-09-22 21:49:00 sml-str40 \n", 167 | "2 爱吃的美美侠 2018-09-22 22:16:00 sml-str40 \n", 168 | "3 姜姜会吃胖 2018-09-19 06:36:00 sml-str40 \n", 169 | "4 forevercage 2018-08-24 17:58:00 sml-str50 \n", 170 | "\n", 171 | " cus_comment kouwei huanjing fuwu \\\n", 172 | "0 南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ... 非常好 好 好 \n", 173 | "1 中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服... 很好 很好 很好 \n", 174 | "2 冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州... 很好 很好 很好 \n", 175 | "3 都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ... 非常好 很好 很好 \n", 176 | "4 一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直... 非常好 很好 很好 \n", 177 | "\n", 178 | " shopID stars year month weekday hour comment_len \\\n", 179 | "0 518986 4.0 2018 9 3 6 184 \n", 180 | "1 518986 4.0 2018 9 5 21 266 \n", 181 | "2 518986 4.0 2018 9 5 22 341 \n", 182 | "3 518986 4.0 2018 9 2 6 197 \n", 183 | "4 518986 5.0 2018 8 4 17 261 \n", 184 | "\n", 185 | " cus_comment_processed \n", 186 | "0 南信 算是 广州 著名 甜品店 吧 好几个 时间段 路过 都 是 座无虚席 看着 餐单 上 ... \n", 187 | "1 中午 吃 完 了 所谓 的 早茶 回去 放下 行李 休息 了 会 就 来 吃 下午茶 了 服... \n", 188 | "2 冲刺 王者 战队 吃遍 蓉城 战队 有 特权 五月份 和 好 朋友 毕业 旅行 来 了 广州... \n", 189 | "3 都 说来 广州 吃 糖水 就要 来南信 招牌 姜撞奶 红豆 双皮奶 牛 三星 云吞面 一楼 ... \n", 190 | "4 一直 很 期待 也 最 爱 吃 甜品 广州 的 甜品 很 丰富 很 多样 来 之前 就 一直... " 191 | ] 192 | }, 193 | "execution_count": 26, 194 | "metadata": {}, 195 | "output_type": "execute_result" 196 | } 197 | ], 198 | "source": [ 199 | "import pandas as pd\n", 200 | "from matplotlib import pyplot as plt\n", 201 | "import jieba\n", 202 | "data = pd.read_csv('data.csv')\n", 203 | "data.head()" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "### 构建标签值\n", 211 | "\n", 212 | "大众点评的评分分为1-5分,1-2为差评,4-5为好评,3为中评,因此我们把1-2记为0,4-5记为1,3为中评,对我们的情感分析作用不大,丢弃掉这部分数据,但是可以作为训练语料模型的语料。我们的情感评分可以转化为标签值为1的概率值,这样我们就把情感分析问题转为文本分类问题了。" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 27, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "#构建label值\n", 222 | "def zhuanhuan(score):\n", 223 | " if score > 3:\n", 224 | " return 1\n", 225 | " elif score < 3:\n", 226 | " return 0\n", 227 | " else:\n", 228 | " return None\n", 229 | " \n", 230 | "#特征值转换\n", 231 | "data['target'] = data['stars'].map(lambda x:zhuanhuan(x))\n", 232 | "data_model = data.dropna()" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": {}, 238 | "source": [ 239 | "### 文本特征处理\n", 240 | "\n", 241 | "中文文本特征处理,需要进行中文分词,jieba分词库简单好用。接下来需要过滤停用词,网上能够搜到现成的。最后就要进行文本转向量,有词库表示法、TF-IDF、word2vec等,这篇文章作了详细介绍,推荐一波 https://zhuanlan.zhihu.com/p/44917421\n", 242 | "\n", 243 | "这里我们使用sklearn库的TF-IDF工具进行文本特征提取。" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 48, 249 | "metadata": {}, 250 | "outputs": [ 251 | { 252 | "data": { 253 | "text/plain": [ 254 | "26328 冲着 老字号 去 的 但 真的 不 咋 地 所有 的 东西 都 不是 先 做 的 点 了 煎...\n", 255 | "3457 名过其实 味道 一般 全是 人 价格 也 贵 感觉 碗碟 都 洗 不 干净\n", 256 | "17834 香芋 西米 以前 系 北京路 个边 上班 落 左班 都 会同 同事 去 吃糖 上个星期 去 ...\n", 257 | "7904 南信 的 双皮奶 非常 滑 不是 很甜 可是 很嫩 非常 美味 还有 鲜虾 肠有 好多好多 ...\n", 258 | "3258 环境 真的 是 一般 可能 是 老店 的 关系 但 很 传统 双皮奶 奶味重 甜度 适中 粥...\n", 259 | "Name: cus_comment, dtype: object" 260 | ] 261 | }, 262 | "execution_count": 48, 263 | "metadata": {}, 264 | "output_type": "execute_result" 265 | } 266 | ], 267 | "source": [ 268 | "#切分测试集、训练集\n", 269 | "from sklearn.model_selection import train_test_split\n", 270 | "x_train, x_test, y_train, y_test = train_test_split(data_model['cus_comment'], data_model['target'], random_state=3, test_size=0.25)\n", 271 | "\n", 272 | "#引入停用词\n", 273 | "infile = open(\"stopwords.txt\",encoding='utf-8')\n", 274 | "stopwords_lst = infile.readlines()\n", 275 | "stopwords = [x.strip() for x in stopwords_lst]\n", 276 | "\n", 277 | "#中文分词\n", 278 | "def fenci(train_data):\n", 279 | " words_df = train_data.apply(lambda x:' '.join(jieba.cut(x)))\n", 280 | " return words_df\n", 281 | " \n", 282 | "x_train[:5]" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 49, 288 | "metadata": {}, 289 | "outputs": [ 290 | { 291 | "data": { 292 | "text/plain": [ 293 | "TfidfVectorizer(analyzer='word', binary=False, decode_error='strict',\n", 294 | " dtype=, encoding='utf-8', input='content',\n", 295 | " lowercase=True, max_df=1.0, max_features=3000, min_df=1,\n", 296 | " ngram_range=(1, 2), norm='l2', preprocessor=None, smooth_idf=True,\n", 297 | " stop_words=['!', '\"', '#', '$', '%', '&', \"'\", '(', ')', '*', '+', ',', '-', '--', '.', '..', '...', '......', '...................', './', '.一', '记者', '数', '年', '月', '日', '时', '分', '秒', '/', '//', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', '://', '::', ';', '<', '=', '>', '>>', '?', '@'...3', '94', '95', '96', '97', '98', '99', '100', '01', '02', '03', '04', '05', '06', '07', '08', '09'],\n", 298 | " strip_accents=None, sublinear_tf=False,\n", 299 | " token_pattern='(?u)\\\\b\\\\w\\\\w+\\\\b', tokenizer=None, use_idf=True,\n", 300 | " vocabulary=None)" 301 | ] 302 | }, 303 | "execution_count": 49, 304 | "metadata": {}, 305 | "output_type": "execute_result" 306 | } 307 | ], 308 | "source": [ 309 | "#使用TF-IDF进行文本转向量处理\n", 310 | "from sklearn.feature_extraction.text import TfidfVectorizer\n", 311 | "tv = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 312 | "tv.fit(x_train)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "markdown", 317 | "metadata": {}, 318 | "source": [ 319 | "### 机器学习建模\n", 320 | "\n", 321 | "特征和标签已经准备好了,接下来就是建模了。这里我们使用文本分类的经典算法朴素贝叶斯算法,而且朴素贝叶斯算法的计算量较少。特征值是评论文本经过TF-IDF处理的向量,标签值评论的分类共两类,好评是1,差评是0。情感评分为分类器预测分类1的概率值。" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": 50, 327 | "metadata": {}, 328 | "outputs": [ 329 | { 330 | "data": { 331 | "text/plain": [ 332 | "0.9275308869629356" 333 | ] 334 | }, 335 | "execution_count": 50, 336 | "metadata": {}, 337 | "output_type": "execute_result" 338 | } 339 | ], 340 | "source": [ 341 | "#计算分类效果的准确率\n", 342 | "from sklearn.naive_bayes import MultinomialNB\n", 343 | "from sklearn.metrics import roc_auc_score, f1_score\n", 344 | "classifier = MultinomialNB()\n", 345 | "classifier.fit(tv.transform(x_train), y_train)\n", 346 | "classifier.score(tv.transform(x_test), y_test)" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": 51, 352 | "metadata": { 353 | "scrolled": false 354 | }, 355 | "outputs": [ 356 | { 357 | "data": { 358 | "text/plain": [ 359 | "0.8971584233148908" 360 | ] 361 | }, 362 | "execution_count": 51, 363 | "metadata": {}, 364 | "output_type": "execute_result" 365 | } 366 | ], 367 | "source": [ 368 | "#计算分类器的AUC值\n", 369 | "y_pred = classifier.predict_proba(tv.transform(x_test))[:,1]\n", 370 | "roc_auc_score(y_test,y_pred)" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 52, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "#计算一条评论文本的情感评分\n", 380 | "def ceshi(model,strings):\n", 381 | " strings_fenci = fenci(pd.Series([strings]))\n", 382 | " return float(model.predict_proba(tv.transform(strings_fenci))[:,1])" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": 53, 388 | "metadata": {}, 389 | "outputs": [ 390 | { 391 | "name": "stdout", 392 | "output_type": "stream", 393 | "text": [ 394 | "好评实例的模型预测情感得分为0.8638082706675478\n", 395 | "差评实例的模型预测情感得分为0.7856544482460911\n" 396 | ] 397 | } 398 | ], 399 | "source": [ 400 | "#从大众点评网找两条评论来测试一下\n", 401 | "test1 = '很好吃,环境好,所有员工的态度都很好,上菜快,服务也很好,味道好吃,都是用蒸馏水煮的,推荐,超好吃' #5星好评\n", 402 | "test2 = '糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。' #1星差评\n", 403 | "print('好评实例的模型预测情感得分为{}\\n差评实例的模型预测情感得分为{}'.format(ceshi(classifier,test1),ceshi(classifier,test2)))" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "可以看出,准确率和AUC值都非常不错的样子,但点评网上的实际测试中,5星好评模型预测出来了,1星差评缺预测错误。为什么呢?我们查看一下**混淆矩阵**" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 54, 416 | "metadata": { 417 | "scrolled": true 418 | }, 419 | "outputs": [ 420 | { 421 | "data": { 422 | "text/plain": [ 423 | "array([[ 46, 385],\n", 424 | " [ 8, 4984]], dtype=int64)" 425 | ] 426 | }, 427 | "execution_count": 54, 428 | "metadata": {}, 429 | "output_type": "execute_result" 430 | } 431 | ], 432 | "source": [ 433 | "from sklearn.metrics import confusion_matrix\n", 434 | "y_predict = classifier.predict(tv.transform(x_test))\n", 435 | "cm = confusion_matrix(y_test, y_predict)\n", 436 | "cm" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": {}, 442 | "source": [ 443 | "可以看出,**负类的预测非常不准**,433单准确预测为负类的只有15.7%,应该是由于**数据不平衡**导致的,模型的默认阈值为输出值的中位数。比如逻辑回归的输出范围为[0,1],当某个样本的输出大于0.5就会被划分为正例,反之为反例。在数据的类别不平衡时,采用默认的分类阈值可能会导致输出全部为正例,产生虚假的高准确度,导致分类失败。\n", 444 | "\n", 445 | "处理样本不均衡问题的方法,首先可以选择调整阈值,使得模型对于较少的类别更为敏感,或者选择合适的评估标准,比如ROC或者F1,而不是准确度(accuracy)。另外一种方法就是通过采样(sampling)来调整数据的不平衡。其中欠采样抛弃了大部分正例数据,从而弱化了其影响,可能会造成偏差很大的模型,同时,数据总是宝贵的,抛弃数据是很奢侈的。另外一种是过采样,下面我们就使用过采样方法来调整。\n", 446 | "\n", 447 | "### 过采样(单纯复制)\n", 448 | "\n", 449 | "单纯的重复了反例,因此会过分强调已有的反例。如果其中部分点标记错误或者是噪音,那么错误也容易被成倍的放大。因此最大的风险就是对反例过拟合。" 450 | ] 451 | }, 452 | { 453 | "cell_type": "code", 454 | "execution_count": 55, 455 | "metadata": { 456 | "scrolled": true 457 | }, 458 | "outputs": [ 459 | { 460 | "data": { 461 | "text/plain": [ 462 | "1.0 19916\n", 463 | "0.0 1779\n", 464 | "Name: target, dtype: int64" 465 | ] 466 | }, 467 | "execution_count": 55, 468 | "metadata": {}, 469 | "output_type": "execute_result" 470 | } 471 | ], 472 | "source": [ 473 | "data['target'].value_counts()" 474 | ] 475 | }, 476 | { 477 | "cell_type": "code", 478 | "execution_count": 56, 479 | "metadata": {}, 480 | "outputs": [], 481 | "source": [ 482 | "#把0类样本复制10次,构造训练集\n", 483 | "index_tmp = y_train==0\n", 484 | "y_tmp = y_train[index_tmp]\n", 485 | "x_tmp = x_train[index_tmp]\n", 486 | "x_train2 = pd.concat([x_train,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp,x_tmp])\n", 487 | "y_train2 = pd.concat([y_train,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp,y_tmp])" 488 | ] 489 | }, 490 | { 491 | "cell_type": "code", 492 | "execution_count": 57, 493 | "metadata": {}, 494 | "outputs": [ 495 | { 496 | "data": { 497 | "text/plain": [ 498 | "0.9049699937533463" 499 | ] 500 | }, 501 | "execution_count": 57, 502 | "metadata": {}, 503 | "output_type": "execute_result" 504 | } 505 | ], 506 | "source": [ 507 | "#使用过采样样本(简单复制)进行模型训练,并查看准确率\n", 508 | "clf2 = MultinomialNB()\n", 509 | "clf2.fit(tv.transform(x_train2), y_train2)\n", 510 | "y_pred2 = clf2.predict_proba(tv.transform(x_test))[:,1]\n", 511 | "roc_auc_score(y_test,y_pred2)" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 58, 517 | "metadata": { 518 | "scrolled": true 519 | }, 520 | "outputs": [ 521 | { 522 | "data": { 523 | "text/plain": [ 524 | "array([[ 331, 100],\n", 525 | " [ 637, 4355]], dtype=int64)" 526 | ] 527 | }, 528 | "execution_count": 58, 529 | "metadata": {}, 530 | "output_type": "execute_result" 531 | } 532 | ], 533 | "source": [ 534 | "#查看此时的混淆矩阵\n", 535 | "y_predict2 = clf2.predict(tv.transform(x_test))\n", 536 | "cm = confusion_matrix(y_test, y_predict2)\n", 537 | "cm" 538 | ] 539 | }, 540 | { 541 | "cell_type": "markdown", 542 | "metadata": {}, 543 | "source": [ 544 | "可以看出,即使是简单粗暴的复制样本来处理样本不平衡问题,负样本的识别率大幅上升了,变为77%,满满的幸福感呀~我们自己写两句评语来看看" 545 | ] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": 59, 550 | "metadata": { 551 | "scrolled": false 552 | }, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/plain": [ 557 | "0.3520907656917545" 558 | ] 559 | }, 560 | "execution_count": 59, 561 | "metadata": {}, 562 | "output_type": "execute_result" 563 | } 564 | ], 565 | "source": [ 566 | "ceshi(clf2,'排队人太多,环境不好,口味一般')" 567 | ] 568 | }, 569 | { 570 | "cell_type": "markdown", 571 | "metadata": {}, 572 | "source": [ 573 | "可以看出把0类别的识别出来了,太棒了~" 574 | ] 575 | }, 576 | { 577 | "cell_type": "markdown", 578 | "metadata": {}, 579 | "source": [ 580 | "### 过采样(SMOTE算法)\n", 581 | "\n", 582 | "SMOTE(Synthetic minoritye over-sampling technique,SMOTE),是在局部区域通过K-近邻生成了新的反例。相较于简单的过采样,SMOTE降低了过拟合风险,但同时运算开销加大\n", 583 | "\n", 584 | "对SMOTE感兴趣的同学可以看下这篇文章https://www.jianshu.com/p/ecbc924860af" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": 60, 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "#使用SMOTE进行样本过采样处理\n", 594 | "from imblearn.over_sampling import SMOTE\n", 595 | "oversampler=SMOTE(random_state=0)\n", 596 | "x_train_vec = tv.transform(x_train)\n", 597 | "x_resampled, y_resampled = oversampler.fit_sample(x_train_vec, y_train)" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 61, 603 | "metadata": {}, 604 | "outputs": [ 605 | { 606 | "data": { 607 | "text/plain": [ 608 | "1.0 14920\n", 609 | "0.0 1348\n", 610 | "Name: target, dtype: int64" 611 | ] 612 | }, 613 | "execution_count": 61, 614 | "metadata": {}, 615 | "output_type": "execute_result" 616 | } 617 | ], 618 | "source": [ 619 | "#原始的样本分布\n", 620 | "y_train.value_counts()" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": 62, 626 | "metadata": { 627 | "scrolled": true 628 | }, 629 | "outputs": [ 630 | { 631 | "data": { 632 | "text/plain": [ 633 | "1.0 14920\n", 634 | "0.0 14920\n", 635 | "dtype: int64" 636 | ] 637 | }, 638 | "execution_count": 62, 639 | "metadata": {}, 640 | "output_type": "execute_result" 641 | } 642 | ], 643 | "source": [ 644 | "#经过SMOTE算法过采样后的样本分布情况\n", 645 | "pd.Series(y_resampled).value_counts()" 646 | ] 647 | }, 648 | { 649 | "cell_type": "markdown", 650 | "metadata": {}, 651 | "source": [ 652 | "我们经过插值,把0类数据也丰富为14923个数据了,这时候正负样本的比例为1:1,接下来我们用平衡后的数据进行训练,效果如何呢,好期待啊~" 653 | ] 654 | }, 655 | { 656 | "cell_type": "code", 657 | "execution_count": 63, 658 | "metadata": { 659 | "scrolled": true 660 | }, 661 | "outputs": [ 662 | { 663 | "data": { 664 | "text/plain": [ 665 | "0.9072990102028675" 666 | ] 667 | }, 668 | "execution_count": 63, 669 | "metadata": {}, 670 | "output_type": "execute_result" 671 | } 672 | ], 673 | "source": [ 674 | "#使用过采样样本(SMOTE)进行模型训练,并查看准确率\n", 675 | "clf3 = MultinomialNB()\n", 676 | "clf3.fit(x_resampled, y_resampled)\n", 677 | "y_pred3 = clf3.predict_proba(tv.transform(x_test))[:,1]\n", 678 | "roc_auc_score(y_test,y_pred3)" 679 | ] 680 | }, 681 | { 682 | "cell_type": "code", 683 | "execution_count": 64, 684 | "metadata": { 685 | "scrolled": false 686 | }, 687 | "outputs": [ 688 | { 689 | "data": { 690 | "text/plain": [ 691 | "array([[ 330, 101],\n", 692 | " [ 601, 4391]], dtype=int64)" 693 | ] 694 | }, 695 | "execution_count": 64, 696 | "metadata": {}, 697 | "output_type": "execute_result" 698 | } 699 | ], 700 | "source": [ 701 | "#查看此时的准确率\n", 702 | "y_predict3 = clf3.predict(tv.transform(x_test))\n", 703 | "cm = confusion_matrix(y_test, y_predict3)\n", 704 | "cm" 705 | ] 706 | }, 707 | { 708 | "cell_type": "code", 709 | "execution_count": 65, 710 | "metadata": { 711 | "scrolled": true 712 | }, 713 | "outputs": [ 714 | { 715 | "data": { 716 | "text/plain": [ 717 | "0.23491893934922978" 718 | ] 719 | }, 720 | "execution_count": 65, 721 | "metadata": {}, 722 | "output_type": "execute_result" 723 | } 724 | ], 725 | "source": [ 726 | "#到网上找一条差评来测试一下情感评分的预测效果\n", 727 | "test3 = '糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。'\n", 728 | "ceshi(clf3,test3)" 729 | ] 730 | }, 731 | { 732 | "cell_type": "markdown", 733 | "metadata": {}, 734 | "source": [ 735 | "可以看出,使用SMOTE插值与简单的数据复制比起来,AUC率略有提高,实际预测效果也挺好\n", 736 | "\n", 737 | "### 模型评估测试\n", 738 | "\n", 739 | "接下来我们把3W条数据都拿来训练,数据量变多了,模型效果应该会更好" 740 | ] 741 | }, 742 | { 743 | "cell_type": "code", 744 | "execution_count": 66, 745 | "metadata": { 746 | "scrolled": true 747 | }, 748 | "outputs": [], 749 | "source": [ 750 | "#词向量训练\n", 751 | "tv2 = TfidfVectorizer(stop_words=stopwords, max_features=3000, ngram_range=(1,2))\n", 752 | "tv2.fit(data_model['cus_comment'])\n", 753 | "\n", 754 | "#SMOTE插值\n", 755 | "X_tmp = tv2.transform(data_model['cus_comment'])\n", 756 | "y_tmp = data_model['target']\n", 757 | "sm = SMOTE(random_state=0)\n", 758 | "X,y = sm.fit_sample(X_tmp, y_tmp)\n", 759 | "\n", 760 | "clf = MultinomialNB()\n", 761 | "clf.fit(X, y)\n", 762 | "\n", 763 | "def fenxi(strings):\n", 764 | " strings_fenci = fenci(pd.Series([strings]))\n", 765 | " return float(clf.predict_proba(tv2.transform(strings_fenci))[:,1])" 766 | ] 767 | }, 768 | { 769 | "cell_type": "code", 770 | "execution_count": 67, 771 | "metadata": {}, 772 | "outputs": [ 773 | { 774 | "data": { 775 | "text/plain": [ 776 | "0.28900092243477077" 777 | ] 778 | }, 779 | "execution_count": 67, 780 | "metadata": {}, 781 | "output_type": "execute_result" 782 | } 783 | ], 784 | "source": [ 785 | "#到网上找一条差评来测试一下\n", 786 | "fenxi('糯米外皮不绵滑,豆沙馅粗躁,没有香甜味。12元一碗不值。')" 787 | ] 788 | }, 789 | { 790 | "cell_type": "markdown", 791 | "metadata": {}, 792 | "source": [ 793 | "只用到了简单的机器学习,就做出了不错的情感分析效果,知识的力量真是强大呀,666~\n", 794 | "### 后续优化方向\n", 795 | "\n", 796 | "- 使用更复杂的机器学习模型如神经网络、支持向量机等\n", 797 | "- 模型的调参\n", 798 | "- 行业词库的构建\n", 799 | "- 增加数据量\n", 800 | "- 优化情感分析的算法\n", 801 | "- 增加标签提取等\n", 802 | "- 项目部署到服务器上,更好地分享和测试模型的效果" 803 | ] 804 | } 805 | ], 806 | "metadata": { 807 | "kernelspec": { 808 | "display_name": "Python 3", 809 | "language": "python", 810 | "name": "python3" 811 | }, 812 | "language_info": { 813 | "codemirror_mode": { 814 | "name": "ipython", 815 | "version": 3 816 | }, 817 | "file_extension": ".py", 818 | "mimetype": "text/x-python", 819 | "name": "python", 820 | "nbconvert_exporter": "python", 821 | "pygments_lexer": "ipython3", 822 | "version": "3.6.5" 823 | } 824 | }, 825 | "nbformat": 4, 826 | "nbformat_minor": 2 827 | } 828 | --------------------------------------------------------------------------------