Python 文本解析器

一、课程介绍

本课程讲解一个使用 Python 来解析纯文本生成一个 HTML 页面的小程序。

二、相关技术

Python:一种面向对象、解释型计算机程序设计语言,用它可以做 Web 开发、图形处理、文本处理和数学处理等等。

HTML:超文本标记语言,主要用来实现网页。

三、项目截图

纯文本文件:

Welcome to ShiYanLou

ShiYanLou is the first experiment with IT as the core of online education platform.*Our aim is to do the experiment, easy to learn IT*.

Course

-Basic Course

-Project Course

-Evaluation Course

Contact us

-Web:http://www.shiyanlou.com

-QQ Group:241818371

-E-mail:support@shiyanlou.com

解析后生成的 HTML 页面如下图

四、项目讲解

1. 文本块生成器

首先我们需要有一个文本块生成器把纯文本分成一个一个的文本块,以便接下来对每一个文本快进行解析,util.py 代码如下:

#!/usr/bin/python
# encoding: utf-8

def lines(file):
    """
    生成器,在文本最后加一空行
    """
    for line in file: yield line
    yield '\n'

def blocks(file):
    """
    生成器,生成单独的文本块
    """
    block = []
    for line in lines(file):
        if line.strip():
            block.append(line)
        elif block:
            yield ''.join(block).strip()
            block = []

2. 处理程序

通过文本生成器我们得到了一个一个的文本块,然后需要有处理程序对不同的文本块加相应的 HTML 标记,handlers.py 代码如下:

#!/usr/bin/python
# encoding: utf-8

class Handler:
    """
    处理程序父类
    """
    def callback(self, prefix, name, *args):
        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)

    def sub(self, name):
        def substitution(match):
            result = self.callback('sub_', name, match)
            if result is None: result = match.group(0)
            return result
        return substitution

class HTMLRenderer(Handler):
    """
    HTML 处理程序,给文本块加相应的 HTML 标记
    """
    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):
        return '<a target="_blank" style="text-decoration: none;color: #BC1A4B;" href="%s">%s</a>' % (match.group(1), match.group(1))

    def sub_mail(self, match):
        return '<a style="text-decoration: none;color: #BC1A4B;" href="mailto:%s">%s</a>' % (match.group(1), match.group(1))

    def feed(self, data):
        print data

3. 规则

有了处理程序和文本块生成器,接下来就需要一定的规则来判断每个文本块交给处理程序将要加什么标记,rules.py 代码如下:

#!/usr/bin/python
# encoding: utf-8

class Rule:
    """
    规则父类
    """
    def action(self, block, handler):
        """
        加标记
        """
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True

class HeadingRule(Rule):
    """
    一号标题规则
    """
    type = 'heading'
    def condition(self, block):
        """
        判断文本块是否符合规则
        """
        return not '\n' in block and len(block) <= 70 and not block[-1] == ':'

class TitleRule(HeadingRule):
    """
    二号标题规则
    """
    type = 'title'
    first = True

    def condition(self, block):
        if not self.first: return False
        self.first = False
        return HeadingRule.condition(self, block);

class ListItemRule(Rule):
    """
    列表项规则
    """
    type = 'listitem'
    def condition(self, block):
        return block[0] == '-'

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block[1:].strip())
        handler.end(self.type)
        return True

class ListRule(ListItemRule):
    """
    列表规则
    """
    type = 'list'
    inside = False
    def condition(self, block):
        return True

    def action(self, block, handler):
        if not self.inside and ListItemRule.condition(self, block):
            handler.start(self.type)
            self.inside = True
        elif self.inside and not ListItemRule.condition(self, block):
            handler.end(self.type)
            self.inside = False
        return False

class ParagraphRule(Rule):
    """
    段落规则
    """
    type = 'paragraph'

    def condition(self, block):
        return True

4. 解析

最后我们就可以进行解析了,markup.py 代码如下:

#!/usr/bin/python
# encoding: utf-8

import sys, re
from handlers import *
from util import *
from rules import *

class Parser:
    """
    解析器父类
    """
    def __init__(self, handler):
        self.handler = handler
        self.rules = []
        self.filters = []

    def addRule(self, rule):
        """
        添加规则
        """
        self.rules.append(rule)

    def addFilter(self, pattern, name):
        """
        添加过滤器
        """
        def filter(block, handler):
            return re.sub(pattern, handler.sub(name), block)
        self.filters.append(filter)

    def parse(self, file):
        """
        解析
        """
        self.handler.start('document')
        for block in blocks(file):
            for filter in self.filters:
                block = filter(block, self.handler)
            for rule in self.rules:
                if rule.condition(block):
                    last = rule.action(block, self.handler)
                    if last: break
        self.handler.end('document')

class BasicTextParser(Parser):
    """
    纯文本解析器
    """
    def __init__(self, handler):
        Parser.__init__(self, handler)
        self.addRule(ListRule())
        self.addRule(ListItemRule())
        self.addRule(TitleRule())
        self.addRule(HeadingRule())
        self.addRule(ParagraphRule())

        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')

