在电商数据分析场景中,评价的情感倾向分析是核心需求之一。本文将完整实现从苏宁商品评价爬取朴素贝叶斯情感分类的全流程,帮助大家掌握爬虫、文本预处理、机器学习分类的完整过程。

一、项目目标

  • 爬取苏宁商品的「优质评价」和「差评」数据
  • 对评价文本进行分词、停用词过滤等预处理
  • 基于朴素贝叶斯算法训练情感分类模型
  • 实现单条评价的情感倾向预测

二、环境的安装

selenium网页自动化 / 爬取(操控浏览器)

pip install selenium

pandas数据处理(表格型数据操作)

pip install pandas

jieba中文分词(将中文文本拆分为词语)

pip install jieba

scikit-learn (sklearn)机器学习(数据分割、特征提取、模型训练、评估)

pip install scikit-learn

三、Selenium爬取苏宁商品优质评价

目前爬虫有两种方法,一种是request方法,一种是selenium方法

3.1爬虫方法介绍

3.1.1、request方法

Request 方法是基于 HTTP/HTTPS 协议直接向目标服务器发送请求、获取响应数据的爬虫方式,核心依赖requests库。属于无浏览器渲染的 “轻量级” 爬虫

(1)核心原理

苏宁商品评价数据本质上存储在服务器中,前端页面展示评价时,会通过 AJAX(异步请求)从后端接口获取 JSON/HTML 格式的评价数据。Request 方法的核心是:

  1. 分析浏览器抓包(F12 开发者工具),找到返回评价数据的真实接口(通常是https://xxx.com/comment/xxx形式);
  2. 构造请求头(Headers,包含 User-Agent、Cookie、Referer 等)模拟浏览器请求;
  3. 向真实接口发送 GET/POST 请求,直接提取响应中的评价数据(如用户名、评价内容、评分、时间等)。

3.1.2、Selenium方法

Selenium 是一款浏览器自动化工具,核心是模拟人工操作浏览器(如打开页面、点击翻页、滚动加载),等待页面完全渲染后再提取评价数据,属于有浏览器渲染的 “重量级” 爬虫

(1)核心原理

苏宁部分商品评价采用 “懒加载”(滚动到底部才加载更多)、“JS 动态渲染”(评价内容通过 JS 拼接生成),无法通过简单接口获取。Selenium 方法的核心是:

  1. 启动真实浏览器(Chrome/Edge/Firefox),加载苏宁商品评价页面;
  2. 模拟人工操作:等待页面加载完成、点击 “查看更多评价”、滚动页面加载更多内容;
  3. 通过 XPath/CSS 选择器定位评价元素(如评价内容的<div>标签),提取文本数据。

接下来我们就分别使用Selenium方法爬取好评,然后用request方法爬取差评。

3.2 使用Selenium爬取苏宁商品优质评价数据

使用 Selenium 模拟浏览器操作,分别爬取优质评价,并保存为 txt 文件。

但是使用Selenium 模拟浏览器操作,需要我们下载msedgedriver.exe,因为浏览器本身是一个独立的应用程序(比如 Edge、Chrome),有严格的安全限制,不允许外部程序直接调用它的功能。而在我们下在浏览器驱动以后,我们就可以通过Python来访问浏览器了。

这里以edge浏览器为例,Chrome同理

3.2.1下载edge浏览器驱动

首先我们要查看浏览器的版本,驱动也需要下载对应版本

这里我们可以看到,我们的edge是146.0.3856.62最新版 (64 位),那在网页里面也下载最新版x64版本就可以了

edge浏览器驱动官网下载:
https://developer.microsoft.com/zh-cn/microsoft-edge/tools/webdriver/?form=MA13LH

下载自己电脑对应版本的驱动,我电脑是x64,之前看edge版本也可以看到电脑是x86还是x64。

解压以后我们需要msedgedriver.exe的路径,当然如果你想省事可以像我一样,直接把msedgedriver.exe,复制到Python文件当中,后面直接引用就行。

3.2.2找到自己的edge浏览器的路径

基本上edge路径都是

C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

但是如果有人不是,那就需要自己找到自己电脑上的edge路径

3.2.3、找到自己需要爬取网页的URL

这个就是我们需要爬取的URL,如果有需要,大家可以改成自己的,整个苏宁商品的评论代码逻辑是差不多的,如果你要爬京东或者其他网站,可以按照我的思路修改代码。

zhttps://review.suning.com/cluster_cmmdty_review/cluster-38249278-000000012389328846-0000000000-1-good.htm?originalCm

3.3Selenium爬取苏宁商品优质评价完整代码

from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
import time

__browser_url = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"# 定义Edge浏览器的安装路径
__driver_path = r"msedgedriver.exe"# 定义Edge驱动程序的路径(需确保驱动版本与浏览器匹配)

edge_options = Options()# 创建Edge浏览器的配置对象
edge_options.binary_location = __browser_url# 指定Edge浏览器的二进制文件路径

service = Service(executable_path=__driver_path)# 创建驱动服务对象,指定驱动程序的路径
driver = webdriver.Edge(service=service, options=edge_options)# 初始化Edge浏览器驱动,传入服务配置和浏览器选项

# 访问苏宁商品的优质评价页面(目标URL)
driver.get(
    'https://review.suning.com/cluster_cmmdty_review/cluster-38249278-000000012389328846-0000000000-1-good.htm?originalCm')

yzpj_file = open('优质评价1.txt', 'w', encoding='gbk')# 打开文件用于保存爬取的评价内容,编码为gbk避免中文乱码,模式为写入

total_comments = 0# 初始化总评价数计数器,用于统计爬取的评价总数

def get_py_content(file):  # 定义函数:获取当前页面的所有评价内容
    global total_comments  # 声明使用全局作用域的total_comments变量
    pj_elements_content = driver.find_elements(by=By.CLASS_NAME,    # 通过类名定位所有评价内容元素(body-content是评价内容的类名)
                                               value='body-content')
    current_page_count = len(pj_elements_content)    # 获取当前页面的评价数量
    total_comments += current_page_count    # 将当前页面评价数累加到总评价数

    for i in range(len(pj_elements_content)):    # 遍历所有评价元素,将每个评价的文本写入文件
        file.write(pj_elements_content[i].text + '\n')
    print(f"爬取评价数{total_comments}")

get_py_content(yzpj_file)# 调用函数,获取第一页的评论内容

# 通过XPath定位下一页按钮元素(class为next rv-maidian 的元素)
next_elements = driver.find_elements(by=By.XPATH, value='//*[@class="next rv-maidian "]')
print(next_elements)# 打印定位到的下一页元素列表(用于调试)

# 循环判断:如果存在下一页按钮,则继续爬取
while next_elements != []:
    # 取第一个下一页按钮元素
    next_element = next_elements[0]
    time.sleep(1)    # 等待1秒,避免页面加载过快导致操作失效
    next_element.click()    # 点击下一页按钮
    get_py_content(yzpj_file)    # 爬取新页面的评价内容
    # 重新定位下一页按钮,判断是否还有下一页
    next_elements = driver.find_elements(by=By.XPATH, value='//*[@class="next rv-maidian "]')

yzpj_file.close()# 关闭保存评价的文件
driver.quit()# 关闭浏览器驱动,释放资源

3.4代码详解

我们第一步就是导入相关库,然后定义edge浏览器路径和我们之前下载的msedgedriver.exe

创建一个 Edge 浏览器的配置对象 edge_options。然后指定 Edge 浏览器的路径。之后创建一个 EdgeDriver的服务对象 service。初始化并启动 Edge 浏览器驱动,返回一个 driver 对象。

访问苏宁商品的优质评价页面,将爬取到的评价数据保存到yzpj_file,这里是用‘gbk’编码,那么全程用‘gbk’编码,如果用‘utf-8’那全程用‘utf-8’。total_comments = 0初始化总评论数计数器,初始值为 0

定义一个名为 get_py_content 的函数用于提取当前页面的评价并写入文件。声明函数内部使用的 total_comments 是全局变量,如果不使用 global 关键字,函数会将其视为局部变量,无法实现跨页面累加统计。通过 Selenium 定位当前页面中所有 class 为 body-content 的元素,这些元素对应商品评价的正文内容。
我们打开f12开发者工具,然后点开鼠标选择元素,然后把鼠标放到我们需要评论上,就可以看到评价正文的固定类名body-content,然后我们就可以通过body-content来获取评论正文。
后面就是使用一个for循环遍历所有评价元素。然后调用函数,获取第一页的评论内容。

通过 XPath 语法 定位页面中 class 为 next rv-maidian 的元素(即 “下一页” 按钮)。后面我们使用while循环持续执行翻页操作,如果next_elements为空,则结束翻页。

3.5运行结果

这里17733条数据太多了,爬个2000条够用了

四、request爬取差评

我先给大家Selenium爬取苏宁商品优质评价完整代码,原理一样的。

Selenium爬取苏宁商品差评完整代码

from selenium import webdriver
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from selenium.webdriver.common.by import By
import time

__browser_url = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"  # edge浏览器的安装路径
__driver_path = r"msedgedriver.exe"

edge_options = Options()  # 配置对象
edge_options.binary_location = __browser_url

service = Service(executable_path=__driver_path)
driver = webdriver.Edge(service=service, options=edge_options)  # 驱动器

driver.get('https://review.suning.com/cluster_cmmdty_review/cluster-38249278-000000012389328846-0000000000-1-bad.htm?originalCmmdtyType=general&safp=d488778a.10004.loverRight.166')

yzpj_file = open('差评1.txt', 'w',encoding='gbk')

def get_py_content(file):  # 获取当前这个页面的所有评价内容
    pj_elements_content = driver.find_elements(by=By.CLASS_NAME, value='body-content')  # 寻找标签元素
    for i in range(len(pj_elements_content)):
        file.write(pj_elements_content[i].text + '\n')

get_py_content(yzpj_file)  # 获取第一页的评论内容


next_elements = driver.find_elements(by=By.XPATH, value='//*[@class="next rv-maidian "]')
print(next_elements)

while next_elements != []:  # 是否获取到下一页的标签
    next_element = next_elements[0]
    time.sleep(1)
    next_element.click()  # 点击标签
    get_py_content(yzpj_file)
    next_elements = driver.find_elements(by=By.XPATH, value='//*[@class="next rv-maidian "]')
yzpj_file.close()

那么接下来我来介绍另一种request爬虫方法

4.1获取页面信息

相比较于Selenium用Python驱动edge爬取评论,request就原始多了,而且爬有反爬机制的网站非常容易被禁,而且代码逻辑更加复杂。

首先我们打开需要爬取的网页的开发工具(f12)

https://review.suning.com/cluster_cmmdty_review/cluster-38249278-000000012389328846-0000000000-1-bad.htm?originalCm&

我们选择网络,然后找到第一列类型为document的数据点开,不同网站第一列名称不一样,找类型为document的数据就行。

这里我们至少需要复制两条请求头数据:

    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",

"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",

在这里提醒一下,Cookie不是必须的请求头,如果你不用爬不了再用,Cookie是动态的,非常容易过期,添加以后每一次运行都要保证更新,非常麻烦,能不用就不用。

4.2request爬取苏宁商品差评完整代码

import requests
import time

CLUSTER_ID = "38249278"  # cluster ID
COMMODITY_ID = "000000012389328846"  # 商品ID
FIXED_PARAM = "0000000000"  # URL中固定的参数段
REVIEW_TYPE = "bad"  # 评论类型:bad(差评)
FILE_NAME = "差评1.txt"  # 保存的文件名

# URL后缀参数(固定不变)
URL_SUFFIX = "?originalCmmdtyType=general&safp=d488778a.10004.loverRight.166&safpn=10009"

# 反爬配置
DELAY = 2  # 请求间隔(秒)
TIMEOUT = 15  # 请求超时时间
# ===================================================

# 完整请求头
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
}


