20233210林渤程 2023-2024-2 实验4 Python综合实践

芝士就食知识 2024-05-25 17:02:12

课程:《Python程序设计》
班级: 2332
姓名: 林渤程
学号:20233210
实验教师:王志强
实验日期:2024年5月15日
必修/选修: 公选课

1.实验内容

利用python实现爬虫,爬取B站评论,并获得词云图

2. 实验过程及结果

1)引入函数库
其中csv用于保存,re、hashlib、time用于多页爬取,pandas等用于生成词云,具体用途在下面提到

import requests
import re
from bs4 import BeautifulSoup
import csv                   
import hashlib                    
import time
from urllib.parse import quote
import pandas as pd
from wordcloud import WordCloud
import matplotlib.pyplot as plt

2)请求获取数据
首先,用header假装自己是电脑,防止出现I am a teapot;其中data为字符串参数,具体数值可以在网页后端找到;最后,用request进行请求

img

def datasolve(next_page, date, w_rid, flag1, oid):
     headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
    }
    # I am a teapot
    url = 'https://api.bilibili.com/x/v2/reply/wbi/main'
    data = {
            'oid': oid,
            'type': '1',
            'mode': '2',
            'pagination_str': '{"offset":%s}' % next_page,
            'plat': '1',
            'web_location': '1315875',
            'w_rid': w_rid,
            'wts': date,
        }
   response = requests.get(url, params=data, headers=headers)

3)解析数据
首先,我们发现评论的位置被{}包裹起来,故使用json;而后我们逐一查找键值找到评论所在地,可以利用搜索快速查找,利用键值一层一层往下,最后使用循环将一页评论读完

img

img

img


def datasolve(next_page, date, w_rid, flag1, oid):
     headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
    }
    url = 'https://api.bilibili.com/x/v2/reply/wbi/main'
    data = {
            'oid': oid,
            'type': '1',
            'mode': '2',
            'pagination_str': '{"offset":%s}' % next_page,
            'plat': '1',
            'web_location': '1315875',
            'w_rid': w_rid,
            'wts': date,
        }
   response = requests.get(url, params=data, headers=headers)
   json_data = response.json()      #b站的评论响应内容被{}包裹起来,故用json
   all_reply = json_data['data']['replies']
   for reply in all_reply:
        message = reply['content']['message'].replace('\n', ',')    #去掉换行符
        like = reply['like']
        datalist = {
            'reply': message,
            'like': like,
        }
        savedata(datalist, flag1)
        flag1 = False
        print(datalist)

4)保存数据
用csv进行保存,写入表格,其中fieldnames为字段名,即表头,根据获取数据写出,用flag进行判断,防止写完一次、覆盖一次,应使用’a‘,进行追加;采用‘utf-8’来对爬去的信息进行解码

def savedata(datalist, flag):
    if flag:
        fe = open('data.csv', 'w', encoding = 'utf-8', newline='')
        csv_writer = csv.DictWriter(fe, fieldnames=[
            'reply',
            'like',
        ])      #字段名,即表头,根据获取数据
        csv_writer.writeheader()
        csv_writer.writerow(datalist)
    else:
        fe = open('data.csv', 'a', encoding='utf-8', newline='')
        csv_writer = csv.DictWriter(fe, fieldnames=[
            'reply',
            'like',
        ])
        csv_writer.writerow(datalist)

5)多页爬取

img

通过比较可以发现,不同的评论页的网址(滚动会出现不同的网址)只有pagination_str,w_rid,wts会变。其中wts为时间戳可直接通过time函数获取,通过学习我们可以发现下一页的pagination_str蕴含在本页中,故同提取评论一样可获得。
-通过不完全归纳法,可以大致看出不同视频只有oid变了

img

img

但通过在评论给的相应中搜索我们找不到w_rid在里面,通过学习可知其被加密,故进行泛搜索找到了其源代码

img

通过滚动页面,设置断点我们可以知道相关的参数,也通过学习与验算可知md5加密在函数库hashlib中

def Hash(date, next_page,flag,oid):
    #获取w_rid的值
    next_offset = '{"offset":%s}' % next_page
    en = [
        "mode=2",
        f"oid={oid}",
        f"pagination_str={quote(next_offset)}",
        "plat=1",
        "type=1",
        "web_location=1315875",
        f"wts={date}"
    ]
    ee = '&'.join(en)
    string = ee + "ea1db124af3c7062474693fa704f4ff8"                 #常数为H
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    w_rid = MD5.hexdigest()
    return w_rid

