Python数据爬取处理可视化,手把手全流程教学

news/2025/2/12 16:33:11/文章来源:https://www.cnblogs.com/h4o3/p/18579642

这篇博客中,选取openjudge网站上“百练”小组中的用户答题数据,作为材料进行教学

目录
  • 爬取主页面内容
  • 主页面内容提取
  • 需求数据爬取
  • 数据处理
  • 数据分析

网站地址:http://bailian.openjudge.cn/

使用到的Python包:requests、pandas、re、BeautifulSoup、time、matplotlib、seaborn




爬取主页面内容

使用requests库,来请求主界面的内容

import requests  # 导入requests库以便进行HTTP请求# 定义目标URL,指向需要获取内容的网页
url = "http://bailian.openjudge.cn/"
# 发送GET请求到指定的URL,并将响应保存到response变量中
response = requests.get(url)
# 初始化一个变量,用于保存HTML内容,默认为None
html_context_save = None# 检查HTTP响应的状态码,以确定请求是否成功
if response.status_code == 200:# 如果状态码为200,表示请求成功html_content = response.text  # 获取响应的HTML内容html_context_save = html_content  # 保存HTML内容到变量print(html_content)  # 打印HTML内容以供查看
else:# 如果状态码不为200,表示请求失败print("failed to receive the context")  # 输出失败信息

输出结果:拿到了主页面的HTML内容




主页面内容提取

F12进入控制台,在元素里找到“练习”,然后复制路径,得到
document.querySelector("#main > div.main-content > ul > li > h3 > a"),为我们所需要的子页面位置

提取子网页地址:

from bs4 import BeautifulSoup  # 导入BeautifulSoup库用于解析HTML文档# 使用 BeautifulSoup 库解析 HTML 内容,将其存储在 soup 变量中
soup = BeautifulSoup(html_context_save, 'html.parser')  # 传入HTML内容和解析器类型# 查找所有具有类名为 "current-contest label" 的HTML元素,并将结果存储在 contests 列表中
contests = soup.find_all(class_="current-contest label")  # 返回一个符合条件的标签列表import pandas as pd  # 导入pandas库用于数据处理和存储# 创建一个列表来保存未结束的比赛信息
data = []# 遍历每个比赛上下文以获取比赛信息
for context in contests:# 获取比赛标题,提取h3标签中的文本,并去除多余的空白符h3_text = context.find('h3').get_text(strip=True)# 查找h3标签下的锚标签(a)并提取其链接和文本a_tag = context.find('a')  # 查找第一个<a>标签a_href = a_tag['href'] if a_tag else None  # 若存在a_tag,提取其href属性a_text = a_tag.get_text(strip=True) if a_tag else None  # 若存在a_tag,提取其文本内容# 将比赛信息添加到列表中,用字典格式存储标题和链接data.append({'标题': a_text,  # 比赛标题'链接': f"http://bailian.openjudge.cn{a_href}",  # 完整链接,结合基础URL})# 将数据保存为DataFrame,以便于后续的操作和展示
df = pd.DataFrame(data)# 输出DataFrame,显示所有未结束的比赛信息
print(df)

获得了子网页的地址:




需求数据爬取

进入子网页,点击“状态”,看到上方网站页面多出了“status/”内容,内部的内容为我们需要的数据,可以看到关云长,赵子龙等同学都在提交作业。
在这里我们提取前67页数据作为数据分析的素材。
爬取内容存放在openjudge_data.csv文件

