python爬虫:爬取A站视频信息

一、爬取任务

  1. 选取acfun视频分享网站(https://www.acfun.cn)作为数据来源,使用python编程,通过网络爬虫技术爬取100个视频 的相关信息。
  2. 爬取数据字段:UP主用户名、用户ID、视频标题、视频上传时间、观看量、点赞数量、弹幕数量、收藏数量、投币数量、视频时长等。
  3. 技术要求:使用python语言,requests库进行数据爬取,bueatifulsoup和正则表达式提取数据。遵守目标网站的robots.txt文件规定,合理控制爬虫的访问频率。

二、数据分析

  1. 对抓取到的数据进行整理和清洗,去除无效或错误的数据。进行基本的统计分析包括:视频数量分布(按UP主、上传时间等)观看量、点赞量、弹幕量、收藏量和投币量的统计(如平均数、中位数、标准差等)
  2. 技术要求:使用python的pandas库进行数据清洗和分析。使用matplotlib或seaborn库对数据分析结果进行可视化。

三、实验过程

(一)数据爬取部分

在A站娱乐栏目中搞笑类型界面,进入开发者模式(按快捷键F12),如下图所示提取信息。

获取到请求头和请求网址后就可以简单验证能否成功get页面信息。

import requests

headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
        'Referer': 'https://www.acfun.cn/v/list1/index.htm'
    }
res = requests.get(url=page_url, headers=headers)
        if res.status_code != 200:
            print('请求失败')
        else:
            print('请求成功')

成功get页面信息后,接着需要从页面上提取出每个视频信息,从页面源码上获取到视频信息只有视频标题、UP主名、视频播放量、评论量,要获取更多信息要到视频url源码获取。所以我先从源码中得到每个视频的url。

def get_url_data(html_data):
    soup = BeautifulSoup(html_data, 'html.parser')
    video_data = soup.find_all('div', id="listwrapper")
    for html in video_data:
        # 获取视频ID
        video_ID = re.findall(r'<a href="(.*?)"', str(html))
        video_id = [user_ID[3:] for user_ID in video_ID]
        # 循环获取'https://www.acfun.cn'+视频ID
        video_url = ['https://www.acfun.cn' + video_ID[i] for i in range(len(video_ID))]
        
        print(video_url)

然后从视频播放页面中提取视频点赞量、弹幕量、up主id等信息。

def get_data(video_url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
        'Cookie': '######自己的cookie#####'
    }
    response = requests.get(url=video_url, headers=headers, timeout=30)
    # print(response)
    soup = BeautifulSoup(response.text, 'html.parser')
    video_data = soup.find('div', class_='video-description clearfix')
    # 获取视频标题
    title = video_data.find('span').text.strip()
    # 获取up_ID
    up_ID = soup.find('a', class_='up-name')['href'].replace('/u/', '')
    # 获取播放量
    video_view_num = soup.find('span', class_='viewsCount').text

    # 获取UP博主名
    up_name = soup.find('a', class_='up-name').text.strip()
    # 获取粉丝数
    # 提取粉丝数,去除“关注”文字,strip()作用是去除文本开头和结尾的空白
    followers_element = soup.find('div', class_='follow-up not-follow')  # .get_text().strip().replace('\xa0', ' ')
    # 检查是否找到了对应的元素
    if followers_element:
        followers_text = followers_element.get_text().strip().replace('\xa0', ' ')
        # 分割文本,获取数字部分
        followers_num = followers_text.split(' ')[1]

    else:
        # 如果没有找到该元素,可以设定一个默认值或进行其他处理
        followers_num = "0"  # 或者其他默认文本

    # 获取弹幕量
    danmu_num = int(video_data.find('span', class_='danmuCount sp2').text)
    # 获取点赞量
    like_num = video_data.find('span', class_='likeCount').text
    if like_num == '点赞':
        like_num = 0
    # 获取收藏量
    collect_num = int(video_data.find('span', class_='collectionCount').text)
    # 获取投币量
    coin_num = video_data.find('span', class_='bananaCount').text
    # 获取评论量
    comment_num = int(video_data.find('span', class_='commentCount').text)
    # 获取视频类型标签
    video_type = soup.find_all('a', class_='fl')
    if video_type:
        video_type = [video_type[i].text for i in range(len(video_type))]
    # 获取单个视频全部数据
    df_data1 = {
        '视频标题': title,
        'up_ID': up_ID,
        'UP博主名': up_name,
        '粉丝数': followers_num,
        '评论量': comment_num,
        '播放量': video_view_num,
        '弹幕量': danmu_num,
        '点赞量': like_num,
        '收藏量': collect_num,
        '投币量': coin_num,
        '视频网址': video_url,
        '视频类型': video_type
    }
    print(df_data1)