def build_url(page_num):
    """构建指定页码的URL"""
    base_url = f"https://review.suning.com/cluster_cmmdty_review/cluster-{CLUSTER_ID}-{COMMODITY_ID}-{FIXED_PARAM}-{page_num}-{REVIEW_TYPE}.htm"
    full_url = base_url + URL_SUFFIX
    return full_url


def get_page_reviews(page_num):
    """获取指定页码的评论内容"""
    url = build_url(page_num)
    response = requests.get(url=url, headers=HEADERS, timeout=TIMEOUT,
                            allow_redirects=True, stream=False)
    response.encoding = "utf-8"
    html_content = response.text

    reviews = []
    blocks = html_content.split('class="body-content"')

    for block in blocks[1:]:
        start_idx = block.find(">") + 1
        end_idx = block.find("</div>", start_idx)
        review_text = block[start_idx:end_idx].strip()
        review_text = review_text.replace("\n", "").replace("\r", "").replace("\t", "")
        # 过滤空评论(只保留有实际内容的评论)
        if review_text and len(review_text) > 2:
            reviews.append(review_text)

    print(f"[第{page_num}页]爬取 {len(reviews)} 条评论")
    return reviews


def crawl_all_bad_reviews():
    all_reviews = []
    page_num = 1
    max_pages = 50
    empty_page_count = 0  # 连续空页计数
    max_empty_pages = 2  # 连续2页空就停止

    while page_num <= max_pages and empty_page_count < max_empty_pages:
        current_reviews = get_page_reviews(page_num)

        if current_reviews:
            all_reviews.extend(current_reviews)
            empty_page_count = 0
        else:  # 无有效评论
            empty_page_count += 1
            print(f"[第{page_num}页]无有效评论,连续空页数:{empty_page_count}")

        time.sleep(DELAY)
        page_num += 1

    with open(FILE_NAME, "w", encoding="gbk") as f:
        for idx, review in enumerate(all_reviews, 1):
            f.write(f"{review}\n")

    print(f"总计爬取 {len(all_reviews)} 条有效差评")
    print(f"评论已保存到:{FILE_NAME}")