"""
运行程序
"""
handler = HTMLRenderer()
parser = BasicTextParser(handler)
parser.parse(sys.stdin)

运行程序(纯文本文件为 test.txt,生成 HTML 文件为 test.html)

python markup.py < test.txt > test.html

五、代码下载

可以使用下列命令下载本课程相关代码:

$ git clone http://git.shiyanlou.com/shiyanlou/python_markup

六、小结

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

Python 文本解析器的更多相关文章

  1. 5. python 文本解析

    5. python 文本解析 这一章节我们简单的聊聊文本解析的两种方法: 1.分片,通过分片,记录偏移处,然后提取想要的字符串 例子: >>> line='aaa bbb ccc'  ...

  2. Python HTML解析器BeautifulSoup(爬虫解析器)

    BeautifulSoup简介 我们知道,Python拥有出色的内置HTML解析器模块——HTMLParser,然而还有一个功能更为强大的HTML或XML解析工具——BeautifulSoup(美味的 ...

  3. Python 网页解析器

    Python 有几种网页解析器? 1. 正则表达式 2.html.parser (Python自动) 3.BeautifulSoup(第三方)(功能比较强大) 是一个HTML/XML的解析器 4.lx ...

  4. python——BS解析器

  5. python 之网页解析器

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

  6. 面试官问我:如何在 Python 中解析和修改 XML

    摘要:我们经常需要解析用不同语言编写的数据.Python提供了许多库来解析或拆分用其他语言编写的数据.在此 Python XML 解析器教程中,您将学习如何使用 Python 解析 XML. 本文分享 ...

  7. 深入浅出 Vue.js 第九章 解析器---学习笔记

    本文结合 Vue 源码进行学习 学习时,根据 github 上 Vue 项目的 package.json 文件,可知版本为 2.6.10 解析器 一.解析器的作用 解析器的作用就是将模版解析成 AST ...

  8. 28.解析器Parser

    什么是解析器 因为前后端分离,可能有json.xml.html等各种不同格式的内容 后端也必须要有一个解析器来解析前端发送过来的数据 不然后端无法处理前端数据 后端有一个渲染器Render,和解析器是 ...

  9. python模块介绍- HTMLParser 简单的HTML和XHTML解析器

    python模块介绍- HTMLParser 简单的HTML和XHTML解析器 2013-09-11 磁针石 #承接软件自动化实施与培训等gtalk:ouyangchongwu#gmail.comqq ...

随机推荐

  1. Qt分析:Qt中的两种定时器(可是QObject为什么要提高定时器呢,没必要啊。。。)

    Qt有两种定时器,一种是QObject类的定时器,另一种是QTimer类的定时器.   (1)QObject类的定时器   QObject类提供了一个基本的定时器,通过函数startTimer()来启 ...

  2. perl学习(4) 子程序

    子程序,类比c语言中的函数,在形式上个人认为最大的区别:没有形参 1.1.定义子程序 1.2.调用 #! /usr/bin/perl sub marine { $n += 1 ; print &quo ...

  3. vs使代码可以折叠的方法

    set [工具]->[选项]->[文本编辑器]->[C/C++]->[查看]->[大纲显示]->[大纲语句块] = True

  4. android linearlayout imageview置顶摆放

    在练习android时,想在Linearlayout内放一图片,使其图片置顶,预期效果是这样的: 但xml代码imageview写成这样后, <ImageView android:layout_ ...

  5. BZOJ 1029: [JSOI2007]建筑抢修

    1029: [JSOI2007]建筑抢修 Description 小刚在玩JSOI提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者.但是T部落的基地里已经有 ...

  6. Oracle 11g RAC 环境下单实例非缺省监听及端口配置

    如果在Oracle 11g RAC环境下使用dbca创建单实例数据库后,Oracle会自动将其注册到缺省的1521端口及监听器.大多数情况下我们使用的为非缺省监听器以及非缺省的监听端口.而且在Orac ...

  7. mysql语句:批量更新多条记录的不同值

    mysql更新语句很简单,更新一条数据的某个字段,一般这样写: 1 UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_va ...

  8. 企业级IM应该帮助员工提高绩效,避免无关的信息干扰

    很多上班族一定熟悉如下的场景: 您早上上班一打开QQ,就弹出一个新闻集成窗口,随便点开看看吧,这一点不要紧,您就被一个又一个的链接带着逛下去了.等回过神来要工作的时候,发现已经在这些八卦新闻上浪费了一 ...

  9. 64位CentOS上编译 Hadoop 2.2.0

    下载了Hadoop预编译好的二进制包,hadoop-2.2.0.tar.gz,启动起来后.总是出现这样的警告: WARN util.NativeCodeLoader: Unable to load n ...

  10. Android 使用动态载入框架DL进行插件化开发

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客) 概述: 随着应用的不断迭代.应用的体积不断增大,项目越来越臃肿,冗余添 ...