Python 实现文本解析器

一、 内容介绍

本节实验我们学习使用 Python 来解析纯文本文件,并生成 HTML 页面。本节实验只是一个简单实现,支持了较少部分的 Markdown 语法,但通过实验中的解析实现思路,同学们可以完成更多更复杂的语法解析实现。

知识点

-Python 基本语法

-Python 面向对象编程

-yield 关键字

-正则表达式

-HTML 标记语言

二、实现原理及步骤

一共有文本块生成器、处理程序、规则、解析和运行与测试五个步骤,本课程中将创建以下的代码文件,每个文件的作用简介如下:

•util.py:实现文本块生成器把纯文本分成一个一个的文本块,以便接下来对每一个文本块进行解析

•handlers.py:为文本块打上合适的 HTML 标记

•rules.py:设计一定的规则来判断每个文本块交给处理程序将要加什么标记

•markup.py:对整个文本进行解析的程序

三、 源代码

1.util.py

# -*- coding: UTF-8 -*-
'''处理 TXT 文本,创建返回文本块的生成器
'''# 生成器函数,函数调用为生成器
# 调用此函数时,file 参数一定是 IOWrapper 对象,IOWrapper 对象是迭代器对象
# 该函数的作用是给文本文件的 IOWrapper 迭代器的末尾增加一个换行符
def lines(file):"""生成器,在文本最后加一空行"""for line in file:yield lineyield '\n'# 同上一个函数 lines ,它也是个生成器函数
# 调用此函数时,file 参数一定是 IOWrapper 对象,IOWrapper 对象是迭代器对象
# 函数的返回值是生成器,生成器的每次迭代都会返回一个文本块
def blocks(file):"""生成器,将 TXT 文件内容生成一个个单独的文本块,按空行分"""block = []# 使用 for 循环调用生成器for line in lines(file):# 如果不是空行if line.strip():# 将该行数据添加到 block 列表里block.append(line)# 如果是空行,且 block 列表里有内容# 这里可以看出 lines 生成器函数的作用了,如果最后一行不是空行# 那么最后一个文本块就不会作为 yield 的参数被生成器返回elif block:yield ''.join(block).strip()# 每次生成文本块后,要清空 block 列表block = []

2.handlers.py

# -*- coding: UTF-8 -*-
'''
HTML 文本处理类,用于打印各种 HTML 标签
'''class Handler:"""处理程序父类"""def callback(self, prefix, name, *args):# 获取处理方法,getattr 接收三个参数# 1、Python 对象# 2、属性字符串# 3、缺省值# 返回值为 Python 对象的属性值,如果没有此属性值,返回第三个参数method = getattr(self, prefix + name, None)# 调用处理方法,将其返回值作为当前函数的返回值if callable(method): return method(*args)def start(self, name):self.callback('start_', name)def end(self, name):self.callback('end_', name)# 参数 name 的值为过滤器名字,是字符串def sub(self, name):def substitution(match):result = self.callback('sub_', name, match)if result is None:result = match.group(0)return resultreturn substitutionclass HTMLRenderer(Handler):"""HTML 处理程序,给文本块加相应的 HTML 标记"""# 以下各个方法分别在符合条件时被调用,打印标签或标签的 text 值def start_document(self):print('<html><head><title>ShiYanLou</title></head><body>')def end_document(self):print('</body></html>')def start_paragraph(self):print('<p style="color: #444;">')def end_paragraph(self):print('</p>')def start_heading(self):print('<h2 style="color: #68BE5D;">')def end_heading(self):print('</h2>')def start_list(self):print('<ul style="color: #363736;">')def end_list(self):print('</ul>')def start_listitem(self):print('<li>')def end_listitem(self):print('</li>')def start_title(self):print('<h1 style="color: #1ABC9C;">')def end_title(self):print('</h1>')def sub_emphasis(self, match):return('<em>%s</em>' % match.group(1))def sub_url(self, match):s = ('<a target="_blank" style="text-decoration: none;''color: #BC1A4B;" href="{}">{}</a>')return s.format(match.group(1), match.group(1))def sub_mail(self, match):s = ('<a style="text-decoration: none;color: #BC1A4B;" ''href="mailto:{}">{}</a>')return s.format(match.group(1), match.group(1))def feed(self, data):print(data)

