首页 > 编程语言 >Python爬虫:获取某站视频评论+数据可视化 手把手教程

Python爬虫:获取某站视频评论+数据可视化 手把手教程

时间:2024-11-25 16:32:27浏览次数:10  
标签:comment plt format Python 手把手 某站 comments 评论 data

目录

前言

一、获取数据

1.1 使用 requests 库发送 HTTP 请求

1.2 获取'User-Agent','cookie'和视频oid

二、处理数据

2.1 某站响应的json数据格式 

2.2 封装函数process_comment :选择指定字段 

2.3 封装函数fetch_comments :发送 HTTP 请求并保存字段为xlsx文件

2.4 cookie更新

三、运行结果展示

四、爬虫完整代码

五、可视化代码和运行结果展示

5.1 可视化代码

5.2 运行结果展示


前言

        笔者是一名研究牲,因课程作业而接触爬虫,本文旨在帮助大家理解某站的机制和使用爬虫的方法,速速上手实操。

        笔者本来是爬wb的,但wb一篇文章只能加载15页左右的评论,用于翻页的返回值'max_id'会变成0,然后从第一页重新爬取,获取二级评论或者针对同一作者爬取全部文章收益也不大,wb反爬机制也比较健全,俺就来爬某站嘞~

一、获取数据

1.1 使用 requests 库发送 HTTP 请求

'''
page:请求第几页的评论
video_oid:视频的oid,用于唯一标识视频
'''
url = f'https://api.bilibili.com/x/v2/reply/main?next={page}&type=1&oid={video_oid}&mode=3'

headers = {
    'User-Agent': '',    #输入你的用户代理字符串
    'cookie':''          #输入你的cookie字符串
}

response = requests.get(url=url, headers=headers, timeout=10)
if response.status_code == 200:    #请求成功时,解析响应的JSON数据
    data = response.json()

1.2 获取'User-Agent','cookie'和视频oid

第一步:打开某站

第二步:按F12键,或者网页右键选择“检查”,然后选择“网络”,在过滤框中输入main,选中<main?oid=...>的这一项,没有就刷新页面

第三步:

选择“标头”下拉,在“请求标头”中有'User-Agent','cookie'字段

         

选择“载荷”,可以看到视频的oid

二、处理数据

2.1 某站响应的json数据格式 

一次申请会返回20条一级评论数据,以及每条一级评论下的数条二级评论数据,内容很多,这里仅展示部分字段:

{'code': 0,
 'data': {...
          #评论部分
          'replies': [ #第一条一级评论
                       {...
                       'content': {'jump_url': {},
                                   'max_line': 6,
                                   'members': [],
                                   'message': '咦? 白鼠是谁来着。。?好像是那个几百年前做鬼畜的。。?'},
                       'count': 190,
                       'ctime': 1475305239,
                       ...
                       'like': 98872,
                       'member': {...
                                  'level_info': {'current_exp': 0,
                                                 'current_level': 6,
                                                 'current_min': 0,
                                                 'next_exp': 0},
                                  ...
                                  'sex': '保密',
                                  'sign': '版本更新了',
                                  'uname': 'HakosW',
                                  ...}
                       'mid': 3150316,
                       'mid_str': '3150316',
                       'note_cvid_str': '0',
                       'oid': 6482189,
                       'oid_str': '6482189',
                       ...
                       #第一条一级评论下的二级评论,如果没有,值为none
                       'replies':[{...},
                                  {...},
                                  {...}]
                       ...},
                       #第二条一级评论
                       {...},
                       ...]
        }
}

可以使用 pprint 来格式化打印 JSON 数据,寻找自己想要爬取的字段

import pprint
# 使用pprint打印JSON数据的结构
pprint.pprint(json_data)

2.2 封装函数process_comment :选择指定字段 

因为一级评论和二级评论结构相同,为减少冗余代码,将其封装成函数process_comment

def process_comment(comment, is_reply=False, parent_username=''):
    """处理评论信息并返回字典"""
    return {
        '用户UID': str(comment['mid']),  # 确保 UID 是字符串格式
        '用户昵称': comment['member']['uname'],
        '用户性别': comment['member']['sex'],
        '粉丝等级': comment['member']['fans_detail']['level'] if comment['member']['fans_detail'] else 0,
        '被回复用户': parent_username,  # 被回复的一级评论用户(如果是二级评论)
        '评论内容': comment['content']['message'],
        '评论层级': '二级评论' if is_reply else '一级评论',
        '用户当前等级': comment['member']['level_info']['current_level'],
        '点赞数量': comment['like'],
        '回复时间': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(comment['ctime']))
    }

