淘汽车投诉信息爬取与数据分析挖掘 课程设计
一、选题的背景
随着我国经济水平的不断发展,越来越多的人都会购买小汽车,方便自己出行。本文通过爬取汽车消费网上的汽车相关投诉数据,并根据爬取到数据做文本分析和可视化展示,主要是通过文本分析探索目前汽车存在的普遍问题,为消费者在购车时提供一个参考价值,同时也让汽车商家认识到自身产品的问题并及时改正。如此,该项目无论是对于消费者还是商家来说,都是非常有实际应用意义的。
二、主题式网络爬虫的设计方案
本文的主题式网络爬虫与数据分析命名为“淘汽车投诉信息爬取与数据分析挖掘”,其中主要爬取的汽车消费网(http://tousu.315che.com/tousulist/serial/93/)的按品牌的汽车投诉数据,包括品牌车型、单号、投诉的问题、投诉时间和经销商等信息。爬虫所涉及到的python包有parsel、re、pandas和selenium,parsel和re都是解释并提取网页数据,selenium负责点击网页并获取网页源代码,pandas用于保存数据。汽车投诉信息爬虫过程设计如下:
三、数据页面的结构特征分析
打开汽车消费网(http://tousu.315che.com/tousulist/serial/93/)的投诉信息,可以看到如下界面:
右键检查,得到如下的网页层级结构:
很明显,需要爬取的品牌车型就在某个div下的a元素的href属性内,我们可以在网页中右键复制xpath表达式直接解析该数据并提取。
该方法便可以爬取到每个品牌车型的投诉数据详细网址,以备后续使用。
接着进入某个品牌车型的投诉数据详情页,继续按照相同的方法探索网页结构。经点击探索发现,只要获取到每页的品牌车型的页面数据,就可以得到每个投诉数据的详细网址,进而可以爬取到该数据。
部分数据爬取代码如下图:
四、网络爬虫程序设计
1. 数据爬取与采集
数据采集使用python函数的方法爬取,完整代码如下:
1 # 导入所需包 2 import parsel 3 from selenium import webdriver 4 from selenium.webdriver.support.ui import WebDriverWait 5 import re 6 import pandas as pd 7 8 9 url = 'http://tousu.315che.com/tousulist/serial/55467/' # 目标网站网址 10 # 不加载图片 11 options = webdriver.ChromeOptions() 12 options.add_experimental_option('prefs', {'profile.managed_default_content_settings.images': 2}) 13 driver = webdriver.Chrome(options=options) 14 wait = WebDriverWait(driver, 50) # driver请求数据等待时间 15 data_list = [] 16 17 # 获取每个品牌车型下的所有投诉网址 18 def get_page_url(url): 19 driver.get(url) 20 html = driver.page_source 21 selector = parsel.Selector(html) 22 letterTabList = selector.xpath('//div[@id="letterTabList"]//div[@class="row clearfix tousu-brand-list"]').getall() 23 for letter in letterTabList: 24 car_url = re.findall('<a href="(.*?)">.*?</a>', letter) 25 for page_url in car_url: 26 driver.get(page_url) 27 source = driver.page_source 28 try: 29 selector = parsel.Selector(source) 30 pages = selector.xpath('//div[@class="pagination pdtb20"]/span[@class="pag-tip"]/text()').get() 31 pages = int(pages[1:-1]) 32 url_n = selector.xpath('//div[@class="pagination pdtb20"]/a[@class="page"]/@href').get() 33 base_url = url_n.split('2.htm')[0] 34 for page in range(1,pages+1): 35 page_detail_url = base_url + str(page) + '.htm' 36 driver.get(page_detail_url) 37 source = driver.page_source 38 get_detail_url(source) 39 except: 40 get_detail_url(source) 41 42 # 获取汽车投诉网址 43 def get_detail_url(html): 44 selector = parsel.Selector(html) 45 try: 46 detail_urls = selector.xpath('//ul[@class="clearfix"]/li/a/@href').getall() 47 if detail_urls != []: 48 for detail_url in detail_urls: 49 driver.get(detail_url) 50 source = driver.page_source 51 get_data(source) 52 except Exception as e: 53 print(e) 54 55 # 提取数据 56 def get_data(html): 57 data = {} 58 selector = parsel.Selector(html) 59 # 投诉详细信息 60 content = selector.xpath('//div[@class="describe"]/p/text()').get().replace('\n', '').strip() 61 info = selector.xpath('//div[@class="complaints-appeal-info"]/p/text()').getall() 62 car = info[0].split(':')[1] # 品牌车型 63 number = info[1].split(':')[1] # 单号 64 problem = info[2].split(':')[1] # 诉求问题 65 start_time = info[3].split(':')[1] # 投诉时间 66 saller = info[4].split(':')[1] # 经销商 67 data['品牌车型'] = car 68 data['单号'] = number 69 data['诉求问题'] = problem 70 data['投诉时间'] = start_time 71 data['经销商'] = saller 72 data['content'] = content 73 data_list.append(data) 74 print(data) 75 76 # 保存数据 77 def write_data(data_list): 78 data = pd.DataFrame(data_list) 79 data.to_csv('汽车投诉信息.csv', mode='a', index=False, encoding='utf-8-sig') 80 81 82 def main(): 83 get_page_url(url) 84 write_data(data_list) 85 driver.quit() 86 87 88 if __name__ == '__main__': 89 main()
运行结果:
2.对数据进行清洗与处理
2.1 数据读取与探索
1 import pandas as pd 2 import numpy as np 3 import matplotlib.pyplot as plt 4 from sklearn.linear_model import LinearRegression 5 # matplotlib基本配置 6 plt.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体为仿宋 7 plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题 8 df = pd.read_csv('汽车投诉信息.csv', error_bad_lines=False) 9 df.head()
运行结果:
1 df.info()
运行结果:
从图中可以发现,源数据是存在较多缺失值的,需要清洗数据。
2.2 数据清洗
1 df.dropna(inplace=True) 2 df.reset_index(drop=True, inplace=True) 3 df.info()
运行结果:
2.3 数据处理
把时间该为年-月-日。
1 df['投诉时间'] = df['投诉时间'].apply(lambda x:str(x).split()[0]) 2 df.head()
运行结果:
3.数据分析与可视化
3.1 品牌车型投诉前十条形图
1 car = df['品牌车型'].value_counts() 2 plt.figure(figsize=(15, 8)) 3 plt.bar(car[:10].index, car[:10].values, facecolor='green', edgecolor='black') 4 # 显示横轴标签 5 plt.xlabel('品牌车型', fontsize=18) 6 # 显示纵轴标签 7 plt.ylabel('%timeit诉数量', fontsize=18) 8 # 显示图标题 9 plt.xticks(fontsize=15) 10 plt.yticks(fontsize=15) 11 for i, j in zip(car.index[:10], car.values[:10]): 12 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 13 plt.title('品牌牌车型投诉前十条形图', fontsize=25) 14 plt.savefig('品牌牌车型投诉前十条形图.png') 15 plt.show()
运行结果:
由图可知,受投诉最多的品牌车型为本田CR-V,高达1622条,其他受投诉比较多的车型有科鲁兹、蒙迪欧和福克斯等品牌车型。
3.2 汽车投诉时间前十条形图
1 times = df['投诉时间'].value_counts() 2 plt.figure(figsize=(15, 8)) 3 plt.bar(times [:10].index, times [:10].values, facecolor='green', edgecolor='black') 4 # 显示横轴标签 5 plt.xlabel('投诉时间', fontsize=18) 6 # 显示纵轴标签 7 plt.ylabel('投诉数量', fontsize=18) 8 # 显示图标题 9 plt.xticks(fontsize=15) 10 plt.yticks(fontsize=15) 11 for i, j in zip(times.index[:10], times.values[:10]): 12 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 13 plt.title('汽车投诉时间条形图', fontsize=25) 14 plt.savefig('汽车投诉时间条形图.png') 15 plt.show()
由图可知,汽车被投诉的时间集中在2018年,说明汽车被消费者投诉后并不能及时解决,有历史遗留问题。
3.3 汽车投诉经销商前十条形图
1 franchiser = df['经销商'].value_counts() 2 plt.figure(figsize=(20, 8)) 3 plt.bar(franchiser[:10].index, franchiser[:10].values, facecolor='green', edgecolor='black') 4 # 显示横轴标签 5 plt.xlabel('经销商', fontsize=18) 6 # 显示纵轴标签 7 plt.ylabel('投诉数量', fontsize=18) 8 # 显示图标题 9 plt.xticks(fontsize=15) 10 plt.yticks(fontsize=15) 11 for i, j in zip(franchiser.index[:10], franchiser.values[:10]): 12 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 13 plt.title('汽车投诉经销商前十条形图', fontsize=25) 14 plt.savefig('汽车投诉经销商前十条形图.png') 15 plt.show()
从图中可以发现,大部分被投诉汽车没有经消商(或者未知),分布在北京和上海的经销商较多。
4.汽车投诉详情文本分析
4.1 汽车投诉信息词云
分词:
1 cut_content = df['详细诉求内容'].apply(jieba.lcut) 2 cut_content
词云图:
1 cut_list = [] 2 for cl in cut_content: 3 for w in cl: 4 cut_list.append(w) 5 cut_tuple = [] 6 cut_sum = Counter(cut_list) 7 for key, value in cut_sum.items(): 8 cut_tuple.append((key, value)) 9 cut_tuple 10 def wordcloud_base() -> WordCloud: 11 c = ( 12 WordCloud() 13 .add("", cut_tuple, word_size_range=[20, 100]) 14 .set_global_opts(title_opts=opts.TitleOpts(title='投诉诉求词云图')) 15 ) 16 return c 17 18 wd = wordcloud_base() 19 wd.render_notebook()
运行结果:
分析词云图可以发现,发动机、变速箱、漏油、轮胎、机油等是常见的汽车投诉问题,说明在这些方面汽车生产商需要特别关注,应努力提高汽车质量。
4. 2 汽车详细诉求信息与品牌车型逻辑回归模型
通过文本词向量,构建汽车详细诉求信息与品牌车型逻辑回归模型,从诉求问题中识别出品牌车型,便于商家和消费者发现汽车品牌的被投诉问题的关键因素。
4.2.1 文本词向量
1 cut_word_list = np.array([''.join(cont).split(',') for cont in cut_content.tolist()]) 2 dictionary = corpora.Dictionary(cut_word_list) 3 corpus = [dictionary.doc2bow(text) for text in cut_word_list] 4 # word2vec训练词向量 5 def word2vec_model(): 6 model = Word2Vec(cut_word_list, vector_size=200, window=5, min_count=1, seed=1, workers=4) 7 model.save('word2vec.model') 8 word2vec_model() 9 # 加载模型得出词向量 10 model = Word2Vec.load('word2vec.model') 11 model.train(cut_word_list, total_examples=model.corpus_count, epochs=10) 12 wv = model.wv # 所有分词对应词向量 13 # word2vec构建文档向量 14 def get_word2vec_vec(content=None): 15 text_vec = np.zeros((content.shape[0], 200)) 16 for ind, text in enumerate(content): 17 wlen = len(text) 18 vec = np.zeros((1, 200)) 19 for w in text: 20 try: 21 vec += wv[w] 22 except: 23 pass 24 text_vec[ind] = vec/wlen 25 word2vec = pd.DataFrame(data=text_vec) 26 word2vec.to_csv('word2vec.csv', index=False) 27 return text_vec 28 word2vec = get_word2vec_vec(cut_word_list) 29 word2vec
运行结果:
文本特征散点图:
1 # PCA降维 2 pca = PCA(n_components=2) 3 pca_vec = pca.fit_transform(word2vec) 4 plt.figure(figsize=(15, 8)) 5 plt.scatter(pca_vec[:, 0], pca_vec[:, 1],facecolor='yellow', edgecolor='black') 6 # 显示横轴标签 7 plt.xlabel('特征1', fontsize=18) 8 # 显示纵轴标签 9 plt.ylabel('特征2', fontsize=18) 10 # 显示图标题 11 plt.xticks(fontsize=12) 12 plt.yticks(fontsize=15) 13 plt.title('汽车投诉文本特征散点图', fontsize=25) 14 plt.savefig('汽车投诉文本特征散点图.png') 15 plt.show()
运行结果:
两个文本特征关系不明显,说明不存在特征多重共线性,可以用于构建逻辑回归模型。
4.2.2 逻辑回归模型构建
目标数值化:
1 df['标签'] = LabelEncoder().fit_transform(df['品牌车型']) 2 df['标签']
运行结果:
模型构建与训练:
1 logit = LogisticRegression() 2 logit.fit(pca_vec, df['标签']) 3 logit.coef_ 4 logit.score(pca_vec, df['标签'])
运行结果:
以上便得出了该文本逻辑回归模型的系数,可用于后续深入分析。
5.附上完整代码:
1 # 导入所需包 2 3 import parsel 4 from selenium import webdriver 5 from selenium.webdriver.support.ui import WebDriverWait 6 import re 7 import pandas as pd 8 9 10 url = 'http://tousu.315che.com/tousulist/serial/55467/' 11 # 目标网站网址 12 13 # 不加载图片 14 15 options = webdriver.ChromeOptions() 16 options.add_experimental_option('prefs', {'profile.managed_default_content_settings.images': 2}) 17 driver = webdriver.Chrome(options=options) 18 wait = WebDriverWait(driver, 50) 19 # driver请求数据等待时间 20 data_list = [] 21 22 # 获取每个品牌车型下的所有投诉网址 23 24 def get_page_url(url): 25 driver.get(url) 26 html = driver.page_source 27 selector = parsel.Selector(html) 28 letterTabList = selector.xpath('//div[@id="letterTabList"]//div[@class="row clearfix tousu-brand-list"]').getall() 29 for letter in letterTabList: 30 car_url = re.findall('<a href="(.*?)">.*?</a>', letter) 31 for page_url in car_url: 32 driver.get(page_url) 33 source = driver.page_source 34 try: 35 selector = parsel.Selector(source) 36 pages = selector.xpath('//div[@class="pagination pdtb20"]/span[@class="pag-tip"]/text()').get() 37 pages = int(pages[1:-1]) 38 url_n = selector.xpath('//div[@class="pagination pdtb20"]/a[@class="page"]/@href').get() 39 base_url = url_n.split('2.htm')[0] 40 for page in range(1,pages+1): 41 page_detail_url = base_url + str(page) + '.htm' 42 driver.get(page_detail_url) 43 source = driver.page_source 44 get_detail_url(source) 45 except: 46 get_detail_url(source) 47 48 # 获取汽车投诉网址 49 50 def get_detail_url(html): 51 selector = parsel.Selector(html) 52 try: 53 detail_urls = selector.xpath('//ul[@class="clearfix"]/li/a/@href').getall() 54 if detail_urls != []: 55 for detail_url in detail_urls: 56 driver.get(detail_url) 57 source = driver.page_source 58 get_data(source) 59 except Exception as e: 60 print(e) 61 62 # 提取数据 63 64 def get_data(html): 65 data = {} 66 selector = parsel.Selector(html) 67 # 投诉详细信息 68 69 content = selector.xpath('//div[@class="describe"]/p/text()').get().replace('\n', '').strip() 70 info = selector.xpath('//div[@class="complaints-appeal-info"]/p/text()').getall() 71 car = info[0].split(':')[1] 72 # 品牌车型 73 number = info[1].split(':')[1] 74 # 单号 75 problem = info[2].split(':')[1] 76 # 诉求问题 77 start_time = info[3].split(':')[1] 78 # 投诉时间 79 saller = info[4].split(':')[1] 80 # 经销商 81 data['品牌车型'] = car 82 data['单号'] = number 83 data['诉求问题'] = problem 84 data['投诉时间'] = start_time 85 data['经销商'] = saller 86 data['content'] = content 87 data_list.append(data) 88 print(data) 89 90 # 保存数据 91 92 def write_data(data_list): 93 data = pd.DataFrame(data_list) 94 data.to_csv('汽车投诉信息.csv', mode='a', index=False, encoding='utf-8-sig') 95 96 97 def main(): 98 get_page_url(url) 99 write_data(data_list) 100 driver.quit() 101 102 103 if __name__ == '__main__': 104 main() 105 106 import pandas as pd 107 import numpy as np 108 import matplotlib.pyplot as plt 109 from sklearn.linear_model import LinearRegression 110 111 # matplotlib基本配置 112 plt.rcParams['font.sans-serif'] = ['FangSong'] 113 # 指定默认字体为仿宋 114 plt.rcParams['axes.unicode_minus'] = False 115 # 解决保存图像是负号'-'显示为方块的问题 116 df = pd.read_csv('汽车投诉信息.csv', error_bad_lines=False) 117 df.head() 118 119 df.info() 120 121 df.dropna(inplace=True) 122 df.reset_index(drop=True, inplace=True) 123 df.info() 124 125 df['投诉时间'] = df['投诉时间'].apply(lambda x:str(x).split()[0]) 126 df.head() 127 128 car = df['品牌车型'].value_counts() 129 plt.figure(figsize=(15, 8)) 130 plt.bar(car[:10].index, car[:10].values, facecolor='green', edgecolor='black') 131 132 # 显示横轴标签 133 134 plt.xlabel('品牌车型', fontsize=18) 135 136 # 显示纵轴标签 137 138 plt.ylabel('%timeit诉数量', fontsize=18) 139 140 # 显示图标题 141 142 plt.xticks(fontsize=15) 143 plt.yticks(fontsize=15) 144 for i, j in zip(car.index[:10], car.values[:10]): 145 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 146 plt.title('品牌牌车型投诉前十条形图', fontsize=25) 147 plt.savefig('品牌牌车型投诉前十条形图.png') 148 plt.show() 149 150 times = df['投诉时间'].value_counts() 151 plt.figure(figsize=(15, 8)) 152 plt.bar(times [:10].index, times [:10].values, facecolor='green', edgecolor='black') 153 154 # 显示横轴标签 155 156 plt.xlabel('投诉时间', fontsize=18) 157 158 # 显示纵轴标签 159 160 plt.ylabel('投诉数量', fontsize=18) 161 162 # 显示图标题 163 164 plt.xticks(fontsize=15) 165 plt.yticks(fontsize=15) 166 for i, j in zip(times.index[:10], times.values[:10]): 167 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 168 plt.title('汽车投诉时间条形图', fontsize=25) 169 plt.savefig('汽车投诉时间条形图.png') 170 plt.show() 171 172 franchiser = df['经销商'].value_counts() 173 plt.figure(figsize=(20, 8)) 174 plt.bar(franchiser[:10].index, franchiser[:10].values, facecolor='green', edgecolor='black') 175 # 显示横轴标签 176 177 plt.xlabel('经销商', fontsize=18) 178 179 # 显示纵轴标签 180 181 plt.ylabel('投诉数量', fontsize=18) 182 183 # 显示图标题 184 185 plt.xticks(fontsize=15) 186 plt.yticks(fontsize=15) 187 for i, j in zip(franchiser.index[:10], franchiser.values[:10]): 188 plt.text(i, j, j, va='bottom', ha='center', fontsize=15, color='red') 189 plt.title('汽车投诉经销商前十条形图', fontsize=25) 190 plt.savefig('汽车投诉经销商前十条形图.png') 191 plt.show() 192 193 cut_content = df['详细诉求内容'].apply(jieba.lcut) 194 cut_content 195 196 cut_list = [] 197 for cl in cut_content: 198 for w in cl: 199 cut_list.append(w) 200 cut_tuple = [] 201 cut_sum = Counter(cut_list) 202 for key, value in cut_sum.items(): 203 cut_tuple.append((key, value)) 204 cut_tuple 205 def wordcloud_base() -> WordCloud: 206 c = ( 207 WordCloud() 208 .add("", cut_tuple, word_size_range=[20, 100]) 209 .set_global_opts(title_opts=opts.TitleOpts(title='投诉诉求词云图')) 210 ) 211 return c 212 213 wd = wordcloud_base() 214 wd.render_notebook() 215 216 cut_word_list = np.array([''.join(cont).split(',') for cont in cut_content.tolist()]) 217 dictionary = corpora.Dictionary(cut_word_list) 218 corpus = [dictionary.doc2bow(text) for text in cut_word_list] 219 # word2vec训练词向量 220 def word2vec_model(): 221 model = Word2Vec(cut_word_list, vector_size=200, window=5, min_count=1, seed=1, workers=4) 222 model.save('word2vec.model') 223 word2vec_model() 224 # 加载模型得出词向量 225 226 model = Word2Vec.load('word2vec.model') 227 model.train(cut_word_list, total_examples=model.corpus_count, epochs=10) 228 wv = model.wv # 所有分词对应词向量 229 230 # word2vec构建文档向量 231 232 def get_word2vec_vec(content=None): 233 text_vec = np.zeros((content.shape[0], 200)) 234 for ind, text in enumerate(content): 235 wlen = len(text) 236 vec = np.zeros((1, 200)) 237 for w in text: 238 try: 239 vec += wv[w] 240 except: 241 pass 242 text_vec[ind] = vec/wlen 243 word2vec = pd.DataFrame(data=text_vec) 244 word2vec.to_csv('word2vec.csv', index=False) 245 return text_vec 246 word2vec = get_word2vec_vec(cut_word_list) 247 word2vec 248 249 # PCA降维 250 251 pca = PCA(n_components=2) 252 pca_vec = pca.fit_transform(word2vec) 253 plt.figure(figsize=(15, 8)) 254 plt.scatter(pca_vec[:, 0], pca_vec[:, 1],facecolor='yellow', edgecolor='black') 255 # 显示横轴标签 256 257 plt.xlabel('特征1', fontsize=18) 258 259 # 显示纵轴标签 260 261 plt.ylabel('特征2', fontsize=18) 262 263 # 显示图标题 264 265 plt.xticks(fontsize=12) 266 plt.yticks(fontsize=15) 267 plt.title('汽车投诉文本特征散点图', fontsize=25) 268 plt.savefig('汽车投诉文本特征散点图.png') 269 plt.show() 270 271 df['标签'] = LabelEncoder().fit_transform(df['品牌车型']) 272 df['标签'] 273 274 logit = LogisticRegression() 275 logit.fit(pca_vec, df['标签']) 276 logit.coef_ 277 logit.score(pca_vec, df['标签'])
五、结论
1. 汽车消费网汽车投诉数据用常规方法爬取只能得到少数数据,使用selenium可以爬取更多数据。
2. 受投诉最多的品牌车型为本田CR-V,高达1622条,其他受投诉比较多的车型有科鲁兹、蒙迪欧和福克斯等品牌车型。
3. 汽车被投诉的时间集中在2018年,说明汽车被消费者投诉后并不能及时解决,有历史遗留问题比较突出。
4. 大部分被投诉汽车没有经消商(或者未知),分布在北京和上海的经销商较多。
5. 发动机、变速箱、漏油、轮胎、机油等是常见的汽车投诉问题,说明在这些方面汽车生产商需要特别关注,应努力提高汽车质量。