每一页大约有30个视频,完成以上步骤基本可以获取到30个视频信息。如果要获取更多视频,需要获取页面url。只需要改变page这个参数就可以获取到多个页面,因此,需要for循环实现。

def download_data(page_num):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
        'Referer': 'https://www.acfun.cn/v/list1/index.htm'
    }
    all_page_data = []
    base_url = 'https://www.acfun.cn/v/list206/index.htm?sortField=rankScore&duration=all&date=default&page={page}'
    for idx in range(1, page_num):
        page_url = base_url.format(page=idx)
        all_page_data.append(page_url)

    for page_url in all_page_data:
        res = requests.get(url=page_url, headers=headers)
        if res.status_code != 200:
            print('请求失败')
            break
        else:
            text = res.text
            # get_data(text)
            time.sleep(2)

    # save_data(all_data)
    print(all_page_data)

(二)爬取A站视频信息完整代码

import re
import time
from pprint import pprint

import pandas as pd
import requests
from bs4 import BeautifulSoup
from lxml import etree

all_data = []
other_data = []


def download_data(page_num):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
        'Referer': 'https://www.acfun.cn/v/list1/index.htm',
        'Cookie': '####自己的cookie###'
    }
    all_page_data = []
    count = 0
    base_url = 'https://www.acfun.cn/v/list206/index.htm?sortField=rankScore&duration=all&date=default&page={page}'
    for idx in range(1, page_num):
        page_url = base_url.format(page=idx)
        all_page_data.append(page_url)  # 将所有页面的url添加到列表中

    for page_url in all_page_data:
        res = requests.get(url=page_url, headers=headers, timeout=30)
        if res.status_code != 200:
            print('请求失败')
            break
        else:
            text = res.text
            # for i in range(1, len(all_page_data)):
            get_url_data(text)
            time.sleep(10)  # 避免频繁请求,适当延迟
            count += 1
            print('第{}页数据获取成功'.format(count))

    print(all_page_data)
    print(other_data)
    

# 获取视频ID、视频网址、视频时长
def get_url_data(html_data):
    soup = BeautifulSoup(html_data, 'html.parser')
    video_data = soup.find_all('div', id="listwrapper")
    for html in video_data:
        # 获取视频ID
        video_ID = re.findall(r'<a href="(.*?)"', str(html))
        video_id = [user_ID[3:] for user_ID in video_ID]
        # 循环获取'https://www.acfun.cn'+视频ID
        video_url = ['https://www.acfun.cn' + video_ID[i] for i in range(len(video_ID))]
        # 获取视频时长
        time = html.find_all('span', class_="video-time")
        video_time = [time.text for time in time]

        # 保存数据
        df_data = {

            '视频ID': video_id,
            '视频网址': video_url,
            '视频时长': video_time

        }
        other_data.append(df_data)
        print(video_url)
    # 通过视频URL循环获取视频相关信息
    for url in video_url:
        # 获取视频相关信息
        get_data(url)


# 获取视频相关信息
def get_data(video_url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76',
        'Cookie': '#######自己的cookie###'
    }
    response = requests.get(url=video_url, headers=headers, timeout=30)
    # print(response)
    soup = BeautifulSoup(response.text, 'html.parser')
    video_data = soup.find('div', class_='video-description clearfix')
    # 获取视频标题
    title = video_data.find('span').text.strip()

    # 获取up_ID
    up_ID = soup.find('a', class_='up-name')['href'].replace('/u/', '')
    # 获取播放量
    video_view_num = soup.find('span', class_='viewsCount').text

    # 获取UP博主名
    up_name = soup.find('a', class_='up-name').text.strip()
    # 获取粉丝数
    # 提取粉丝数,去除“关注”文字,strip()作用是去除文本开头和结尾的空白
    followers_element = soup.find('div', class_='follow-up not-follow')  # .get_text().strip().replace('\xa0', ' ')
    # 检查是否找到了对应的元素
    if followers_element:
        followers_text = followers_element.get_text().strip().replace('\xa0', ' ')
        # 分割文本,获取数字部分
        followers_num = followers_text.split(' ')[1]

    else:
        # 如果没有找到该元素,可以设定一个默认值或进行其他处理
        followers_num = "0"  # 或者其他默认文本

    # 获取弹幕量
    danmu_num = int(video_data.find('span', class_='danmuCount sp2').text)
    # 获取点赞量
    like_num = video_data.find('span', class_='likeCount').text
    if like_num == '点赞':
        like_num = 0
    # 获取收藏量
    collect_num = int(video_data.find('span', class_='collectionCount').text)
    # 获取投币量
    coin_num = video_data.find('span', class_='bananaCount').text
    # 获取评论量
    comment_num = int(video_data.find('span', class_='commentCount').text)
    # 获取视频类型标签
    video_type = soup.find_all('a', class_='fl')
    if video_type:
        video_type = [video_type[i].text for i in range(len(video_type))]
    # 获取单个视频全部数据
    df_data1 = {
        '视频标题': title,
        'up_ID': up_ID,
        'UP博主名': up_name,
        '粉丝数': followers_num,
        '评论量': comment_num,
        '播放量': video_view_num,
        '弹幕量': danmu_num,
        '点赞量': like_num,
        '收藏量': collect_num,
        '投币量': coin_num,
        '视频网址': video_url,
        '视频类型': video_type
    }
    all_data.append(df_data1)
    print(df_data1)
    save_data(all_data, file_name='acfun_data1.csv')