crawl_all_bad_reviews()

4.3代码详解

request代码逻辑真的比Selenium复杂很多,翻页读取需要你自己去找规律,而且每个网站规律不一样,我之前写的爬的豆瓣短评、红楼梦全卷,他们代码翻页的逻辑都不同,都需要自己找规律。

https://blog.csdn.net/qq_74437448/article/details/157732998?spm=1001.2014.3001.5501爬取《葬送的芙莉莲》豆瓣短评:https://blog.csdn.net/qq_74437448/article/details/157732998?spm=1001.2014.3001.5501

爬取《红楼梦》全卷:

https://blog.csdn.net/qq_74437448/article/details/158705986?spm=1001.2014.3001.5501

先从请求头开始

这里我们可以看到,我们将URL拆解为

"https://review.suning.com/cluster_cmmdty_review/cluster-{CLUSTER_ID}-{COMMODITY_ID}-{FIXED_PARAM}-{page_num}-{REVIEW_TYPE}.htm"
原URL:
"https://review.suning.com/cluster_cmmdty_review/cluster-38249278-000000012389328846-0000000000-1-bad.htm?originalCm&"
注意到规律:

38249278就是CLUSTER_ID一个id

000000012389328846就是COMMODITY_ID商品id

0000000000就是FIXED_PARAM固定参数

