基于 Hive 的评论数据分析前处理流程实践

本文记录一次小测的完整实现过程,内容覆盖 B站评论获取、原始数据整理、Hive 端清洗、MySQL 落库以及结果导出。整体思路是搭建一条从数据抓取到分析落地的基础数据处理流程。([cnblogs.com][1])

说明:文中示例仅用于学习测试,实际采集时需要注意访问频率和平台规则。


1. 项目流程概述

这次实验的目标不是只把评论爬下来,而是把一整套数据链路走通。整体流程可以概括为:

B站评论接口 → Python采集脚本 → 本地原始文件 → 格式预处理 → HDFS → Hive清洗 → MySQL存储 → CSV导出分析。([cnblogs.com][1])

如果按执行顺序来看,大致可以分成下面几个步骤:

  1. 输入视频的 BV 号;
  2. 调用接口抓取评论数据;
  3. 将原始结果保存为 JSON 或 CSV;
  4. 再把数据转换成适合 Hive 导入的文本格式;
  5. 上传到 HDFS;
  6. 在 Hive 中建立原始表并完成清洗;
  7. 将清洗后的结果同步到 MySQL;
  8. 最后导出结果,便于后续可视化或统计分析。([cnblogs.com][1])

2. 实验环境准备

2.1 Python 部分

首先准备 Python 运行环境,并安装数据采集和处理会用到的依赖:

sudo yum install -y python3 python3-devel python3-pip
pip3 install requests pandas

2.2 Hadoop 与 Hive

启动 HDFS,并检查 Hive 元数据服务是否正常:

start-dfs.sh
hive --service metastore &
jps

2.3 MySQL 环境

MySQL 用来存放最终清洗结果,便于后续报表、接口调用或者简单查询分析。原文中还补充了 JDBC 驱动到 Hive 的 lib 目录,以支持 Hive/MySQL 交互。([cnblogs.com][1])

sudo yum install -y mysql-community-server
sudo systemctl start mysqld

cp mysql-connector-java-8.0.30.jar $HIVE_HOME/lib/

3. 评论采集实现

这一部分的核心任务是:根据输入的 BV号,先换取对应视频的 aid,再分页请求评论接口,把评论内容组织成结构化数据保存下来。原文示例中保存了 comment_id、user_id、username、用户等级、会员信息、评论内容、点赞数、回复数、时间戳和页码等字段。([cnblogs.com][1])

下面是整理后的版本:

import requests
import json
import time
import pandas as pd
from datetime import datetime

class BiliCommentSpider:
    def __init__(self):
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://www.bilibili.com"
        }

    def fetch_aid(self, bvid):
        api = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}"
        resp = requests.get(api, headers=self.headers, timeout=10)
        if resp.status_code == 200:
            data = resp.json()
            return data["data"]["aid"]
        return None

    def crawl_comments(self, bvid, max_pages=10):
        aid = self.fetch_aid(bvid)
        if not aid:
            return []

        result = []
        for pn in range(1, max_pages + 1):
            params = {
                "type": 1,
                "oid": aid,
                "pn": pn,
                "ps": 20,
                "sort": 1
            }
            resp = requests.get(
                "https://api.bilibili.com/x/v2/reply",
                headers=self.headers,
                params=params,
                timeout=10
            )

            data = resp.json()
            if data.get("code") != 0:
                break

            replies = data.get("data", {}).get("replies", [])
            if not replies:
                break

            for item in replies:
                result.append({
                    "comment_id": item["rpid"],
                    "user_id": item["member"]["mid"],
                    "username": item["member"]["uname"],
                    "user_level": item["member"]["level_info"]["current_level"],
                    "vip_type": item["member"]["vip"]["vipType"],
                    "vip_status": item["member"]["vip"]["vipStatus"],
                    "content": item["content"]["message"],
                    "like_count": item["like"],
                    "reply_count": item["count"],
                    "create_time": item["ctime"],
                    "page": pn
                })

            time.sleep(1)

        return result

    def save_file(self, comments, bvid):
        now = datetime.now().strftime("%Y%m%d_%H%M%S")
        csv_name = f"comment_{bvid}_{now}.csv"
        json_name = f"comment_{bvid}_{now}.json"

        pd.DataFrame(comments).to_csv(csv_name, index=False, encoding="utf-8-sig")
        with open(json_name, "w", encoding="utf-8") as f:
            json.dump(comments, f, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    spider = BiliCommentSpider()
    bv = input("请输入视频BV号:").strip()
    comments = spider.crawl_comments(bv)

    if comments:
        spider.save_file(comments, bv)
        print(f"采集完成,共获取 {len(comments)} 条评论")
    else:
        print("未获取到评论数据")

运行方式

python3 bilibili_crawler.py

4. 数据预处理

由于 Hive 更适合直接读取格式规整的文本数据,所以这里需要把 JSON 数据转换成 制表符分隔 的文本文件。原文这一部分的思路是:逐条读取 JSON 记录,然后把字段拼成一行,顺便替换掉制表符和换行符,避免 Hive 读入异常。([cnblogs.com][1])

可以写成下面这样:

import json
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]