3.rules.py

# -*- coding: UTF-8 -*-
'''处理文本块的规则类,所有类均为单例模式,在程序运行时除了 Rule 每个类仅创建一个实例
'''class Rule:"""所有规则类的父类"""def action(self, block, handler):"""加标记,以下三行执行打印 HTML 标签的功能"""handler.start(self.type)    # 打印标签头handler.feed(block)         # 打印标签 text 部分handler.end(self.type)      # 打印标签尾return True                 # 打印完成,返回 Trueclass HeadingRule(Rule):"""一号标题规则,HTML 文件的一级标题规则(最大字号)<h1> 标签"""type = 'heading'    # 文本块类型def condition(self, block):"""判断文本块是否符合规则,返回值为布尔值 True 或 False"""return not '\n' in block and len(block) <= 70 and not block[-1] == ':'class TitleRule(HeadingRule):"""二号标题规则,次级标题规则,继承一号标题规则类 <h2> 标签"""type = 'title'  # 文本块类型first = True    # 这是一个浮动值# 首次调用该类的实例,该值为 True# 之后调用该类的实例,该值为 Falsedef condition(self, block):# 以下三行代码保证在首次调用该类实例和后续调用时 first 的值不同# 符合一号标题规则的文本块一定符合二号标题规则,它们只有先后次序这一个区别# 首次调用时返回 False ,即代码块不符合二号标题规则# 之后调用返回 True ,即使用该类的 action 方法进行处理if not self.first:return Falseself.first = Falsereturn super().condition(block)class ListItemRule(Rule):"""列表项规则,<li> 标签"""type = 'listitem'   # 文本块类型def condition(self, block):# 行首为减号,则该代码块符合列表项规则return block[0] == '-'def action(self, block, handler):handler.start(self.type)        # 打印 <li> 标签头handler.feed(block[1:].strip()) # 打印 <li> 标签的 text 部分,注意去掉减号handler.end(self.type)          # 打印 <li> 标签尾return True                     # 处理完毕,返回 Trueclass ListRule(ListItemRule):"""列表规则,<ul> 标签"""type = 'list'   # 文本块类型inside = False  # 该值亦为浮动值,判断是否为列表规则def condition(self, block):# 判断代码块是否符合规则这里返回 True# 在 action 方法中调用父类的同名方法再次判断return Truedef action(self, block, handler):# 如果 self.inside 为 False 且父类的 condition 方法返回值为 True# 第一次出现符合列表项规则的文本块时,满足这两个要求if not self.inside and super().condition(block):# 打印 <ul> 标签头handler.start(self.type)# 将 inside 属性值改为 Trueself.inside = True# 打印一堆连续 li 标签后,出现非列表项规则的文本块elif self.inside and not super().condition(block):# 打印 <ul> 标签尾handler.end(self.type)# 再次修改 inside 属性为 Falseself.inside = False# 该方法只用于在合适的条件下打印 <ul> 标签# 永远返回 False ,以调用其它规则实例继续处理return Falseclass ParagraphRule(Rule):"""段落规则,<p> 标签"""type = 'paragraph'  # 文本块类型def condition(self, block):# 不符合以上各类的判断规则的代码块一律按此规则处理return True# 注意这个列表中实例的顺序不能随意改动,原因参见相应类中的注释说明
rule_list = [ListRule(), ListItemRule(), TitleRule(), HeadingRule(),ParagraphRule()]

###4.markup.py