-1-就是page_num第一页

bad.htm就是REVIEW_TYPE差评

然后把后面不变的参数单独提取:URL_SUFFIX = "?originalCmmdtyType=general&safp=d488778a.10004.loverRight.166&safpn=10009"

定义一个名为 build_url 的函数,接收参数 page_num(目标页码),用于生成对应页码的苏宁商品评价页面 URL。

定义一个名为get_page_reviews的函数。
url = build_url(page_num):调用之前定义的 build_url 函数,生成目标页码的完整 URL。
requests.get(...):发起 HTTP GET 请求,携带请求头、超时时间等配置:
headers=HEADERS:模拟浏览器请求头,避免被服务器识别为爬虫。
timeout=TIMEOUT:设置请求超时时间,防止程序长时间挂起。
allow_redirects=True:允许自动跟随重定向。
stream=False:不使用流式下载,直接获取完整响应。
response.encoding = "utf-8":指定响应内容编码为 UTF-8,避免中文乱码。
html_content = response.text:获取页面的 HTML 源码文本。

分割 HTML 定位评论块,reviews = []:初始化空列表,用于存储提取到的评论。
blocks = html_content.split('class="body-content"'):
以评论内容的 class 标识 class="body-content" 为分割点,将 HTML 拆分成多个片段。
分割后,blocks[0] 是页面头部内容,blocks[1:] 每个元素都对应一条评论的 HTML 片段。

这里和Selenium不同,我们除了知道评论正文存储在class=body-content里面,我们还需要找到评论内容开始标签。

我们先复制部分差评,然后搜索他的位置,双击搜索位置,可以看到评论正文就在

<div class="topic-body">
                                                        <p class="body-content">
                                                        退不了货的,别买!!!手机被拆开过的后封机,二手的,还在手机贴了一大张贴纸,很丑
                                                        </p>
                                                    <div class="body-images clearfix">