def save_data(df_data, file_name):
    acfun_data = pd.DataFrame(df_data)
    acfun_data.to_csv(file_name, index=False, encoding='utf-8-sig')


if __name__ == '__main__':
    download_data(page_num=2)

运行结果:

(三)数据分析与可视化

数据可视化是在jupyter notebook上实现的

导入所需要的包

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import numpy as np
#导入数据
df = pd.read_csv("./acfun_data3.csv")
df.head()

#检测数据中的缺失值
df.isnull().sum

#对数据进行缺失值处理
df.fillna(0, inplace=True)

df.info() #获取数据详细信息

df.describe()

#把播放量转化为整数
def convert_to_int_play_count(play_count_str):
    """
    将包含“万”字的播放量字符串转换为整数。

    返回:
    int: 转换后的整数播放量。
    """
    if '万' in play_count_str:
        # 去掉“万”字并乘以10000
        return int(float(play_count_str.replace('万', '')) * 10000)
    else:
        # 如果没有“万”,假定已经是整数或可以转换为整数
        return int(play_count_str)

# 假设df是您的DataFrame,且"play_count"是包含播放量的列
# 应用转换函数
df["播放量"] = df["播放量"].apply(convert_to_int_play_count)
df["粉丝数"] = df["粉丝数"].apply(convert_to_int_play_count)
df["投币量"] = df["投币量"].apply(convert_to_int_play_count)
#UP主累计上榜视频总播放量前20
up_play = df.groupby('up_ID')['播放量'].sum().sort_values(ascending=False)[:20]
up_play

# 为了坐标轴上能显示中文
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

#上前10热门视频数量的UP主
up = df['UP博主名'].value_counts()[:10]
print(up)

#粉丝数量排名前十的UP博主
top_10_followers = (
    df.sort_values(by='粉丝数', ascending=False)  # 按粉丝数量降序排序
    .groupby('UP博主名')['粉丝数']  # 按博主名分组并保留'粉丝数量'列
    .first()  # 选择每个组的第一个(因为粉丝数量已经排序,所以是最大的)
    .nlargest(10)  # 获取前10个最大值
)

print(top_10_followers)

#播放量排名前十的视频
plt.figure(figsize=(20, 10))
df.sort_values(by='播放量', ascending=False, inplace=True)
df.head(10).plot(kind='bar', x='视频标题', y='播放量',fontsize=10,color='red')
plt.xlabel('视频标题',fontsize=15)
plt.ylabel('播放量(万)',fontsize=15)
plt.title('播放量排名前10的视频',fontsize=20)
plt.xticks(rotation=45, ha='right')  # 旋转x轴标签,防止重叠
plt.show()

#播放量直方图
# 数据准备
a = df['播放量']
s = pd.Series(a) 
# 用Matplotlib画直方图
plt.hist(s)
plt.title('播放量直方图',fontsize=20)
plt.show()

#投币量与点赞量散点图
# 假设df是包含数据的DataFrame
coin_amounts = df["投币量"]
like_amounts = df["点赞量"]

# 绘制散点图
plt.figure(figsize=(10, 6))
plt.scatter(coin_amounts, like_amounts, color='blue', alpha=0.3)
plt.title('投币量与点赞量散点图',fontsize=20)
plt.xlabel('投币量',fontsize=15)
plt.ylabel('点赞量',fontsize=15)
plt.xticks([0,2000,4000,6000,8000,10000,12000])
plt.show()

小结

希望我的讲解,大家能够看懂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值