with open(input_file, "r", encoding="utf-8") as f:
    records = json.load(f)

with open(output_file, "w", encoding="utf-8") as f:
    for row in records:
        line = "\t".join([
            str(row.get("comment_id", "")),
            str(row.get("user_id", 0)),
            str(row.get("username", "")).replace("\t", " ").replace("\n", " "),
            str(row.get("user_level", 0)),
            str(row.get("vip_type", 0)),
            str(row.get("vip_status", 0)),
            str(row.get("content", "")).replace("\t", " ").replace("\n", " "),
            str(row.get("like_count", 0)),
            str(row.get("reply_count", 0)),
            str(row.get("create_time", 0)),
            str(row.get("page", 0))
        ])
        f.write(line + "\n")

执行命令:

python3 preprocess_data.py data.json processed.txt

5. 上传到 HDFS 并在 Hive 中建表

完成文本转换后,就可以上传到 HDFS,之后通过 Hive 外部表进行映射。原文这里也是先建目录、上传文件,再创建数据库和原始表。([cnblogs.com][1])

5.1 上传数据

hdfs dfs -mkdir -p /user/hadoop/bilibili/input
hdfs dfs -put processed.txt /user/hadoop/bilibili/input

5.2 创建原始表

CREATE DATABASE IF NOT EXISTS bilibili_analysis;
USE bilibili_analysis;

