一、爬取任务
- 选取acfun视频分享网站(https://www.acfun.cn)作为数据来源,使用python编程,通过网络爬虫技术爬取100个视频 的相关信息。
- 爬取数据字段:UP主用户名、用户ID、视频标题、视频上传时间、观看量、点赞数量、弹幕数量、收藏数量、投币数量、视频时长等。
- 技术要求:使用python语言,requests库进行数据爬取,bueatifulsoup和正则表达式提取数据。遵守目标网站的robots.txt文件规定,合理控制爬虫的访问频率。
二、数据分析
- 对抓取到的数据进行整理和清洗,去除无效或错误的数据。进行基本的统计分析包括:视频数量分布(按UP主、上传时间等)观看量、点赞量、弹幕量、收藏量和投币量的统计(如平均数、中位数、标准差等)
- 技术要求:使用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()
小结
希望我的讲解,大家能够看懂。