Makzan
Makzan

我管理世界職業技能競賽之網站技術項目、舉辦本地設計與開發賽事、開課分享技術心得。一個用網頁來表達自己的作家。

Python|使用Pandas 读取澳门公职人员开考列表及进行文字处理与筛选

此篇,我们借助澳门公职人员开考列表,示范了从网络中取得内容后,如何通过split 字串处理抽出内容、通过apply 批量套用函数、使用groupby 计算基统计数值、及使用== 筛选数据。

大家好,来继续Python Pandas 数据处理系列介绍。

我在使用Pandas 的read_html 读取网页上的表格内容中介绍了不同的表格读取例子,但读取后未进一步探讨如何处理。在这篇中,我们以澳门公职人员开考列表之网页作为例子,从读取至分柝文字及搜寻筛选开考资讯。

在这个例子中,我们会学到Python 及Pandas 的以下几方面技巧:

  • 当read_html 不成功时,使用requests 配搭取得网页内的<table> 元素内容
  • 使用Split 将字串切开
  • 使用apply 加split 将一行内容拆成多栏,方便搜寻筛选
  • Python 列表Slicing 取得头N 项数据

目标网站分析

目标网站: https://concurso-uni.safp.gov.mo/

https://concurso-uni.safp.gov.mo/

在上述网站中,中间有三个开考表格,我示范的目标是第三个:「专业或职务能力评估开考(2021/7/1后开展的)」。

中间三个是<table> 表格,即我们期望使用read_html 取得网站后,最少有三个DataFrame 表格,可能会因为网站其他地方也使用了<table> 而有更多,但最少应为3 个。

使用requests 配搭取得网页内的<table> 元素内容

在使用Pandas 的read_html 读取时,有些情况会出现表格就在那𥚃,但使用pandas read_html 时会报错,说找不到<table> 元素,此时大约有以下可能,可能性由高至低排列:

  1. 网站内容目测是表格,但背后的HTML 代码其实不是表格。
  2. 网站的<table> 数据是JavaScript 动态加载的,来源是JSON。
  3. 网站的<table> 数据是JavaScript 动态载入的,而来源亦是<table> HTML 格式。
  4. 网站在pandas 存取时会屏蔽,不返回数据。

这个网站就是原因4,当我们尝试用以下代码时,read_html 会报错说找不到<table> 元素。因为Pandas 的存取会内置使用urllib3,所以网站会有可能针对这个字眼来的客户端进行屏蔽。

 import pandas as pd
url = "https://concurso-uni.safp.gov.mo/"

tables = pd.read_html(url) # 错误

解决方案是使用其他存取方法取得此版网页的HTML,再把HTML 交给read_html 分析成DataFrame 数据。

而其他方法,包括使用BeautifulSoup 或Selenium。前者是下载网站的HTML 内容并分析生成元素结构树,然后我们可以通过CSS 选取器按条件取得当中的内容,是通用的网站爬虫方式。而后者,Selenium 是控制浏览器的自动化测试工具,可以用编程控制浏览器载入网页,包括用JavaScript 动态生成的网页,甚至自动填表或进行介面操作等。

由于BeautifulSoup 是下载内容的机制而Selenium 是启动浏览器自动化机制,两者运行速度相差甚远,所以我们会优先使用BeautifulSoup 下载HTML 内容,若失败时,或必须自动化操作始能存取目标数据时,才会使用Selenium。

以下代码,我们尝试使用requests 读取网站,再将读取得的HTML 源代码直接交予read_html,印出找到的表格数量,得到4 个表格。

 import pandas as pd
import requests

url = "https://concurso-uni.safp.gov.mo/"
res = requests.get(url)
tables = pd.read_html(res.text)
print(len(tables))

取得的4 个数据表格中,当中第三个,就是我们需要的。所以df = tables[3] 然后列印出来望望,得出以下数据。

设定栏名称

Pandas 的操作,多以栏作为思考。我们会检视数据的栏位,只保留需要的,反为这些原始数据的栏位重新命名,以适合我们一会使用。

由于这个表格只有两栏,我们可以直接用df.columns 设定。例如:

 df.columns = ["日期", "内容"]

设定后,我们可以通过df["内容"] 来存取整栏的内容。每栏数据的类型为Pandas 的Series (系列)类型。

例如以下为df["内容"] 的结果,可以列出每行的内容。

分柝文字内容,得出部门、范畴、开考职阶

从图中可以看出,这些内容有待清理。这些内容有一定模式,就是一开始是部门名称,伴随一个空格,紧接是开考范野,再伴随另一个空格,紧接是开考职阶。我们可以利用split,对每一行的内容按空格切开,得出列表。