函数参数说明

  • comment: 包含评论信息的字典,来自API的响应。
  • is_reply: 布尔值,指示该评论是否为回复评论(即二级评论)。默认为 False
  • parent_username: 被回复的用户的用户名(如果是二级评论时使用)。默认为空字符串。

2.3 封装函数fetch_comments :发送 HTTP 请求并保存字段为xlsx文件

本来笔者是保存为csv文件的,不过用excel打开要手动调整部分列的格式才能看清楚,所以就使用Pandas 库的ExcelWriter来保存文件,并在代码中设置了部分列的宽度。

def fetch_comments(video_oid, max_pages=10000):  # 最大页面数量可调整
    comments = []
    count = 0
    for page in range(1, max_pages + 1):
        url = f'https://api.bilibili.com/x/v2/reply/main?next={page}&type=1&oid={video_oid}&mode=3'
        try:
            response = requests.get(url=url, headers=headers, timeout=10)
            if response.status_code == 200:
                data = response.json()
                # 使用pprint打印JSON数据的结构
                #pprint.pprint(data)
                print(f"正在读取第{page}页")
                if data['data']['replies'] is None:
                    break
                if data and 'replies' in data['data']:
                    for comment in data['data']['replies']:
                        # 处理一级评论
                        comments.append(process_comment(comment))

                        # 检查是否有二级评论
                        if 'replies' in comment and comment['replies']:
                            for reply in comment['replies']:
                                # 处理二级评论
                                comments.append(process_comment(reply, is_reply=True, parent_username=comment['member']['uname']))
                #如果本次爬取后,总评论数与上次相同,则认为已经爬取到最后一页,所有评论已全部获取,结束循环
                if count == len(comments):
                    break
                count = len(comments)
            else:
                break
        except requests.RequestException as e:
            print(f"请求出错: {e}")
            break
        time.sleep(0.5) #每两次爬取的间隔时间

    # 将评论信息保存为DataFrame
    df = pd.DataFrame(comments)

    # 使用 ExcelWriter 和 xlsxwriter 设置格式
    with pd.ExcelWriter(f'comments_{file_name}.xlsx', engine='xlsxwriter') as writer:
        df.to_excel(writer, index=False, sheet_name='Comments')
        workbook = writer.book
        worksheet = writer.sheets['Comments']

        # 设置 UID 列格式为文本,以防止科学计数法
        uid_format = workbook.add_format({'num_format': '@'})  # 文本格式
        worksheet.set_column('A:A', 18, uid_format)  # 设置 UID 列宽和格式
        worksheet.set_column('B:B', 20, uid_format)  # 设置昵称列宽和格式
        worksheet.set_column('E:E', 20, uid_format)  # 设置被回复用户列宽和格式
        worksheet.set_column('F:F', 80, uid_format)  # 设置评论内容列宽和格式

        # 设置时间列格式
        time_format = workbook.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'})
        worksheet.set_column('J:J', 20, time_format)  # 设置时间列宽和格式

    print(f"评论已保存到文件: comments_{file_name}.xlsx")
    return

函数参数说明

  • max_pages: 爬取的最大页面数量。
  • count:统计已爬取的评论总数,用于判断是否已爬取所有评论。
  • time_sleep:每两次爬取的时间间隔,防止被封ip

2.4 cookie更新

如果代码运行成功后,隔几天再次使用无法运行,成功获得json,但'replies'为空,那说明网站的cookie更新了,你需要再次进网页查找,更新一下代码中的cookie值。