这一块我们用for循环,从 blocks[1:] 开始遍历,跳过页面头部片段。
start_idx = block.find(">") + 1:找到评论内容开始标签 > 的位置,+1 跳过标签本身。
end_idx = block.find("</div>", start_idx):从 start_idx 开始,找到结束标签 </div> 的位置。
block[start_idx:end_idx].strip():截取标签之间的文本并去除首尾空白。
调用 replace 方法,移除文本中的换行符 \n、回车符 \r、制表符 \t。

然后用if条件过滤无效评论
只保留非空且长度大于 2 的评论,避免空字符串或无意义短文本。
将符合条件的评论被添加到 reviews 列表中。

用request让程序停下也是老大难,因为我们从第一页开始for循环,我们打开的网页差评就两页,但是实际上后面网页也有,只不过么有评论,用request也会去读取,所以我们就要另外想办法让他读取完停下。

这里我们就使用了一个while循环,同时满足未超过最大页数和未超过连续空页数限制才继续爬取。我们这里只要读取了2页空数据,代码就会停下。

4.4运行结果

五、文本预处理:分词与停用词过滤

爬取到评价数据后,需要对文本进行分词(将连续文本拆分为独立词语)和停用词过滤(移除无意义的虚词),为后续机器学习建模做准备。

5.1完整代码

import pandas as pd

cp_content = pd.read_table(r"差评1.txt",header=None, names=['content'],encoding='gbk')
yzpj_content = pd.read_table(r"优质评价1.txt",header=None, names=['content'],encoding='gbk')

import jieba

cp_segments = []
contents = cp_content.content.values.tolist()  # 将content列数据取出并转化为list格式,目的是分别 jieba.lcut分词
for content in contents:
    results = jieba.lcut(content)
    if len(results) > 1:  # 当分词之后,这条评论如果只有1个内容,
        cp_segments.append(results)  # 将分词后的内容添加到列表segments中

cp_fc_results = pd.DataFrame({'content': cp_segments})
cp_fc_results.to_excel('cp_fc_results.xlsx', index=False)

yzpj_segments = []
contents = yzpj_content.content.values.tolist()  # 将content列数据取出并转化为list格式
for content in contents:
    results = jieba.lcut(content)
    if len(results) > 1:
        yzpj_segments.append(results)  # 将分词后的内容添加到列表segments中

# ## 分词结果储存在新的数据框中
yzpj_fc_results = pd.DataFrame({'content': yzpj_segments})
yzpj_fc_results.to_excel('yzpj_fc_results.xlsx', index=False)

stopwords = pd.read_csv(r".\StopwordsCN.txt", encoding='utf8', engine='python', index_col=False)

# 定义去除停用词函数
def drop_stopwords(contents, stopwords):
    segments_clean = []
    for content in contents:
        line_clean = []
        for word in content:
            if word in stopwords:
                continue
            line_clean.append(word)
        segments_clean.append(line_clean)
    return segments_clean

# 调用去除停用词函数
contents = cp_fc_results.content.values.tolist()  # DataFrame格式转为List格式
stopwords = stopwords.stopword.values.tolist()     # 停用词转为List格式
cp_fc_contents_clean_s = drop_stopwords(contents, stopwords)

contents = yzpj_fc_results.content.values.tolist()  # DataFrame格式转为List格式
yzpj_fc_contents_clean_s = drop_stopwords(contents, stopwords)

5.2代码解析

导入我们的优质评价和差评,这里如果导出的是“gbk”那就都用“gbk”,用“utf-80”就都用“utf-80”,然后让所有列都命名为content

定义空列表 cp_segments,用于存储差评的评论结果,每条评论对应一个词语列表。cp_content 是之前读取的 DataFrame,content 列存储原始评论文本。
.values.tolist():将 DataFrame 的 content 列转换为Python 列表,便于逐行遍历分词。

然后使用for循环遍历每一条原始评论,jieba.lcut(content):对单条评论执行精确模式分词,返回词语列表(results)。
if len(results) > 1:过滤掉分词后只有 1 个词语的评论(如 “差”“好” 这类无分析价值的单字短评)。
cp_segments.append(results):将符合条件的分词结果存入列表。

将分词后的结果 cp_segments 封装为 pandas.DataFrame
将 DataFrame 导出为 Excel 文件 cp_fc_results.xlsx。

定义空列表 yzpj_segments,用于存储优质评价的分词结果,和上面的差评类似就不多介绍了。

pd.DataFrame({'content': yzpj_segments}):将分词结果封装为 DataFrame,列名为 content。
.to_excel('yzpj_fc_results.xlsx', index=False):导出为 Excel 文件。

