淘汽车投诉信息爬取与数据分析挖掘 课程设计

一、选题的背景

随着我国经济水平的不断发展,越来越多的人都会购买小汽车,方便自己出行。本文通过爬取汽车消费网上的汽车相关投诉数据,并根据爬取到数据做文本分析和可视化展示,主要是通过文本分析探索目前汽车存在的普遍问题,为消费者在购车时提供一个参考价值,同时也让汽车商家认识到自身产品的问题并及时改正。如此,该项目无论是对于消费者还是商家来说,都是非常有实际应用意义的。

二、主题式网络爬虫的设计方案

本文的主题式网络爬虫与数据分析命名汽车投诉信息爬取与数据分析挖掘”其中主要爬取的汽车消费网(http://tousu.315che.com/tousulist/serial/93/)的按品牌的汽车投诉数据,包括品牌车型、单号、投诉的问题、投诉时间和经销商等信息。爬虫所涉及到的python包有parselrepandasseleniumparselre都是解释并提取网页数据,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. 发动机、变速箱、漏油、轮胎、机油等是常见的汽车投诉问题,说明在这些方面汽车生产商需要特别关注,应努力提高汽车质量。

 

 

 

 

 

 

 

 

 

posted @ 2021-12-30 18:41  小辞学java  阅读(493)  评论(0编辑  收藏  举报