import sys, re
from handlers import HTMLRenderer
from util import blocks
from rules import rule_listclass Parser:"""解析器父类"""def __init__(self, handler):self.handler = handlerself.rules = []     # 判断文本块规则的实例列表self.filters = []   # 过滤方法列表def addRule(self, rule):"""向 self.rules 列表中添加规则类的实例"""self.rules.append(rule)def addFilter(self, pattern, name):"""向 self.filters 列表中添加过滤函数"""# 该过滤器函数接收两个参数:文本块和 HTMLRenderer 类的实例def filter(block, handler):# re.sub 接收三个参数 a b c ,将字符串 c 中的 a 字段替换成 b# b 的值可以是字符串或函数# 在下一行代码中 b 参数的值为 handler 的 sub 方法的返回值# sub 方法的参数 name 的值为字符串,它和 'sub_' 组成一个大字符串# 大字符串就是 handler 的一个方法名return re.sub(pattern, handler.sub(name), block)self.filters.append(filter)def parse(self, file):"""核心方法,解析文本,打印符合要求的标签,写入新的文件中"""# 调用 handler 实例的 start 方法,参数为 handler 实例的某个方法名的一部分# 结果就是调用 handler 的 start_document 方法,打印文档的 head 标签self.handler.start('document')# blocks 是从 urti.py 文件引入的生成器函数# blocks(file) 就是一个生成器# 使用 for 循环生成器for block in blocks(file):# 调用过滤器,对每个文本块进行处理for filter in self.filters:block = filter(block, self.handler)# 循环规则类的实例for rule in self.rules:# 如果符合规则,调用实例的 action 方法打印标签if rule.condition(block):last = rule.action(block, self.handler)# 如果 action 方法的返回值为 True# 表示该文本块处理完毕,结束循环if last:break# 同 self.handler.start# 调用 handler 的 end_document 方法打印 '</body></html>'self.handler.end('document')class BasicTextParser(Parser):"""纯文本解析器"""def __init__(self, handler):# 运行父类 Parser 的同名方法super().__init__(handler)# 增加规则类的实例到 self.urls 列表for rule in rule_list:self.addRule(rule)# 增加三个过滤函数,分别处理斜体字段、链接和邮箱self.addFilter(r'\*(.+?)\*', 'emphasis')self.addFilter(r'(http://[\.a-zA-Z/]+)', 'url')self.addFilter(r'([\.a-zA-Z]+@[\.a-zA-Z]+[a-zA-Z]+)', 'mail')def main():'''主函数,控制整个程序的运行'''handler = HTMLRenderer()parser = BasicTextParser(handler)# 将文件内容作为标准输入,sys.stdin 获取标准输入的内容,生成 IOWrapper 迭代器对象parser.parse(sys.stdin)if __name__ == '__main__':main()

四、 结果及分析

在这个小程序中,我们使用了 Python 来解析纯文本文件并生成 HTML 文件,这个只是简单实现,支持了很少部分的 Markdown 语法,通过这个案例大家可以动手试试解析完整的 Markdown 文件。

