Python爬虫之正则 & BeautifulSoup4解析HTML

时间:2022-11-01 09:17:23

目录:

1.前言

  • 事务管理类app的项目,需要后台导入某工学生的个人课表,选择用python抓取并解析HTML的方式
  • 如果习惯了实战一遍、优先解决问题,再去学习技术细节,那本文应该对你有所帮助,因为我就是这样干的
  • 本篇文章记录 解析HTML 获取课程内容的部分,知识点包括:
    • 编码演变的历史
    • 正则表达式
    • BeautifulSoup4库的使用
  • 登录系统并抓取HTML页面参见: python爬虫之urllib登录抓取HTML页面
  • GitHub下载地址:Python源码与解析的HTML文件

2.编码演变历史

为了弄清下面几个问题,需要对编码演变的历史做必要的介绍,更详细介绍可以在本文的参考资料中查看

  • 为什么UTF-8格式的代码,跨平台复制粘贴,有时会出现中文注释乱码,甚至没有中文代码也无法正常运行,在转成GB2312或者Unicode就搞定
  • 做正则匹配时,错误的编码格式会导致中文匹配失败
  • BeautifulSoup的构造函数应选择哪种编码

2.1 Charset & Character Encoding

  • 字符(Character):是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等
  • 字符集(Charset):是多个字符的集合,每个字符集包含字符个数不同,常见字符集有ASCII,GB2312,BIG5,Unicoede,UTF-8等
  • 字符编码(Character Encoding):为了让计算机能够识别和存储字符集所使用的法则,使字符集和数字系统建立对应关系
    下面按发展史简要介绍几种主要字符集及其编码

2.2 ASCII字符集与编码

  • ASCII字符集:包括控制字符(回车,换行等),可显示字符(英文字符,阿拉伯数字等)
  • ASCII编码:将ASCII字符集转换为计算机可存储计算的数的规则,
  • 特点:这是较早出现、现今最通用的单字节编码,即8 bits表示一个字符.举例,查ASCII码对照表知,a的ASCII码值为97(十进制),二进制表示为01100001(占用一字节)
  • 缺点:显然ASCII能显示的字符仅限于英语,扩展后的ASCII也只解决了部分西欧语言的显示,Macbook就放弃了ASCII而采用后面提到的Unicode

2.3 中文字符集与编码

  • 中国用上了计算机之后,当然要为中文做一套计算机编码规则,采用多字节编码,即每个字符由1个、2个或4个字节变长编码表示
  • GB2312:中国国家标准简体中文字符集,GB2312简体中文编码表GBK包含GB2312,GB18030包含GBK,增加了一些罕见汉字
  • BIG5:繁体中文集

2.4 大一统的Unicode

  • 各个国家的文字都有一套自己的编码,乱成一锅粥是多么可怕的一件事情。因此Unicode字符集与编码就应运而生
  • Unicode是基于通用字符集UCS(Universal Character Set)发展而来,是计算机可以显示世界上数十种文字

2.5 UTF-8编码

  • UTF-8是针对Unicode的变长字符编码,注意UTF-8不是字符集,是一种编码
  • 优点:变长编码可以提高了编码效率。通俗的说,传输相同文字内容时,UTF-8编码比Unicode原先的编码方式(等长编码)使用较少的码字

2.6 Python中的编码

在python2.7中,有unicode类型

str_unicode = u'测试' #显示定义unicode类型变量
str_gb = str_unicode.encode('gb2312') #将unicode对象以gb2312编码
str_utf = str_unicode.encode('utf-8') #将unicode对象以utf-8编码
str_origin = str_gb.decode('gb2312') #将gb2312编码的字符串解码获取unicode

在python3中,取消了unicode类型,取而代之的是:使用unicode字符的字符串类型str。编码前是字符串类型str,编码后是字节流类类型bytes。