读取中文停用词表StopwordsCN.txt(存储在当前目录下),用于后续过滤无意义虚词(如 “的”“了”“很” 等)。

这一块资源我也上传了有需要可以下载。

定义函数对分词后的文本进行停用词过滤,移除无实际语义的虚词(如 “的”“了”“很” 等),保留核心情感词汇。

segments_clean = [] 用于存储过滤后的干净文本结果。

for content in contents:遍历每一条已经分词的评论。

for word in content:遍历当前评论中的每一个词语。

contents = ...:将差评分词结果 cp_fc_results(DataFrame 格式)的 content 列提取出来,转为 Python 列表。
stopwords = ...:将停用词表 stopwords(DataFrame 格式)的 stopword 列提取出来,转为 Python 列表。
cp_fc_contents_clean_s = ...:调用 drop_stopwords 函数,传入差评分词列表和停用词列表,得到过滤后的差评数据。
同理yzpj_fc_contents_clean_s = ...:复用已转换好的 stopwords 列表,调用 drop_stopwords 函数,得到过滤后的优质评价数据。

5.3运行结果

六、建立朴素贝叶斯商品评价情感分析模型

经过文本预处理后,我们得到了干净的分词结果。接下来通过朴素贝叶斯算法构建情感分类模型,实现对商品评价的 “差评 / 优质评价” 自动分类,核心步骤包括:数据标签化、数据平衡(过采样)、特征向量化、模型训练与评估。

6.1原代码

import pandas as pd
import jieba
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics

# 1. 数据读取与处理
cp_content = pd.read_table(r"差评1.txt", header=None, names=['content'], encoding='gbk')
yzpj_content = pd.read_table(r"优质评价1.txt", header=None, names=['content'], encoding='gbk')

# 过滤空行
cp_content = cp_content[cp_content['content'].notna() & (cp_content['content'].str.strip() != '')]
yzpj_content = yzpj_content[yzpj_content['content'].notna() & (yzpj_content['content'].str.strip() != '')]

# 2. jieba分词
cp_segments = []
contents = cp_content.content.values.tolist()
for content in contents:
    if isinstance(content, str):
        results = jieba.lcut(content)
        if len(results) > 1:
            cp_segments.append(results)

# 2.2 优质评价分词
yzpj_segments = []
contents = yzpj_content.content.values.tolist()
for content in contents:
    if isinstance(content, str):
        results = jieba.lcut(content)
        if len(results) > 1:
            yzpj_segments.append(results)

# 保存分词结果
cp_fc_results = pd.DataFrame({'content': cp_segments})
cp_fc_results.to_excel('cp_fc_results.xlsx', index=False)
yzpj_fc_results = pd.DataFrame({'content': yzpj_segments})
yzpj_fc_results.to_excel('yzpj_fc_results.xlsx', index=False)

# 3. 移除停用词
stopwords = pd.read_csv(r".\StopwordsCN.txt", encoding='utf8', engine='python', index_col=False)
stopwords_set = set(stopwords.stopword.values.tolist())

# 定义去停用词函数
def drop_stopwords(contents, stopwords):
    segments_clean = []
    for content in contents:
        line_clean = []
        for word in content:
            if word.strip() and word not in stopwords:
                line_clean.append(word)
        if line_clean:
            segments_clean.append(line_clean)
    return segments_clean

# 调用函数去停用词
contents = cp_fc_results.content.values.tolist()
cp_fc_contents_clean_s = drop_stopwords(contents, stopwords_set)
contents = yzpj_fc_results.content.values.tolist()
yzpj_fc_contents_clean_s = drop_stopwords(contents, stopwords_set)

# 4. 朴素贝叶斯分类
# 4.1 添加标签(1=差评,0=优质评价)
cp_train = pd.DataFrame({'segments_clean': cp_fc_contents_clean_s, 'label': 1})
yzpj_train = pd.DataFrame({'segments_clean': yzpj_fc_contents_clean_s, 'label': 0})
pj_train = pd.concat([cp_train, yzpj_train])
pj_train.to_excel('pj_train.xlsx', index=False)