import requests  # 导入请求库,用于发送HTTP请求
from bs4 import BeautifulSoup  # 导入BeautifulSoup库,用于解析HTML文档
import pandas as pd  # 导入Pandas库,用于数据处理和保存
import time  # 导入时间库,用于延迟请求# 定义函数从指定的链接和页码获取数据
def fetch_data(contest_url, page_number):try:# 发送GET请求,并设置请求参数为页码response = requests.get(contest_url, params={'page': page_number}, timeout=10)response.raise_for_status()  # 检查请求是否成功return response.text  # 返回页面的HTML文本except Exception as e:# 如果请求失败,输出错误信息print(f"Error accessing {contest_url}?page={page_number}: {e}")return None  # 返回None表示获取数据失败# 定义函数从HTML页面中解析所需的数据
def parse_page(html, row_title):soup = BeautifulSoup(html, 'html.parser')  # 解析HTML文档problem_status_table = soup.find(id='problemStatus')  # 找到状态表格if not problem_status_table:return []  # 如果没有找到表格,返回空列表rows = problem_status_table.find_all('tr')[1:]  # 获取所有表格行,跳过表头status_data = []  # 用于保存当前页面的状态数据for tr in rows:  # 遍历每一行数据# 提取每列的数据并去除多余空白submit_user = tr.find('td', class_='submit-user').text.strip()course_class = tr.find('td', class_='className').text.strip()problem_title = tr.find('td', class_='title').text.strip()result = tr.find('td', class_='result').text.strip()memory = tr.find('td', class_='memory').text.strip()spending_time = tr.find('td', class_='spending-time').text.strip()code_length = tr.find('td', class_='code-length').text.strip()language = tr.find('td', class_='language').text.strip()# 提取提交时间的绝对和相对格式date_element = tr.find('td', class_='date').find('abbr')absolute_time = date_element['title'] if date_element and 'title' in date_element.attrs else Nonerelative_time = date_element.get_text(strip=True) if date_element else None# 将每次提交的数据添加到列表中status_data.append({'提交人': submit_user,'班级': course_class,'题目': problem_title,'结果': result,'内存': memory,'时间': spending_time,'代码长度': code_length,'语言': language,'提交时间(相对)': relative_time,'提交时间(绝对)': absolute_time,'标题': row_title  # 将标题信息也存入数据中})return status_data  # 返回解析后数据的列表# 用于存储所有抓取到的结果
all_status_data = []# 假设 df 已经被定义,并包含所有比赛的链接和标题
for index, row in df.iterrows():contest_url = row['链接'] + 'status/'  # 生成当前比赛的状态页面链接page_number = 1  # 初始化当前页码previous_data = None  # 存放上一次抓取的数据,用于比较while page_number <= 500:  # 设置页码限制为500html = fetch_data(contest_url, page_number)  # 获取当前页面的HTML内容if html is None:break  # 如果获取失败,则退出循环# 解析当前页面的数据status_data = parse_page(html, row['标题'])if not status_data:break  # 如果没有数据,说明已到最后一页,退出循环# 检查当前页的数据是否与上一页相同if previous_data is not None and status_data == previous_data:print(f"第 {page_number} 页数据与之前相同,跳出循环。")break  # 如果数据相同,说明没有新数据,退出循环all_status_data.extend(status_data)  # 将当前内容添加到总的数据列表print(f'爬取的结果(第 {page_number} 页):{status_data}')  # 输出当前页爬取的数据previous_data = status_data  # 更新最后获取的数据# 控制请求速度,避免被目标网站封锁time.sleep(0.1)  # 每次请求后短暂延迟0.1秒page_number += 1  # 页码自增1,准备抓取下一页# 将抓取的数据转换为Pandas DataFrame,并保存为CSV文件
status_df = pd.DataFrame(all_status_data)# 输出整个DataFrame以确认爬取结果
print("爬取完成,数据如下:")
print(status_df)# 保存为CSV文件,设置不输出索引,编码为utf-8
status_df.to_csv('openjudge_data.csv', index=False, encoding='utf-8')

数据处理

读取文件:

import pandas as pd
df = pd.read_csv("openjudge_data.csv")

检查缺失值:

print(df.isnull().sum())

删去缺失值最多的列:

df.drop(columns=['班级'], inplace=True)

删除“时间”,“内存”,“代码长度”列为空值的行:

df.dropna(subset=['时间','内存','代码长度'], inplace=True)

数据类型转换:

# 确保'内存'列是字符串类型,并使用str.replace()方法去除字符串中的'kB'
# 然后,将处理后的字符串转换为浮点数类型
df['内存'] = df['内存'].astype(str)  # 将'内存'列转换为字符串类型,以确保后续操作不会出错
df['内存'] = df['内存'].str.replace('kB', '', regex=False)  # 去掉'kB'后缀
df['内存'] = df['内存'].astype(float)  # 将字符串类型的'内存'转换为浮点数类型# 确保'时间'列是字符串类型,并使用str.replace()方法去除字符串中的'ms'
# 然后,将处理后的字符串转换为浮点数类型
df['时间'] = df['时间'].astype(str)  # 将'时间'列转换为字符串类型
df['时间'] = df['时间'].str.replace('ms', '', regex=False)  # 去掉'ms'后缀
df['时间'] = df['时间'].astype(float)  # 将字符串类型的'时间'转换为浮点数类型# 确保'代码长度'列是字符串类型,并使用str.replace()方法去除字符串中的' B'
# 然后,将处理后的字符串转换为整数类型
df['代码长度'] = df['代码长度'].astype(str)  # 将'代码长度'列转换为字符串类型
df['代码长度'] = df['代码长度'].str.replace(' B', '', regex=False)  # 去掉' B'后缀
df['代码长度'] = df['代码长度'].astype(int)  # 将字符串类型的'代码长度'转换为整数类型# 显示处理后的DataFrame的前几行
df.head()  # 通过head()方法查看DataFrame的前几行以验证处理效果



数据分析

统计总的提交状况:

print(df.groupby('结果').size())

查看“关云长”用户的提交情况:

# 将“关云长”提交的数据筛选出来
submissions = df[df['提交人'].str.contains("关云长")]# 计算Accepted和Wrong Answer提交的数量
status_counts = submissions['结果'].value_counts()# 打印各状态的提交数量
print("\n提交状态统计:")
print(status_counts)# 统计每次提交的时间和所需内存与时间
submissions['内存'] = submissions['内存']
submissions['时间'] = submissions['时间']# 计算平均内存和平均时间
average_memory = submissions['内存'].mean()
average_time = submissions['时间'].mean()print(f"\n关云长的平均内存使用: {average_memory:.2f} kB")
print(f"关云长的平均运行时间: {average_time:.2f} ms")

数据可视化:

import matplotlib.pyplot as plt
import seaborn as sns# 设置绘图风格
sns.set(style="whitegrid")  # 设置背景风格为白色网格型# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置字体为 SimHei,用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号# 1. 提交状态的条形图
plt.figure(figsize=(8, 6))  # 设置图形的宽度和高度
sns.barplot(x=status_counts.index, y=status_counts.values, palette="viridis")  # 绘制条形图
plt.title("关云长 提交状态统计")  # 设置图表标题
plt.xlabel("结果")  # 设置X轴标签
plt.ylabel("提交数量")  # 设置Y轴标签
plt.xticks(rotation=45)  # X轴标签旋转45度以避免重叠
plt.show()  # 显示图表# 2. 内存和时间的分布图
plt.figure(figsize=(12, 6))  # 设置整体图形的宽度和高度# 内存分布
plt.subplot(1, 2, 1)  # 创建一个1行2列的子图,选择第1个位置
sns.histplot(submissions['内存'], bins=10, kde=True, color='blue')  # 绘制内存分布直方图
plt.title("关云长 提交内存分布")  # 设置子图标题
plt.xlabel("内存 (kB)")  # 设置X轴标签
plt.ylabel("提交数量")  # 设置Y轴标签# 时间分布
plt.subplot(1, 2, 2)  # 选择第2个子图
sns.histplot(submissions['时间'], bins=10, kde=True, color='orange')  # 绘制时间分布直方图
plt.title("关云长 提交时间分布")  # 设置子图标题
plt.xlabel("时间 (ms)")  # 设置X轴标签
plt.ylabel("提交数量")  # 设置Y轴标签plt.tight_layout()  # 自适应调整子图间的间距以使图表更整齐
plt.show()  # 显示整个图形

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/844536.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nuxt.js 应用中的 render:island 事件钩子