CREATE EXTERNAL TABLE raw_comments (
    comment_id  STRING,
    user_id     BIGINT,
    username    STRING,
    user_level  INT,
    vip_type    INT,
    vip_status  INT,
    content     STRING,
    like_count  INT,
    reply_count INT,
    create_time BIGINT,
    page        INT
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LOCATION '/user/hadoop/bilibili/input';

6. Hive 数据清洗

清洗阶段的重点是处理空值、非法值以及脏数据。原文中采用了这些思路:

  • comment_id、user_id、username 等字段做空值补全;
  • 用户等级限定在合理区间;
  • 评论内容去除控制字符;
  • 点赞数、回复数出现负数时置为 0;
  • 时间戳异常时替换成当前时间;
  • 最后过滤掉空评论。([cnblogs.com][1])

SQL 可以整理成下面这样:

CREATE TABLE cleaned_comments AS
SELECT
    COALESCE(comment_id, 'unknown') AS comment_id,
    COALESCE(user_id, 0) AS user_id,
    COALESCE(username, '匿名用户') AS username,
    CASE
        WHEN user_level < 0 OR user_level > 6 THEN 0
        ELSE user_level
    END AS user_level,
    COALESCE(vip_type, 0) AS vip_type,
    COALESCE(vip_status, 0) AS vip_status,
    REGEXP_REPLACE(content, '[\\x00-\\x1F]', ' ') AS content,
    CASE
        WHEN like_count < 0 THEN 0
        ELSE like_count
    END AS like_count,
    CASE
        WHEN reply_count < 0 THEN 0
        ELSE reply_count
    END AS reply_count,
    CASE
        WHEN create_time > 0 AND create_time < UNIX_TIMESTAMP()
        THEN create_time
        ELSE UNIX_TIMESTAMP()
    END AS create_time,
    page
FROM raw_comments
WHERE content IS NOT NULL
  AND content != '';

7. 同步到 MySQL

原文这里给了两种入库方式:

  1. Sqoop 导出,更偏向标准化同步流程;
  2. Hive 直接写 MySQL,适合没有 Sqoop 的情况。([cnblogs.com][1])

7.1 先在 MySQL 创建目标表

CREATE DATABASE IF NOT EXISTS bili_db;
USE bili_db;

DROP TABLE IF EXISTS cleaned_comments;

CREATE TABLE cleaned_comments (
    comment_id   VARCHAR(50) PRIMARY KEY,
    user_id      BIGINT,
    username     VARCHAR(100),
    user_level   INT,
    vip_type     INT,
    vip_status   INT,
    content      TEXT,
    like_count   INT,
    reply_count  INT,
    create_time  BIGINT,
    page         INT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

7.2 使用 Sqoop 导出

sqoop export \
--connect jdbc:mysql://localhost:3306/bili_db \
--username root \
--password your-password \
--table cleaned_comments \
--export-dir /user/hive/warehouse/bilibili_analysis.db/cleaned_comments \
--input-fields-terminated-by '\001' \
--input-null-string '\\N' \
--input-null-non-string '\\N'

7.3 不用 Sqoop 时的做法

CREATE TABLE hive_to_mysql (
    comment_id  STRING,
    user_id     BIGINT,
    username    STRING,
    user_level  INT,
    vip_type    INT,
    vip_status  INT,
    content     STRING,
    like_count  INT,
    reply_count INT,
    create_time BIGINT,
    page        INT
)
STORED BY 'org.apache.hive.storage.jdbc.JdbcStorageHandler'
TBLPROPERTIES (
    "hive.jdbc.url" = "jdbc:mysql://localhost:3306/bili_db?useSSL=false&characterEncoding=utf8mb4",
    "hive.jdbc.driver" = "com.mysql.cj.jdbc.Driver",
    "hive.jdbc.user" = "root",
    "hive.jdbc.password" = "your-password",
    "hive.jdbc.write.batch.size" = "1000"
);

INSERT OVERWRITE TABLE hive_to_mysql
SELECT * FROM cleaned_comments;

8. 导出结果做本地分析

当清洗结果已经进入 MySQL 后,就可以继续导出为 CSV,供后续做统计图表、词频分析或者情感分析等。原文中使用的是 MySQL 的 INTO OUTFILE 导出方式。([cnblogs.com][1])

mysql -uroot -p -e "
USE bili_db;
SELECT * FROM cleaned_comments
INTO OUTFILE '/var/lib/mysql-files/bili_clean_result.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '\"'
LINES TERMINATED BY '\n';
"

9. 总结

通过这次小测,我把一条比较完整的数据处理链路跑通了:先从 B站抓取评论,再借助 Python 进行结构整理,接着上传到 HDFS,在 Hive 中完成基础清洗,最后同步到 MySQL 并导出结果。整个过程虽然不算复杂,但把采集、存储、清洗、入库、导出几个关键步骤串联起来后,更能体现大数据处理流程的完整性。原参考文章也是围绕这条主线展开,并给出了对应的代码和 SQL 实现。([cnblogs.com][1])

posted @ 2026-03-09 22:20  XYu1230  阅读(36)  评论(0)    收藏  举报