6)云图绘制
df['reply'] 为自己设的字段名,font_path='C:/Windows/Fonts/simhei.ttf' 为字体路径(可通用)

def Cloud():           #生成词云,技术不行
    df = pd.read_csv('data.csv')
    text = " ".join(review for review in df['reply'])
    wordcloud = WordCloud(width=2000, height=2000, font_path='C:/Windows/Fonts/simhei.ttf',
                          background_color='white', min_font_size=10).generate(text)
    plt.figure(figsize=(20,20), facecolor=None)
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.tight_layout(pad=0)
    plt.show()

img

7)源代码与运行视频

  • 经过微小的处理(微小处理请见错误分析)我们就可以得到源代码了
import requests
import re
from bs4 import BeautifulSoup
import csv
import hashlib
import time
from urllib.parse import quote
import pandas as pd
from wordcloud import WordCloud
import matplotlib.pyplot as plt

def datasolve(next_page, date, w_rid, flag1, oid):
    headers = {
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0"
    }
    # I am a teapot
    url = 'https://api.bilibili.com/x/v2/reply/wbi/main'
    if flag1:
        #常数在换网站需要修改
        data = {
            'oid': oid,
            'type': '1',
            'mode': '2',
            'pagination_str': '{"offset":%s}' % next_page,
            'plat': '1',
            'web_location': '1315875',
            'w_rid': w_rid,
            'wts': date,
        }
    else:
        #换网站需要修改
        data = {
            'oid': oid,
            'type': '1',
            'mode': '2',
            'pagination_str': next_page,
            'plat': '1',
            'web_location': '1315875',
            'w_rid': w_rid,
            'wts': date,
        }
    response = requests.get(url, params=data, headers=headers)
    #print(response.status_code)
    json_data = response.json()      #b站的评论响应内容被{}包裹起来,故用json
    #print(json_data)
    all_reply = json_data['data']['replies']

    #提取数据所在列表,字典取值一层一层往下
    for reply in all_reply:
        message = reply['content']['message'].replace('\n', ',')    #去掉换行符
        like = reply['like']
        datalist = {
            'reply': message,
            'like': like,
        }
        savedata(datalist, flag1)
        flag1 = False
        print(datalist)

    #下一页请求参数
    next_offset1 = json_data['data']['cursor']['pagination_reply']['next_offset']
    #可恶啊,真不知道为什么爬下一页的pagination_str会少掉‘\‘,(很多#print都是在排查错误),所以用正则提取数字重构一次
    #所以也多了好多条件语句,可能可以精简,但我累了

    next_offset_num = re.findall('cursor\":(\\d+)', next_offset1)[0]      #\d没有,要\\d,好像是被解释成Unicode字符
    next_offset = '{"offset":"{\\"type\\":3,\\"direction\\":1,\\"Data\\":{\\"cursor\\":%s}}"}' % next_offset_num
    #print(next_offset)
    return next_offset


    # html = response.text     # soup不能直接接收response
    # soup = BeautifulSoup(html, "html.parser")
    # all_rep = soup.find_all("span",class_="reply-content")
    # for rep in all_rep:
    #     print(rep.string)


def Hash(date, next_page, flag, oid):
    #获取w_rid的值
    if flag:
        next_offset = '{"offset":%s}' % next_page
        #print(next_offset)
        #print(quote(next_offset))
    else:
        next_offset = next_page
        #print(next_offset)
        #print(quote(next_offset))

    # 换网站需要修改
    en = [
        "mode=2",
        f"oid={oid}",
        f"pagination_str={quote(next_offset)}",
        "plat=1",
        "type=1",
        "web_location=1315875",
        f"wts={date}"
    ]
    ee = '&'.join(en)
    string = ee + "ea1db124af3c7062474693fa704f4ff8"
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    w_rid = MD5.hexdigest()
    #print(w_rid)
    return w_rid


def savedata(datalist, flag):
    if flag:
        fe = open('data.csv', 'w', encoding = 'utf-8', newline='')
        csv_writer = csv.DictWriter(fe, fieldnames=[
            'reply',
            'like',
        ])      #字段名,即表头,根据获取数据
        csv_writer.writeheader()
        csv_writer.writerow(datalist)
    else:
        fe = open('data.csv', 'a', encoding='utf-8', newline='')
        csv_writer = csv.DictWriter(fe, fieldnames=[
            'reply',
            'like',
        ])
        csv_writer.writerow(datalist)


