Python数据爬取处理可视化,手把手全流程教学
这篇博客中,选取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 None
relative_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: # 设置页码限制为500
html = 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() # 显示整个图形


Python数据爬取处理可视化,手把手全流程教学的更多相关文章
- 利用Python爬虫爬取指定天猫店铺全店商品信息
本编博客是关于爬取天猫店铺中指定店铺的所有商品基础信息的爬虫,爬虫运行只需要输入相应店铺的域名名称即可,信息将以csv表格的形式保存,可以单店爬取也可以增加一个循环进行同时爬取. 源码展示 首先还是完 ...
- 豆瓣读书top250数据爬取与可视化
爬虫–scrapy 题目:根据豆瓣读书top250,根据出版社对书籍数量分类,绘制饼图 搭建环境 import scrapy import numpy as np import pandas as p ...
- python+echarts+flask实现对全国疫情数据的爬取并可视化展示
用Python进行数据爬取并存储到数据库,3.15学习总结(Python爬取网站数据并存入数据库) - 天岁 - 博客园 (cnblogs.com) 通过echarts+flask实现数据的可视化展示 ...
- python实现人人网用户数据爬取及简单分析
这是之前做的一个小项目.这几天刚好整理了一些相关资料,顺便就在这里做一个梳理啦~ 简单来说这个项目实现了,登录人人网并爬取用户数据.并对用户数据进行分析挖掘,终于效果例如以下:1.存储人人网用户数据( ...
- 用Python介绍了企业资产情况的数据爬取、分析与展示。
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:张耀杰 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- 用python写一个豆瓣短评通用爬虫(登录、爬取、可视化)
原创技术公众号:bigsai,本文在1024发布,祝大家节日快乐,心想事成. @ 目录 前言 登录 爬取 储存 可视化分析 前言 在本人上的一门课中,老师对每个小组有个任务要求,介绍和完成一个小模块. ...
- python新冠疫情分析-世界疫情数据爬取
事情发展:1.毕业设计是关于疫情数据的可视化展示(基于java,需要做数据可视化,需要做管理员端对数据进行增删改查处理)2.飞起来速度学爬虫,参考了非常多资料,比如b站的黑马爬取(报错,就是在切片那里 ...
- 芝麻HTTP:JavaScript加密逻辑分析与Python模拟执行实现数据爬取
本节来说明一下 JavaScript 加密逻辑分析并利用 Python 模拟执行 JavaScript 实现数据爬取的过程.在这里以中国空气质量在线监测分析平台为例来进行分析,主要分析其加密逻辑及破解 ...
- Python爬虫 股票数据爬取
前一篇提到了与股票数据相关的可能几种数据情况,本篇接着上篇,介绍一下多个网页的数据爬取.目标抓取平安银行(000001)从1989年~2017年的全部财务数据. 数据源分析 地址分析 http://m ...
- 使用python爬虫爬取股票数据
前言: 编写一个爬虫脚本,用于爬取东方财富网的上海股票代码,并通过爬取百度股票的单个股票数据,将所有上海股票数据爬取下来并保存到本地文件中 系统环境: 64位win10系统,64位python3.6, ...
随机推荐
- qumu虚拟机启动后无法远程连接
通过 virsh 在启动 qemu 虚拟机,可以通过 VNC 访问虚拟机,但无法通过设置的 SSH 的外部映射端口登录.首先在宿主机上查看虚拟机(csv\tpm\name)的网络配置,可以看到端口映射 ...
- 使用 Performance API 实现前端资源监控
1. Performance API 的用处 Performance API 是浏览器中内置的一组工具,用于测量和记录页面加载和执行过程中的各类性能指标.它的主要用处包括: 监控页面资源加载:跟踪页面 ...
- Angular 18+ 高级教程 – 学以致用
前言 读这么多原理,到底为了什么?真实项目中真的会用得到吗? 你正在疑惑 "知识的力量" 吗? 本篇会给一个非常非常好的案例,让你感悟 -- 知识如何用于实战. 记住,我的目的是让 ...
- RxJS 系列 – 实战练习
前言 这篇主要是给一些简单例子, 从中体会 RxJS 在管理上的思路. Slide Down Effect with Dynamic Content 我在这篇 CSS & JS Effect ...
- CSS & JS Effect – Styling Input Radio
原生 Radio 的 Limitation <input type="radio" style="width: 25px; height: 25px; cursor ...
- JavaScript – Fetch
前言 上一篇 JavaScript – XMLHttpRequest 有提到 XMLHttpRequest 正在被 Fetch 取代,这篇就继续介绍 Fetch 吧. 参考 阮一峰 – Fetch A ...
- Angular – CLI
前言 一年半没有写 Angular 了. 现在又要开始写了. 复习过程中也顺便整理一下笔记呗. 介绍 CLI 是 Angular 的辅助工具. 输入一些 command 它会帮你 create 一些有 ...
- 痞子衡嵌入式:MCUBootUtility v6.3发布,支持获取与解析启动日志
-- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v5.3.0)发布过去一年了,期间痞子衡也做过三个版本更新,但不足以单独介绍.这一次痞子衡为大家带来了全新重要版本v6. ...
- 邀请参与 2022 第三季度 Flutter 开发者调查
自 Flutter 3 发布之后,我们在以移动端为中心到多平台框架的路线上稳步前行,用 Dart 2.17 的新语言特性帮助大家提升工作效率,并对核心工具进行了改进,让您在跨平台打造优秀体验时更加得心 ...
- Vue 文件流预览 PDF
Vue js // pdf 预览 export function download(id) { return request({ url: '/bbs/regtech/law/download?id= ...