正在读取第1页
{'code': 0,
 'data': {'assist': 0,
          'blacklist': 0,
          'callbacks': None,
          'config': {'read_only': False, 'show_up_flag': False, 'showtopic': 0},
          'context_feature': '',
          'control': {'answer_guide_android_url': '',
                      'answer_guide_icon_url': '',
                      'answer_guide_ios_url': '',
                      'answer_guide_text': '',
                      'bg_text': '',
                      'child_input_text': '',
                      'disable_jump_emote': False,
                      'empty_page': None,
                      'enable_charged': False,
                      'enable_cm_biz_helper': False,
                      'giveup_input_text': '',
                      'input_disable': False,
                      'preload_resources': None,
                      'root_input_text': '',
                      'screenshot_icon_state': 0,
                      'show_text': '',
                      'show_type': 0,
                      'upload_picture_icon_state': 0,
                      'web_selection': False},
          'cursor': {'is_begin': False,
                     'is_end': True,
                     'mode': 0,
                     'mode_text': '',
                     'name': '',
                     'next': 0,
                     'pagination_reply': None,
                     'prev': 0,
                     'session_id': ''},
          'effects': {'preloading': ''},
          'esports_grade_card': None,
          'note': 0,
          'replies': None,
          'top': {'admin': None, 'upper': None, 'vote': None},
          'top_replies': None,
          'up_selection': {'ignore_count': 0, 'pending_count': 0},
          'upper': {'mid': 0},
          'vote': 0},
 'message': '0',
 'ttl': 1}


进程已结束,退出代码为 0

三、运行结果展示

四、爬虫完整代码

import pprint
import requests
import pandas as pd
import time

headers = {
    'User-Agent':'',
    'cookie':''
}

def process_comment(comment, is_reply=False, parent_username=''):
    """处理评论信息并返回字典,并打印返回值"""
    # 处理评论信息
    processed_comment = {
        '用户UID': str(comment['mid']),  # 确保 UID 是字符串格式
        '用户昵称': comment['member']['uname'],
        '用户性别': comment['member']['sex'],
        '粉丝等级': comment['member']['fans_detail']['level'] if comment['member']['fans_detail'] else 0,
        '被回复用户': parent_username,  # 被回复的一级评论用户(如果是二级评论)
        '评论内容': comment['content']['message'],
        '评论层级': '二级评论' if is_reply else '一级评论',
        '用户当前等级': comment['member']['level_info']['current_level'],
        '点赞数量': comment['like'],
        '回复时间': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(comment['ctime']))
    }
    # 打印处理后的评论信息
    print(processed_comment)
    # 返回处理后的评论信息
    return processed_comment

def fetch_comments(video_oid, max_pages=10000):  # 最大页面数量可调整
    comments = []
    count = 0
    for page in range(1, max_pages + 1):
        url = f'https://api.bilibili.com/x/v2/reply/main?next={page}&type=1&oid={video_oid}&mode=3'
        try:
            response = requests.get(url=url, headers=headers, timeout=10)
            if response.status_code == 200:
                data = response.json()
                print(f"正在读取第{page}页")
                # 使用pprint打印JSON数据的结构
                # pprint.pprint(data)
                if data['data']['replies'] is None:
                    break
                if data and 'replies' in data['data']:
                    for comment in data['data']['replies']:
                        # 处理一级评论
                        comments.append(process_comment(comment))

                        # 检查是否有二级评论
                        if 'replies' in comment and comment['replies']:
                            for reply in comment['replies']:
                                # 处理二级评论
                                comments.append(process_comment(reply, is_reply=True, parent_username=comment['member']['uname']))
                #如果本次爬取后,总评论数与上次相同,则认为已经爬取到最后一页,所有评论已全部获取,结束循环
                if count == len(comments):
                    break
                count = len(comments)
            else:
                break
        except requests.RequestException as e:
            print(f"请求出错: {e}")
            break
        time.sleep(0.5) #每两次爬取的间隔时间

    # 将评论信息保存为DataFrame
    df = pd.DataFrame(comments)

    # 使用 ExcelWriter 和 xlsxwriter 设置格式
    with pd.ExcelWriter(f'comments_{file_name}.xlsx', engine='xlsxwriter') as writer:
        df.to_excel(writer, index=False, sheet_name='Comments')
        workbook = writer.book
        worksheet = writer.sheets['Comments']

        # 设置 UID 列格式为文本,以防止科学计数法
        uid_format = workbook.add_format({'num_format': '@'})  # 文本格式
        worksheet.set_column('A:A', 18, uid_format)  # 设置 UID 列宽和格式
        worksheet.set_column('B:B', 20, uid_format)  # 设置昵称列宽和格式
        worksheet.set_column('E:E', 20, uid_format)  # 设置被回复用户列宽和格式
        worksheet.set_column('F:F', 80, uid_format)  # 设置评论内容列宽和格式

        # 设置时间列格式
        time_format = workbook.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'})
        worksheet.set_column('J:J', 20, time_format)  # 设置时间列宽和格式

    print(f"评论已保存到文件: comments_{file_name}.xlsx")
    return