例如,假若有这句字串「交通事务局机电工程范畴第一职阶二等高级技术员」

 example = "交通事务局机电工程范畴第一职阶二等高级技术员"
example.split(" ")

而Python 中,我们可以同时将N 个变量名称对应N 个列表项目,会一一储存对应的项目数据,我们称这个动作为unpack。例如,以下split 范例,我们可以分别将三个值放到三个变量中去。

 example = "交通事务局机电工程范畴第一职阶二等高级技术员"
部门, 范畴, 职阶= example.split(" ")

这样就可以分别将列表的三个值分配到三个独立的变量中。要注意的是使用这招式,必须前后数量匹配才可。如果要处理的文字可能有额外空格,我们可以加上[:3] 来确保只取头三个值。

 example.split(" ")[:3] 

关于[:3],这是列表切片,详情可以参考: Python 使用列表切片List Slicing 取得列表的范围数值

整栏处理的apply 函数

上述就是split 的基本用法。但我们不会这样逐列逐列处理。于Pandas 中,我们是以栏位思考的,可以用apply 来为整栏的数据套用上述的split 函数。

 df["内容"].apply(lambda x: x.split(" ")[:3])

关于lambda 的使用,可以参考@YCJHUO在Pandas 中,如何使用lambda 以及客制化boolean 值

以下为运行结果,可以看到每行内容已成功抽取为三个独立的值。

但我们需要的是生成三栏独立的栏,所以基于上述代码,需要再套用apply(pd.Series),其原理是当Pandas 生成栏(Series)时,会自动将多重列表、字典(Dict)等拆分成栏。所以更新后的代码如下:

 df["内容"].apply(lambda x: x.split(" ")[:3]).apply(pd.Series)

从结果可见,今次我们成功从每行的一串文字抽取出三项资讯,并分别得出三栏。

将此三栏加到原DataFrame 数据表上,数据清理阶段便暂告完成。

 df[["部门", "范畴", "职阶"]] = df["内容"].apply(lambda x: x.split(" ")[:3]).apply(pd.Series)
数据清理完成

数据使用

于这份数据中,我们可以做一些基本查询,例如看一看数据中共涉及哪些部门及哪些范畴。

 set(df["部门"])
列出涉及部门
set(df["范畴"])
列出涉及范畴

我们亦可以通过group by,按范畴分类,数数有多少笔记录,或倒转按部门分类,数数每个部门有多少笔记录。

按范畴分类:

 df.groupby(by="范畴")["内容"].agg(len)

按部门分类:

 df.groupby(by="部门")["内容"].agg(len)

groupby 是将某一栏的数据变成索引,而关连此栏每一项数据的,就按我们提供的函数整合。常见的整合有.agg(np.sum) 加总、.agg(np.mean) 取平均值、及上述代码使用的.agg(len) 数数目。

使用groupby 后,可以按所属数值的数据进行加总、数数、取平均值等运算

筛算特定数据

我们可以将特定值作为筛选条件,通过某一栏位与值的比较,我们会得出一个True/False 的Boolean 布林值序列(Series),再将此序列放回DataFrame,就以只显示True 的那列,达至筛选效果。

 mask = df["范畴"]=="电机工程范畴"
df[mask]
df[mask] 的原理

亦可以随时将觉得有用的DataFrame 数据输出成Excel:

 df[mask].to_excel("最近电机工程范畴开考列表.xlsx")

总结

此篇,我们借助澳门公职人员开考列表,示范了从网络中取得内容后,如何通过split 字串处理抽出内容、通过apply 批量套用函数、使用groupby 计算基统计数值、及使用== 筛选数据。

今日的例子以文字处理为主,之后我们再以其他例子探讨Pandas 的其他应用,如数值处理、移动平均线运算、文字的正规表达成抽出等等。

Pandas 系列文章

  1. 使用Pandas 的read_html 读取网页上的表格内容
  2. 本文:使用Pandas 读取澳门公职人员开考列表及进行文字处理与筛选


— 麦诚Makzan,2022-01-14。


我是麦诚轩(Makzan) ,除了正职外,平常我要么办本地赛与办世界赛,要么任教编程与网站开发的在职培训。现正转型将面授培训内容写成电子书、网上教材等,至今撰写了7 本书, 2 个视频教学课程。

如果我的文章有价值,请左下角👍🏻按赞支持,或订阅赞助我持续创作及分享。

麦诚Makzan


CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…
加载中…

发布评论