def Cloud():           #生成词云,技术不行
    df = pd.read_csv('data.csv')
    text = " ".join(review for review in df['reply'])
    wordcloud = WordCloud(width=2000, height=2000, font_path='C:/Windows/Fonts/simhei.ttf',
                          background_color='white', min_font_size=10).generate(text)
    plt.figure(figsize=(20,20), facecolor=None)
    plt.imshow(wordcloud)
    plt.axis("off")
    plt.tight_layout(pad=0)
    plt.show()

if __name__ == "__main__":  # 当程序执行时
    # 调用函数
    oid = input("请输入oid:")          #可能只有oid变了
    next_page = '""'
    flag1 = True
    for page in range(1, 11):     #可修改页数
        print("正在爬取第(",page,")页")
        date = int(time.time())
        w_rid = Hash(date, next_page,flag1, oid)
        #print(w_rid)
        next_page = datasolve(next_page, date, w_rid, flag1, oid)
        flag1 = False
    Cloud()

3. 实验过程中遇到的问题和解决过程

  • 问题1:天真的我想直接用soup去爬,结果毛都没有

img

问题1解决方案:通过观看教程,使用json进行爬取

  • 问题2:多页查找时算出的w_rid不对,一直权限不足

  • 问题2解决方案:很多#print都是在排查错误,不知道为什么爬下一页的pagination_str会少掉‘\‘,所以用正则提取数字重构一次

  • 问题3:正则表达式’\d‘出错

  • 问题3解决方案:要‘\d’,好像是被解释成Unicode字符

  • 问题4:重构后只有第一个可以,后面还不行

  • 问题4解决方案:使用flag进行判断,因为第一个无需重构,但在hash中要修改,还有在data中不是第一个的也要

  • 问题5:文件里写完一个覆盖一个

  • 问题5解决方案:用flag进行判断,w 还是a

实验体会

多了好多条件语句,可能可以精简,但我真的改不动了,这个加密是真的烦,一点不同就跳不到下一页,原本是想做舆情分析的,但找不到好的参考,所以转做词云了;做完这个实验感觉长脑子了,对爬虫更加理解,深深感受到爬虫速度之快,还有python的库好多、有好多函数。也学会了故障检测,怎么找错。

课程总结

1.老师很好,人帅心善;
2.对python的学习更加深入了,也理清了一些概念,如面向对象与面向过程的区别
3.老师的讲课内容非常生动形象,举得例子也很贴合,便于我们理解
4.建议:我觉得可以进行一个翻译,选取一个C语言的程序,让我们用这节课所学将它转成python实现同样的功能,加深印象

参考资料

...全文
192 回复 打赏 收藏 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
下载代码方式:https://pan.quark.cn/s/054d920fa1d1 《学生博客信息管理系统详解》 学生博客信息管理系统是一款针对教育环境而设计的简化博客平台,其主要目的在于协助教师进行学生博客信息的监控与管理,并对学生的作业得分进行记录。该系统具备方便的查询和访问特性,能够显著提升教学活动中的信息传递效率,并且为学生构建了一个展示个人能力、交流知识的平台。 1. **系统架构** 学生博客信息管理系统大概采用了常规的三层架构,涵盖了表现层(用户交互界面)、业务逻辑层(负责业务规则的执行)以及数据访问层(负责与数据库进行数据交换)。此类架构有利于代码的规范化管理与维护,有助于提升系统的可扩展性与代码复用率。 2. **功能模块** - **用户管理**:包含注册、登录、个人资料维护等功能,旨在保障用户能够安全地使用系统。 - **博客发布**:学生能够建立、修改和移除自己的博客文章,内容形式支持文本、图片、链接等多样化展示。 - **博客查询**:用户可根据关键词、作者等条件进行检索,以快速定位所需的博客内容。 - **作业评分**:教师可以检视学生的作业,实施评分与评语,系统将自动计算每次作业的平均分数与总得分。 - **统计分析**:系统提供数据统计功能,例如学生作业成绩的分布情况、活跃度最高的博主排行等,为教学决策提供数据依据。 3. **技术选型** 从提供的文件"博客管理.jar"可以推断,该系统可能基于Java语言开发,因为.jar是Java程序的可执行文件格式。同时,"博客管理_lib"可能包含了系统运行所需的第三方库资源,这些库资源或许包含了Spring框架、Hibernate用于数据持久化处理,或MyBatis用于数据库交互,...

110

社区成员

发帖
与我相关
我的任务
社区描述
人生苦短,我用Python!
python3.11 高校
社区管理员
  • blackwall0321
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