file_name = ''  # 保存文件名
video_oid = ''  # B站视频的oid
print(f'视频名字: {file_name}, video_oid: {video_oid}')
fetch_comments(video_oid)

参数填写

  • 'user-Agent':你的用户代理字符串
  • 'cookie':你的cookie字符串
  • file_name:保存的文件名(默认位置在代码项目文件夹下,可按需求自行修改)
  • video_oid:某站视频的oid
  • max_pages:爬取的最大页面数量,默认10000页

五、可视化代码和运行结果展示

5.1 可视化代码

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 设置支持中文
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']  # 使用更通用的字体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号
plt.rcParams['font.size'] = 12  # 设置字体大小

# 读取 Excel 文件
name = ''  # 文件名
file_name = f'comments_{name}.xlsx'

# 加载数据
df = pd.read_excel(file_name)

# 打印数据以检查
print(df.head())  # 打印前几行数据
print(df.info())  # 打印数据概况

# 对粉丝等级进行分档
bins = [-1, 0, 10, 20, float('inf')]  # 定义边界,-1用于包含0
labels = ['0', '1-10', '11-20', '21及以上']  # 对应的标签
df['粉丝等级档'] = pd.cut(df['粉丝等级'], bins=bins, labels=labels, right=True)

# 可视化用户性别分布
plt.figure(figsize=(8, 6))
gender_counts = df['用户性别'].value_counts()
plt.pie(gender_counts, labels=gender_counts.index, autopct='%1.1f%%', startangle=140)
plt.title(u'用户性别分布')
plt.axis('equal')  # 使饼图为圆形
plt.show()

# 可视化粉丝等级分布
plt.figure(figsize=(8, 6))
fan_level_counts = df['粉丝等级档'].value_counts()  # 使用新的粉丝等级档

# 过滤掉评论数为0的档位
fan_level_counts = fan_level_counts[fan_level_counts > 0]

plt.pie(fan_level_counts, labels=fan_level_counts.index, autopct='%1.1f%%', startangle=140)
plt.title(u'粉丝等级分布')
plt.axis('equal')
plt.show()

# 可视化用户当前等级分布
plt.figure(figsize=(8, 6))
current_level_counts = df['用户当前等级'].value_counts()
plt.pie(current_level_counts, labels=current_level_counts.index, autopct='%1.1f%%', startangle=140)
plt.title(u'用户当前等级分布')
plt.axis('equal')
plt.show()

# 可视化点赞数量最多的五个评论
plt.figure(figsize=(10, 6))
top_likes = df.nlargest(5, '点赞数量')
bar_plot = sns.barplot(x='点赞数量', y='用户昵称', data=top_likes, palette='viridis', hue='用户昵称', legend=False)

# 在条形图上显示点赞数量
for index, row in enumerate(top_likes.itertuples()):
    bar_plot.text(row.点赞数量 + 0.5, index, row.点赞数量, color='black')

plt.title(u'点赞数量最多的五个评论')
plt.xlabel(u'点赞数量')
plt.ylabel(u'用户昵称')
plt.show()

# 可视化按年份分类的回复时间
df['回复时间'] = pd.to_datetime(df['回复时间'], errors='coerce')  # 确保日期格式正确
df['年份'] = df['回复时间'].dt.year

# 统计每年的评论数量
yearly_comments = df.groupby('年份').size()

# 打印 yearly_comments 以检查
print(yearly_comments)

# 只绘制有数据的年份
if not yearly_comments.empty:
    plt.figure(figsize=(10, 6))
    line_plot = sns.lineplot(x=yearly_comments.index, y=yearly_comments.values, marker='o')

    # 在每个数据点上显示评论数
    for x, y in zip(yearly_comments.index, yearly_comments.values):
        line_plot.text(x, y, str(y), color='black', ha='center', va='bottom')

    plt.title(u'按年份分类的评论数')
    plt.xlabel(u'年份')
    plt.ylabel(u'评论数')
    plt.xticks(yearly_comments.index)  # 确保年份标签显示
    plt.show()
