这篇文章主要介绍了python大作业有哪些题目,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。
【超详细指北】python大作业!
这是笔者最近写python大作业时写的一个实现过程笔记,也就是基本上可以说是本人从0开始上手的一个python练习。程序和本文档从 4.29-5.15日 总共历时17天python动态爱心代码。包含了大部分代码内容。
一、获取数据
(1)user-agent和cookie
user-agent
Cookie:
buvid3=11707BB8-8181-70C7-EBE1-FB1609F40FC370555infoc; i-wanna-go-back=-1; _uuid=F4221228-EF95-B7F10-49C1-F710CAC68D109F77140infoc; buvid4=E437889C-0A9F-DEF4-C164-E3F9F456407172347-022032622-MnLxL6Vqo8K/D8N1XzXHLQ%3D%3D; nostalgia_conf=-1; buvid_fp_plain=undefined; blackside_state=1; rpdid=|(J~J|R~m)Jm0J'uYR)Jm~JYR; CURRENT_BLACKGAP=0; hit-dyn-v2=1; LIVE_BUVID=AUTO4316488832212386; bp_article_offset_154100711=649151673891029000; SESSDATA=fbf8b924%2C1666235070%2C5c5a7%2A41; bili_jct=2f4e142aa58387a4ba58d6610a138881; DedeUserID=154100711; DedeUserID__ckMd5=4a5f601a3689140a; sid=7m78ki9o; CURRENT_QUALITY=0; fingerprint=a0d6414c1242c8cb9c9f7b4f70d4d671; b_ut=5; CURRENT_FNVAL=4048; bsource=search_baidu; b_lsid=AB9536C2_1807AC90CF7; _dfcaptcha=0f5ba157af594817171639f2996e0b43; PVID=1; innersign=1; buvid_fp=a0d6414c1242c8cb9c9f7b4f70d4d671; bp_video_offset_154100711=654934739730300900; fingerprint3=5ad9983134e17174abef4db7b440a5ab
(2)commentData类
该类是获取某一视频的所有评论信息,包括一级评论、二级评论,获取了评论用户的基本信息和评论内容。在该类中,设置headers和Cookie防止反爬,此外还有一个fake_useragent库也可以防止反爬虫,在这里没有使用该库。
写在前面
首先我们来分析一级评论:
一级评论:
根据浏览器f12自带的调试中,我们查找存放评论内容的api。这里给出三个不同视频的评论接口:
三个网页的一级评论api及来源
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390
【https://www.bilibili.com/video/BV1ot411R7SM?spm_id_from=333.999.0.0】
https://api.bilibili.com/x/v2/reply/main?callback=jQuery33102399794496926384_1651209840924&jsonp=jsonp&next=0&type=1&oid=768445836&mode=3&plat=1&_=1651209840925
【https://www.bilibili.com/video/BV11r4y1J7cH?spm_id_from=333.999.0.0】
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17203622673329462698_1651210156500&jsonp=jsonp&next=0&type=1&oid=721394418&mode=3&plat=1&_=1651210156936
【https://www.bilibili.com/video/BV1fQ4y1q7SB/?spm_id_from=333.788.recommend_more_video.16】
-
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390
-
https://api.bilibili.com/x/v2/reply/main?callback=jQuery33102399794496926384_1651209840924&jsonp=jsonp&next=0&type=1&oid=768445836&mode=3&plat=1&_=1651209840925
-
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17203622673329462698_1651210156500&jsonp=jsonp&next=0&type=1&oid=721394418&mode=3&plat=1&_=1651210156936
可见在加粗部分是不同的
第一个api中:
https://api.bilibili.com/x/v2/reply/main?callback=jQuery17208590914915452643_1651207947683&jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1&_=1651207949390
删除第一个和最后一个参数(因为我们不需要js请求,最后一个参数也没有什么影响),得到
一级评论:https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next=0&type=1&oid=34491719&mode=3&plat=1
- next:翻页
- oid:视频编号(aid)
- mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
二级评论:
二级评论也就是视频评论的评论,也就是有人回复评论时的评论。
https://api.bilibili.com/x/v2/reply/reply?callback=jQuery17202729032535004876_1651213886637&jsonp=jsonp&pn=1&type=1&oid=34491719&ps=10&root=1426909940&_=1651213945276
同上删除首尾参数后得到:
二级评论:https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&pn=1&type=1&oid=34491719&ps=10&root=1426909940
- pn:翻页
- oid:视频oid
- ps: 单页显示数量(最大为20)
- root:楼主的回复的rpid
视频的oid可通过视频BV号获取,rpid可以通过一级评论获取(随后我们进行获取)
最后一页评论:
我们自己根据一级评论的api,手动查找到最后一页评论,发现当没有评论时,data下的replies为null,机当前api中next的参数值为最后一页的页码,如果有评论时replies不为空。
因此我们在爬取所有评论时可以将replies是否为null作为循环退出条件。
https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid=34491719&mode=0&plat=1&next=28
1.构造函数init
初始化基本内容:
- mid:up主的uid,传入参数
- name:up主的姓名,传输参数
- BV:爬取视频的BV号,传入参数
- mode:排序方式(这里笔者所写的类中其实一直默认的0,也就是默认排序,其他自测):1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息,传入参数
- header:请求时的header,默认值,可自行更改
- cookies:设置header和cookie防止网站反爬,传入参数
- page:评论页数,通过爬取时根据api返回的replies是否为空进行判断是否爬取完毕,对self.page进行累加,来达到计算总共评论的总数量的目的。
- BVName:视频名称,!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- homeUrl:api的网址开头部分
- oid:视频的id,通过**oid_get(self, BV)**函数返回oid值。
- replyUrl:一级评论的api
- rreplyUrl:二级评论的api
- q:创建的队列,将content_get方法返回爬取内容并存入队列,通过csv_writeIn方法从q队列中进行取出存取,方便多线程工作,是一个生产着消费者模式。
- count:当前评论楼数,指定主楼数,区别是评论还是评论的评论
因为在获取BVName和oid时,需要homeUrl,所以我们讲homeUrl放置在BVName和oid之前
def __init__(self, mid, name, BV, mode, cookies):
self.mid = mid #up主的uid
self.name = name #up主的账号名称
self.BV = BV # BV:视频id号
self.mode = mode # mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
}
self.cookies = cookies # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
self.page = 0 # page:评论页数,出最后一页每页20条
self.homeUrl = "https://www.bilibili.com/video/"
self.BVName = self.BVName_get(self.BV)
self.oid = self.oid_get(self.BV)
#一级评论和二级评论
self.replyUrl="https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid={oid}&mode={mode}&plat=1&next=".format(oid=self.oid,mode=mode)#next=0
self.rreplyUrl = "https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&type=1&oid={oid}&ps=20&root={root}&pn=".format(oid=self.oid, root="{root}")#pn=1
self.q = queue.Queue() # 用来存放爬取的数据,通过队列可以按顺序,使用多线程存入数据库或csv文件中
# 这里我们用到了队列,好处在于,可以用多线程边爬边存,按顺序先进先出
self.count = 1 # count变量指定主楼数,区别是评论还是评论的评论
2.获取视频oid和获取视频名称方法
方法一:通过正则从response中选择以字符串aid开头的值并将其进行返回。
方法二:通过BeautifulSoup4类获取视频名称,获取含有视频名称的标签,从而通过自带大string方法获取名称
(78条消息) Python中BeautifulSoup库的用法_阎_松的博客-CSDN博客_beautifulsoup库的作用
# 获取视频 oid
def oid_get(self, BV):
# 请求视频页面
response = requests.get(url=self.homeUrl + BV).text
# 用正则表达式 通过视频 bv 号获取 oid
oid = re.findall("\"aid\":([0-9]*),", response)[0]#寻找以字符串aid开头的值
print("oid:" + oid)
return oid
def BVName_get(self,BV):
# 请求视频页面
response = requests.get(url=self.homeUrl + BV).text
soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
nameResultSet = soup.find_all(attrs={'class': 'tit'}) # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
result = nameResultSet[0].string #城市与山里的差距,真正体验过,我来告诉你!
print("BVName:" + result)
return result
3.评论内容获取
首先我们请求函数传递url,page(最大页面数,最终代码会删掉这个page参数,因为通过判断replies是否为空来获取所有页码的评论,就不需要指定获取页码的内容了),通过requests库请求数据,需要的数据都在data->replies里面,将该内容用一个列表保存。
评论内容详细分析:其中是该视频的主要评论(也就是一级评论),其下有部分回复该评论的子评论,详细内容包含了评论的id、视频的id、时间戳、评论内容等等,其中主要信息为
- rpid:评论id
- oid:该视频的oid
- mid:账户的uid
- rcount :回复数
- ctime:时间戳
- like:点赞数
- member–>sign:用户标签,即用户的个性签名
- content–>message:评论内容
- replies:评论列表
- replies–>rpid:子评论的id
- replies–>level :用户等级
获取一级评论的数据:
#获取当前页面的评论
def content_get(self, url, page):
now = 0 # 当前页面
while now<=page:
print("page : <{now}>/<{page}>".format(now=now, page=page))
response = requests.get(url=url+str(now), cookies=self.cookies, headers=self.headers, timeout=10).json() # 把response解析为json格式,通过字典获取
replies = response['data']['replies'] # 评论数据在data->replies 里面,每页有 20 条
now += 1
for reply in replies: # 遍历获取每一条,用reply_clean函数提取数据
line = self.reply_clean(reply)
self.count += 1
因为一二级评论格式基本一致,所以将上面获取一级评论的数据的代码修改一下,增加复用性。
这里新增了level_1来判断是否是一级评论,如果是则进行请求下一级,否则不请求。
此外,这里将page参数进行了删除,通过之前分析的,通过判断replies是否为空来判断是否到达评论的最后一页。
#数据获取:获取当前页面的评论
def content_get(self, url, level_1=True):
# level_1判断是否为一级评论。如果为二级评论,则不请求下一级评论(评论的评论)
now = 1
while True:
if level_1:
print("page : <{now}>".format(now=now))
response = requests.get(url=url + str(now), cookies=self.cookies, headers=self.headers).json()
print(url + str(now))
replies = response['data']['replies'] # 评论数据在data->replies 里面,一共有 20 条
if (replies == None)and(now == 1):
#因为当next==0时和next==1时的评论内容是一样的,所以单独写出来一种情况:该视频没有任何评论
self.page=0
print("该页没有评论......")
return
elif replies == None:
self.page = now - 1
print("评论信息获取完成......")
return
elif replies != None:
now += 1
for reply in replies:
# 一级评论则去请求下一级评论
if level_1:
line = self.reply_clean(reply, self.count)
self.count += 1
else:
line = self.reply_clean(reply)
self.q.put(line)
# 这儿我们可以筛选一下,如果有二级评论,调用函数请求二级评论
if level_1 == True and line[-2] != 0:#如果是一级评论且 回复数 不为零 则去请求二级评论
self.content_get(url=self.rreplyUrl.format(root=str(line[-1])), level_1=False) # 递归获取二级评论
4.数据清洗
因为replies下的数据过多而且繁杂,而我们不需要这么多的数据,所以我们进行一下数据的“清洗”,只返回我们需要的数据信息。
将评论时间的时间戳通过time库转换成正常格式。通过之前分析的含义,将需要的信息保存并且返回为列表类型。为了使程序更具用复用性,这里兼容清洗二级评论数据,增加count参数,默认为false,表示是否是二级评论。
如果是二级评论,则返回数据第一个为"回复",否则为楼号。
二级评论没有回复数rcount,三级评论都显示为 回复xxx @谁谁谁
# 数据清洗,将我们需要的数据进行筛选返回
def reply_clean(self, reply, count=False):
# 这个函数可以爬一级评论也能爬二级评论
# count 参数,看看是不是二级评论。
name = reply['member']['uname'] # 名字
sex = reply['member']['sex'] # 性别:男/女/保密
mid = reply['member']['mid'] # 帐号的uid
sign = reply['member']['sign'] # 个性签名
rpid = reply['rpid'] # 评论的id,爬二级评论要用到
rcount = reply['rcount'] # 回复数
level = reply['member']['level_info']['current_level'] # 用户等级
like = reply['like'] # 点赞数
content = reply['content']['message'].replace("\n", "") # 评论内容
t = reply['ctime'] #时间戳
timeArray = time.localtime(t)
otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray) # 评论时间,时间戳转为标准时间格式,2022-05-05 19:15:14
# 如果是二级评论,则返回数据第一个为"回复",否则为楼号
# 二级评论没有回复数rcount,三级评论都显示为 回复xxx @谁谁谁
if count:
return [count, name, sex, level, mid, sign, otherStyleTime, content, like, rcount, rpid]
else:
return ["回复", name, sex, level, mid, sign, otherStyleTime, content, like, ' ', rpid]
5.存储评论内容
将信息存放在dirname的文件夹下,在该文件夹下细分为up主自己的文件夹和视频的文件夹。每次写入一行数据,即将line的列表信息进行写入。不断从队列q中取出内容并保存。最后恢复到开始的工作目录。
完整代码:
#csv文件保存数据
def csv_writeIn(self, mid, name, BV, BVName ):
dirname = '视频评论信息'
begin = os.getcwd() # 保存开始文件工作路径
# 如果没有该文件夹则创建一个
if not os.path.isdir(dirname):
os.mkdir(dirname)
os.chdir(dirname) # 改变当前工作目录到指定的路径
fileName = str(mid) + "-" + str(name) # up主的文件夹:uid-name
if not os.path.isdir(fileName):
os.mkdir(fileName)
os.chdir(fileName)
fileName = str(BV) + "-" + str(BVName) # BV视频的文件夹:BV-BVname
if not os.path.isdir(fileName):
os.mkdir(fileName)
os.chdir(fileName)
file = open("bilibili评论_" + BV + ".csv", "w", encoding="utf-8", newline="")
f = csv.writer(file)
line1 = ['楼层', '姓名', '性别', '等级', 'uid', '个性签名', '评论时间', '评论内容', '点赞数', '回复数', 'rpid']
f.writerow(line1)
file.flush()
while True:
try:
line = self.q.get(timeout=10)
except:
break
f.writerow(line)
file.flush()
file.close()
os.chdir(begin) # 恢复文件工作路径
6.commentData类代码
import os
import time
import requests
import re
import queue
import csv
from threading import Thread
from bs4 import BeautifulSoup
#该类实现爬取保存一个视频的评论信息。
class commentData:
#构造函数__init__,设置基础信息
def __init__(self, mid, name, BV, mode, cookies):
self.mid = mid #up主的uid
self.name = name #up主的账号名称
self.BV = BV # BV:视频id号
self.mode = mode # mode:1、2表示按热度、时间排序; 0、3表示按热度排序,并显示评论和用户信息
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
}
self.cookies = cookies # 设置headers和Cookie防止反爬,还有一个fake_useragent库也可以用
self.page = 0 # page:评论页数,出最后一页每页20条
self.homeUrl = "https://www.bilibili.com/video/"
self.BVName = self.BVName_get(self.BV)
self.oid = self.oid_get(self.BV)
#一级评论和二级评论
self.replyUrl="https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&type=1&oid={oid}&mode={mode}&plat=1&next=".format(oid=self.oid,mode=mode)#next=0
self.rreplyUrl = "https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&type=1&oid={oid}&ps=20&root={root}&pn=".format(oid=self.oid, root="{root}")#pn=1
self.q = queue.Queue() # 用来存放爬取的数据,通过队列可以按顺序,使用多线程存入数据库或csv文件中
# 这里我们用到了队列,好处在于,可以用多线程边爬边存,按顺序先进先出
self.count = 1 # count变量指定主楼数,区别是评论还是评论的评论
# 获取视频 oid
def oid_get(self, BV):
# 请求视频页面
response = requests.get(url=self.homeUrl + BV).text
# 用正则表达式 通过视频 bv 号获取 oid
oid = re.findall("\"aid\":([0-9]*),", response)[0]#寻找以字符串aid开头的值
print("oid:" + oid)
return oid
def BVName_get(self,BV):
# 请求视频页面
response = requests.get(url=self.homeUrl + BV).text
soup = BeautifulSoup(response, "html.parser", from_encoding="utf-8")
nameResultSet = soup.find_all(attrs={'class': 'tit'}) # [<span class="tit">城市与山里的差距,真正体验过,我来告诉你!</span>]
BVName = nameResultSet[0].string #城市与山里的差距,真正体验过,我来告诉你!
print("BVName:" + BVName)
return BVName
#数据获取:获取当前页面的评论
def content_get(self, url, level_1=True):
# level_1判断是否为一级评论。如果为二级评论,则不请求下一级评论(评论的评论)
now = 1
while True:
if level_1:
print("page : <{now}>".format(now=now))
response = requests.get(url=url + str(now), cookies=self.cookies, headers=self.headers).json()
print(url + str(now))
replies = response['data']['replies'] # 评论数据在data->replies 里面,一共有 20 条
if (replies == None)and(now == 1):
#因为当next==0时和next==1时的评论内容是一样的,所以单独写出来一种情况:该视频没有任何评论
self.page=0
print("该页没有评论......")
return
elif replies == None:
self.page = now - 1
print("评论信息获取完成......")
return
elif replies != None:
now += 1
for reply in replies:
# 一级评论则去请求下一级评论
if level_1:
line = self.reply_clean(reply, self.count)
self.count += 1
else:
line = self.reply_clean(reply)
self.q.put(line)
# 这儿我们可以筛选一下,如果有二级评论,调用函数请求二级评论
if level_1 == True and line[-2] != 0:#如果是一级评论且 回复数 不为零 则去请求二级评论
self.content_get(url=self.rreplyUrl.format(root=str(line[-1])), level_1=False) # 递归获取二级评论
# 数据清洗,将我们需要的数据进行筛选返回
def reply_clean(self, reply, count=False):
# 这个函数可以爬一级评论也能爬二级评论
# count 参数,看看是不是二级评论。
name = reply['member']['uname'] # 名字
sex = reply['member'][