title: Nuxt.js 应用中的 render:island 事件钩子 date: 2024/12/1 updated: 2024/12/1 author: cmdragon excerpt: 在 Nuxt.js 中,render:island 钩子允许开发者在构建“岛屿”HTML之前进行处理和修改。此钩子为实现复杂的客户端交互和动态内容提供了基本支持,特别适合与…

linux 软连接使用

转载请注明出处:在Linux系统中,软连接(Symbolic Link)是一种特殊类型的文件链接,类似于Windows系统中的快捷方式。它允许用户通过一个文件路径访问另一个文件或目录,而不需要拥有原始文件或目录的实际拷贝。软连接是通过文件名来引用文件或目录,而不是通过它们的物理位置…

2024.11多模态学习月报

2024年11月学习月报 SegEarth-OV SegEarth-OV是面向遥感图像的开放词汇语义分割模型。SegEarth-OV的模型架构为CLIP,并且采用SimFeatUp作为上采样器。由于FeatUp中的上采样器和下采样器都是可学习的,所以难以保证HR特征完整。于是在SimFeatUp中引入了一个额外的图片重建损失来…

湖北大学新星杯web-misc---wp from sorin

差一点akweb,算是一个小遗憾 WP----sorin 1\Ezhttp Exp如下:访问:得到flag 2\Robots Leak 直接git-dumper下载后, Git log Git showGit stash list 后pop1查看.flag.exe即可 3\Random_Door 爆破flag{{int(1-100)}}.php利用伪协议php://filter/convert.base64-encode/resour…

exe 安装为 windows服务

下载地址 http://www.nssm.cc/download只有一个exe文件放在 C:\Windows\nssm.exe命令行执行 nssm install

UI自动化基础知识

一、UI自动化测试介绍1、什么是自动化测试概念:由程序代替人工进行系统校验的过程1.1自动化测试能解决的问题?回归测试 (冒烟测试)针对之前老的功能进行测试,通过自动化的代码来实现。 针对上一个版本的问题的回归兼容性测试:web实例化不同的浏览器驱动相当于对不同的浏览器进…

AI大模型系列之一:大模型原理科普(深度好文)

.MathJax, .MathJax_Message, .MathJax_Preview { display: none }AI大模型系列之一:大模型原理科普(深度好文)目录 认识AI大模型家族 AI是什么? 机器学习是什么? 机器学习有哪些分支? 什么是强化学习? 深度学习属于哪一类学习? 生成式AI和深度学习是什么关系? 大语言…

Web自动化002-Web自动化元素定位及浏览器的相关操作

Web自动化002-Web自动化元素定位及浏览器的相关操作Web自动化元素定位及浏览器相关的操作标签=元素1.元素定位首先需要选择要被定位的元素(锁定被操作的元素)然后才能对元素进行具体操作(具体的操作方法) selenium第三方库中提供了两类定位的方法 find_element----->返回…

2024-2025-1 20241314 《计算机基础与程序设计》第十周学习总结

2024-2025-1 20241314 《计算机基础与程序设计》第十周学习总结 作业信息这个作业属于哪个课程 2024-2025-1-计算机基础与程序设计这个作业要求在哪里 2024-2025-1计算机基础与程序设计第十周作业这个作业的目标 信息系统 数据库与SQL 人工智能与专家系统 人工神经网络 模拟与…

ElementUI上传多图的操作

一、使用ElementUI上传多图 1.在项目中引入ElementUI的相关组件<el-form-item label="详细图片"><!-- <el-upload:action="api_url+/api/upload.php"list-type="picture-card":file-list="picsUrl":on-success="hand…

十六进制色彩--代码参考表

来源:http://www.allfid.com/control/inpx/color.htm

概念

一、函数 1、计算机的函数,是- 一个固定的一个程序段,或称其为一个子程序,它在可以实现固定运算功能的同时还带有一入口和一个出口,所谓的入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数值代入子程序,供计算机处理,所谓出口,就是指函数的计算结果,也…