else:
    print("没有可用的评论数据来绘制折线图。")

参数填写

  • name:读入的文件名,因为爬虫代码中,会在文件名前加comments_,所以实际的文件名为f'comments_{name},也就是下面的file_name

5.2 运行结果展示

我是终须有梦,如果这篇文章对您有帮助的话,请点一个免费的赞吧!

如有错误,欢迎指正

标签:comment,plt,format,Python,手把手,某站,comments,评论,data
From: https://blog.csdn.net/ambitious_goal/article/details/144003325

相关文章

  • 计算机毕业设计必看必学03361springboot开放实验室管理系统原创定制程序,java、PHP、p
    springboot开放实验室管理系统摘要随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是使用动态网页开发技术java作为系统的开发语言,MySQL作为后台数据......
  • 计算机毕业设计必看必学35702+springboot电影推荐系统原创定制程序,java、PHP、python
                                                              摘 要随着互联网与移动互联网迅速普及,网络上的电影娱乐信息数量相当庞大,人们对获取感兴趣的电影娱乐信息的需求越来越大,个性化的电影推荐系统成......
  • python openpyxl读写excel
    pipinstallopenpyxlimportopenpyxldefcode_main():xlsx_file_name=r'D:\ljh\work_info\test.xlsx'#excel文件路径xlsx_data=openpyxl.load_workbook(xlsx_file_name)table=xlsx_data.active#当前正在活跃的表,也可以指定Sheet对象:table=xl......
  • 【python图解】数据结构之字典和集合
    【python图解】数据结构之字典和集合在Python中,字典和集合是另外的两种重要数据结构,它们分别用于存储键值对和无序的唯一元素集合。下面我们将详细介绍字典和集合的定义、操作方法、使用场景及相关案例。1.字典(Dictionary)字典是一种存储键值对的可变数据类型,它使用大括......
  • Python那些事儿 - 函数
    第十回逐鹿中原前言这一回开始给大家介绍函数的相关知识。其实函数对我们来说并不陌生,我们之前见过的print()、input()、len()、count()等都是函数,只不过Python的开发者们把这些函数提前打包好了,我们只需要去调用就可以。如果我们想通过函数来实现想要的功能,那就需要自己......
  • SpringBoot整合MQTT利用EMQX完成消息的发布与接收+Python模拟硬件测试通信
    教程说明本教程主要内容为使用SpringBoot整合MQTT利用EMQX代理服务完成MQTT的消息发送与接收,然后用Python模拟硬件与SpringBoot应用进行了MQTT消息的通信,教程详细,并在最后讲解了开发中的注意事项,本教程适用于物联网领域、JavaWeb领域的开发人员。前置所需已经搭建好了EMQX代......
  • EPS32+DHT11温湿传感器+OLEAD显示屏整合MicroPython实现温湿度读取并显示 - 幽络源
    环境需求Python版本大于等于3.8、Thonny软件、EPS32已烧录MicroPython固件,可参考上一篇文章 ESP32初学教程Python版-从环境搭建到完成控制LED灯闪烁硬件需求EPS32开发板、DHT11的温湿度传感器、OLEAD显示屏、杜邦线、安卓数据线引脚连接DHT11温湿度传感器连接ESP32使用......
  • 逆向python
    Python逆向若下载下来的题目内存比较大,图标是python的图标,拖进ida中一大堆py的这种字符串。即可断定该exe文件是python编写的。解python的exe包工具:pyinstxtractorhttps://github.com/extremecoders-re/pyinstxtractor使用方法:将exe文件拖到与pyinstxtractor同一目录下。Cmd中pyth......
  • python逆向
    Python逆向若下载下来的题目内存比较大,图标是python的图标,拖进ida中一大堆py的这种字符串。即可断定该exe文件是python编写的。解python的exe包工具:pyinstxtractorhttps://github.com/extremecoders-re/pyinstxtractor使用方法:将exe文件拖到与pyinstxtractor同一目录下。Cmd中pyth......
  • 【Python】高效的数据操作利器:Python中的集合运算详解
    《PythonOpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门!在现代数据处理任务中,集合操作作为一种高效的数据管理方式,广泛应用于去重、交集、差集等操作。Python的set类型以其强大的功能和直观的语法,成为处理集合运算的首选工具。本篇文章将深入剖析set的工作......