Python 实现文本解析器相关推荐

  1. Python 文本解析器

    一.实验介绍 1.1 实验内容 讲解一个使用 Python 来解析纯文本生成一个 HTML 页面的小程序. 将学习和实践以下知识点: Python 基本语法 HTML 标记语言 1.2 实验知识点 P ...

  2. python爬虫五大解析器

    python有五大解析器 一.正则表达式  ,使用第三方库 re(re) 1.匹配规则有 模式 描述 \w 匹配字母.数字及下划线 \W 匹配不是字母.数字及下划线的字符 \s 匹配任意空白字符,等价 ...

  3. python的网页解析器_网页解析器(BeautifulSoup)-- Python

    分享一下关于 Python的网页解析器(BeautifulSoup) BeautifulSoup解析器 为了实现解析器,可以选择使用正则表达式.html.parser.BeautifulSoup.lx ...

  4. 微信小程序商品详情html,微信小程序关于商品详情类的富文本解析器

    微信小程序关于商品详情类的富文本解析器 2020年08月10日 | 萬仟网IT编程 | 我要评论 微信小程序关于商品详情类的富文本解析器富文本的解析,在vue中有v-html解析富文本的方法,但是在微 ...

  5. rust nom 一个文本解析器的使用

    rust nom 一个文本解析器的使用 简述 版本 早期版本 新的改变 常用的几个解析器.组合器 后续 简述 nom, 是 一个Rust 的文本解析器库,它的运行消耗低.速度快,针对一些文本的解析非常 ...

  6. 使用ANTLR做一个简单的Python SQL语法解析器 - 推酷

    使用ANTLR做一个简单的Python SQL语法解析器 - 推酷 使用ANTLR做一个简单的Python SQL语法解析器 - 推酷 posted on 2016-11-14 13:11 lexus ...

  7. python的网页解析器_Python网页解析器使用实例详解

    python 网页解析器 1.常见的python网页解析工具有:re正则匹配.python自带的html.parser模块.第三方库BeautifulSoup(重点学习)以及lxm库. 2.常见网页解 ...

  8. python的网页解析器_python 之网页解析器

    一.什么是网页解析器 1.网页解析器名词解释 首先让我们来了解下,什么是网页解析器,简单的说就是用来解析html网页的工具,准确的说:它是一个HTML网页信息提取工具,就是从html网页中解析提取出& ...

  9. vscode如何添加本地python解释器、解析器 Interpreter?(Python: Select Interpreter)

    先安装python扩展 然后点ctrl+shift+p搜索python:select,选择解析器(或者也可以直接点左下方的) 然后360报毒了,允许通过,然后选择你想要的解释器 然后就好了

最新文章

  1. css实战笔记(一):写网页前的reset工作
  2. LeetCode 28 实现 strStr()
  3. SAP Spartacus 成功读取 Cart 之后,如何将 payload 插入全局 state
  4. edtext 从右边开始输入 安卓_FreeRTOS 从入门到精通6--详解任务管理下(对比PLC,安卓)...
  5. 乐高ev3搭建图纸大全_乐高课程的详细介绍,内附7岁系列课程,还不抓紧时间收藏...
  6. (附源码)springboot西安市中小学生护眼平台开发 毕业设计 080855
  7. html化学式编辑器,常用的化学式编辑器有哪些?4款化学式编辑器分享
  8. 仪器检定是为了满足什么需求?为什么不直接全部校准或检定呢?
  9. SQL实现对销售表现重要指标的计算,对用户特征分层看销售贡献,并统计消费的二八法则
  10. win7 计算机休眠,WIN7如何关闭睡眠和休眠方式(真正的)
  11. python批量爬取京东手机评论信息及星级
  12. 数据库之查询表product——统计所有库存商品的总价值
  13. 本地html如何封装成app,新手适用:如何把网页快速封装成APP
  14. 2021-09-10 LeetCode1894-找到需要补充粉笔的学生编号(每日一题)
  15. 更改系统高亮显示颜色(highlight color)
  16. Godot4补间动画Tween
  17. executeQuery、executeUpdate和execute
  18. 【2019-游记】中山纪念中学暑期游Day4
  19. 搞定Markdown中的图片,一劳永逸的方法!
  20. 先有鸡还是先有蛋,程序员怎么看

热门文章

  1. 云计算ACA练习题5
  2. Android之手机闹钟
  3. BSP Day 24
  4. kernel如何绑定dtb
  5. Android期末复习篇_传智课后习题以及答案(选择、填空、判断、简答、编码题)
  6. 基于深度学习的目标检测:数据增强(一)图像翻转、图像旋转、图像放缩
  7. vue-i18n 英汉互译
  8. 【最快的ubuntu系统安装微信教程】
  9. 笔记本计算机硬件知识,电脑硬件知识(小白必看)
  10. 硅磷晶罐的使用注意事项有哪些?