#过采样
# 确定多数类(优质评价)样本数量
majority_count = len(pj_train[pj_train['label'] == 0])
# 对少数类(差评)进行随机过采样,数量匹配多数类
minority_upsampled = pj_train[pj_train['label'] == 1].sample(n=majority_count, replace=True, random_state=0)
# 合并过采样后的少数类和原始多数类,得到平衡数据集
pj_train_balanced = pd.concat([minority_upsampled, pj_train[pj_train['label'] == 0]])
# 打乱数据顺序(可选,提升训练效果)
pj_train_balanced = pj_train_balanced.sample(frac=1, random_state=0).reset_index(drop=True)

# 4.2 数据切分(使用平衡后的数据集)
x_train, x_test, y_train, y_test = train_test_split(
    pj_train_balanced['segments_clean'].values,  # 平衡数据集
    pj_train_balanced['label'].values,            # 平衡数据集
    random_state=0
)

# 转换为词向量可识别格式
words = []
for line_index in range(len(x_train)):
    words.append(' '.join(x_train[line_index]))

# 词向量转换
vec = CountVectorizer(max_features=4000, lowercase=False, ngram_range=(1,3))
vec.fit(words)
x_train_vec = vec.transform(words)

# 4.3 朴素贝叶斯分类训练
classifier = MultinomialNB(alpha=0.1)
classifier.fit(x_train_vec, y_train)

# 训练集预测与评估
train_pr = classifier.predict(x_train_vec)
print("\n训练集分类报告")
print(metrics.classification_report(y_train, train_pr))

# 测试集预测与评估
test_words = []
for line_index in range(len(x_test)):
    test_words.append(' '.join(x_test[line_index]))
test_pr = classifier.predict(vec.transform(test_words))
print("\n测试集分类报告")
print(metrics.classification_report(y_test, test_pr))

6.2代码详解

将过滤后的差评分词结果 cp_fc_contents_clean_s 封装为 DataFrame,并添加情感标签 label=1。
然后将优质评价添加情感标签 label=0

使用 pd.concat() 纵向合并差评 DataFrame 和优质评价 DataFrame,得到完整的训练数据集 pj_train。之后出为 Excel 文件。

做模型一定不要忘记了数据不匹配的这个大问题。
这里采用了过采样法,好评2000多条,差评12条,用下采样,那好评也变成12条,会导致准确率也不高。

这里就是数据切分为训练集测试集。

这里就是词向量转换,将分词后的列表格式转换为机器学习模型可识别的数值型词向量。

使用for循环遍历每条评论的分词列表,用 ' '.join() 将词语拼接为空格分隔的字符串。

CountVectorizer:统计每个词在文本中出现的次数。
vec.fit(words):统计所有出现的词语 / 短语,确定特征维度;
vec.transform(words):将训练集文本转换为稀疏矩阵格式的词向量,每行对应一条评论,每列对应一个词汇,值为该词汇在当前评论中出现的次数;

使用朴素贝叶斯来进行训练,最后得到训练集和测试集的准确率。

6.3代码结果

七、实现单条评价的情感倾向判断(好评 / 差评)

训练完成的朴素贝叶斯模型最终要落地为可交互的工具,最终实现 “输入任意电商评价文本,自动判断是好评还是差评” 的功能。

7.1原代码

def predict_single_sentence(sentence):
    seg_list = jieba.lcut(sentence)
    seg_clean = [word for word in seg_list if word.strip() and word not in stopwords_set]
    seg_str = ' '.join(seg_clean)
    vec_sentence = vec.transform([seg_str])
    pred_label = classifier.predict(vec_sentence)[0]
    result = "差评" if pred_label == 1 else "好评"
    return result

# 交互预测逻辑
if __name__ == "__main__":
    user_input = input("\n请输入要预测的评价语句:")
    pred_result = predict_single_sentence(user_input)
    print(f"\n预测结果:{user_input}  {pred_result}")

7.2代码详解

这里定义predict_single_sentence函数,接收单条原始评论文本 sentence,使用 jieba.lcut() 进行精确分词,得到词语列表 seg_list。

然后通过列表推导式过滤无意义词语,将过滤后的词语列表拼接为空格分隔的字符串,适配 CountVectorizer 的输入格式。

使用之前模型拟合好的vec,将预处理后的文本转换为词向量矩阵。

然后调用训练好的朴素贝叶斯分类器 classifier,对词向量进行预测,得到标签 pred_label。标签为1是差评,0为好评。

7.3结果展示

这里可以看出来,不管是好评还是差评,模型都有效的识别出来了,说明这个模型的准确率还是非常高的。

以上就是这个项目的完整内容了,希望能对你有所帮助。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