str_unicode = '测试' #字符串类型变量,使用unicode字符
str_gb = str_unicode.encode('gb2312') #将字符串类型对象以gb2312编码为bytes类型对象
str_origin = str_gb.decode('gb2312') #将gb2312编码的字符串解码,获取字符串类型对象

3.正则表达式

此处只说明本项目中用到的几个正则匹配规则,更多正则知识参见python正则表达式对照表,当字典查就行

#匹配连续的多个中文字符,这是unicode编码的中文范围
ur"[\u4e00-\u9fa5]+"
#在py2.7下,可如下形式调用,被正则内容必须为unicode编码,否则应decode
re.match(ur"[\u4e00-\u9fa5]+",u"搞笑le")
re.match(ur"[\u4e00-\u9fa5]+",str_utf.decode('utf-8'))

#匹配课表中两种表示上课时间的方式,#吐槽一下我工的课表真不统一,unicode编码
#第1种:周一第3,4节{第8-16周}
#第2种:第8-14周|2节/周
ur"第\d+-\d+周\|\d节\/周|周[一二三四五六日]第[,\d]+节{第[-\d]+周[\|单双周]*}"

r">第\d+节</td>" #匹配第n节,utf-8编码

# [] 用来指定一个字符集 [abc] 表示 abc其中任意一个字符符合都可以
# - 表示范围
# \d 匹配一个数字字符。等价于[0-9]
# + 表示将其前面的字符重复1或者多次
# {} 可以表示范围

4.BeautifulSoup4库的使用

  • Beautiful Soup4 官方文档,安装、使用、对象类型、遍历文档树一应俱全。相信我,快速浏览一下 当字典用 就行了
  • 下面按本项目parserCourse.py中,对bs4语句的使用顺序做介绍,用过一遍知道 使用流程,就知道该怎么查了
  • 建议在 前言GitHub源码链接中下载parserCourse.py和待解析的html文件,如果对你有帮助,就请给个star吧
from bs4 import BeautifulSoup #记得pip install beautifulsoup4

#第一步:创建beautifulsoup对象
#构造第1个参数是待解析的内容,第2参数指定使用python标准库解析器
#文档会被转成unicode编码
htmlPage = open("./formatCourse.html")
soup = BeautifulSoup(htmlPage, "html.parser")

#第二步:定位,找到id为Table1的table标签,就是这份html中包含课程内容的部分
#find函数返回'bs4.element.Tag'对象,bs4有4种对象(往下看先)
#此处返回了<table></table>标签内的所有内容
tagTable = soup.find('table', id='Table1')

#第三步:按<tr></tr>标签分割课表,并以list输出
tbodyContents = tagTable.tbody.contents
# 1)element.Tag的用法,获取<table>标签下的<tbody>标签及其内容
tabTable.tbody # 依然返回element.Tag对象
# 2)遍历文档树的知识点:.contents属性将Tag的子节点以列表的方式输出
tagTable.tbody.contents # 将<tbody>的内容(子节点)以list输出
# 3)过滤:list中有一些换行符需要过滤掉,只保留Tag类型的元素,即由<tr></tr>构成的元素
tbodyContents = filter(lambda x: type(x) == element.Tag, tbodyContents)

#第四步:构造好匹配课程名、上课时间、教师姓名、教室的正则表达式后
#按照每个<tr></tr>进行遍历,前两个元素是无用的略过
for lessonN in xrange(2, len(tbodyContents)):
#类似第三步分割过滤出<td></td>之后,遍历每行正则出课程信息
trContent = filter(lambda x: type(x) == element.Tag,tbodyContents[lessonN].contents)
trContent[rowN].text #表示取tr标签中的字符

#剩下的部分是遍历并正则匹配出课程信息,看注释就能看懂,不赘述。
#注意:正则匹配时,不确定字符串是什么编码,最好显示的转换一下

5.相关文章阅读

python正则表达式对照表
python爬虫之urllib登录抓取HTML页面

参考:

吴秦:字符集和字符编码
Beautiful Soup4 官方文档

如果对你有所帮助,就请点个赞吧 (^-^)