在Python下使用Txt2Html实现网页过滤代理的教程


Posted in Python onApril 11, 2015

在撰写本 developerWorks 系列文章的过程中,我曾遇到过以最佳格式进行撰写的问题。文字处理程序格式都是专用的,在格式之间转换总不能尽如人意,也很麻烦(而且每种格式都会各自将文档绑定到不同的专用工具,这种情况又与开放源码的精神相违背)。HTML 还算中立 -- 也许您现在阅读的文章正是这种格式 -- 但它也添加了标记,而这些标记很容易引起误输入(或者使人束缚于 HTML 增强型编辑器)。DocBook 是一种有趣的 XML 格式,它可以转换成许多目标格式,并且它拥有技术文章(或书籍)的正确语义;但就像 HTML 一样,在撰写过程中会担心许多标记。LaTeX 特别适合复杂的印刷格式;但它也有许多标记,而这些文章并不需要复杂的印刷格式。

为了在写作时能真正省心 -- 特别是要具有平台和工具的中立性 -- 无格式 ASCII 正是最好的选择。但是,因特网(特别是 Usenet)建议在完全无格式文本的基础上,开发一种“智能 ASCII”文档的非正式标准(请参阅 参考资料 )。“智能 ASCII”只添加了一点额外的语义内容和上下文,而且它们在文本显示中看起来是那么“自然”。电子邮件、新闻组邮件、FAQ、项目自述文件 (README) 和其它电子文档通常包括一些印刷/语义元素,如强调字前后的星号、标题下的下划线、描述文本关系的竖直和水平空格、有选择性的全大写和其它一些信息。Project Gutenberg(请参阅 参考资料 )是一种惊人的成果,它将许多想法加入其自身格式构思中,并认为“智能 ASCII”是长时间保存和分发好书的最佳选择。即使这些文章不会像文学名著那样经久不衰,仍决定将它们写成“智能 ASCII”格式,并用方便的 Python 脚本将它们自动转换成其它格式。
介绍 Txt2Html

Txt2Html 最初是一个简单的文件转换器,从其名称上就可以看出。但因特网建议在工具上添加几个明显的增强功能。因为有许多读者想以“HTML 化”格式查看的文档都在 http: 或 ftp: 链接的后面,所以工具应该真正直接处理这样的远程文档(而不需要下载/转换/查看循环周期)。因为转换的目标最终是 HTML,通常我们要做的就是在 Web 浏览器中查看转换后的目标文档。

将这些放到一起后,Txt2Html 就成了“基于 Web 的过滤代理”。这个词很奇特,也许恰好能“完全表达其含义”。它们体现了以下想法:程序代表您阅读 Web 页面(或其它资源),以某种方式处理内容,然后以某种比原始页面 更好 的形式(至少能满足某些特殊目的)向您显示该页面。这种工具的一个很好例子就是 Babelfish 翻译服务(请参阅 参考资料 )。在通过 Babelfish 运行了 URL 之后,您看到的 Web 页面与原始页面非常相似,但它显示了您能够读懂的文字,而不是您不理解的语言。在某种程度上,所有显示搜索结果页面摘要的搜索引擎所做的都是相同的事情。但那些搜索引擎(按设计)在目标页面的格式以及外观方面有更多的自由度,同时会去掉许多内容。当然,Txt2Html 并不如 Babelfish 那样功能强大;但概念上,它们很大程度上完成相同的事情。请参阅 参考资料以获取更多的例子,其中一些还很幽默。

Txt2Html 最大的优点是使用许多编程技巧,而这些技巧对于不同面向 Web 使用 Python 是共通的。本文将介绍那些技巧,并会说明编码技巧和一些 Python 模块的作用域。请注意:Txt2Html 中的实际模块叫做 dmTxt2Html,以避免与其他人编写的模块名称发生冲突。

使用 cgi 模块

Python 标准发行版中的 cgi 模块对于任何用 Python 开发“公共网关接口”应用程序的人来说都是意外惊喜。可以不使用它来创建 CGI,但您不会这样做。

最通常的情况下,您会通过 HTML 表单与 CGI 应用程序进行交互。要填写调用 CGI 的表单以执行使用规范的操作。例如,Txt2Html 文档使用这个例子来调用 HTML 表单(Txt2Html 自身生成的表单比较复杂,并且可能会改变,但示例将会很好地工作,甚至是在您自己的 Web 页面中):
调用 'Txt2Html' 的 HTML 表单

    <form method="get" action="http://gnosis.cx/cgi/txt2html.cgi">
     URL: <input type="text" name="source" size=40>
     <input type="submit" name="go" value="Display!">
    </form>

可以在 HTML 表单中包括许多输入字段,而字段可以是许多不同类型中的一种(文本、复选框、单选列表、单选按钮等)。任何讲述 HTML 的好书可以帮助初学者创建定制 HTML 表单。此处最需要牢记的是每个字段都有名称属性,而以后会在 CGI 脚本中使用该名称来引用该字段。另一个需要了解的细节是表单可以使用以下两种方法中的一种:"get" 和 "post"。它们的基本差异是 "get" 在 URL 中包括了查询信息,并且这个方法使用户易于保存特定查询以便日后重复使用。另一方面,如果您不希望用户保存查询,请使用 "post" 方法。

以上表单调用的 Python 脚本将导入 cgi 以使整理其调用表单变得更容易。这个模块所做的一件事就是隐藏 CGI 脚本中 "get" 和 "post" 方法之间差异的任何细节。在进行调用之前,这并不是 CGI 创建程序需要操心的细节。该 CGI 模块的主要作用是处理类似于字典样式的调用 HTML 表单中的所有字段。您所得到的并不是 真正 的 Python 字典,但它们的使用方式极其相似:
使用 Python [cgi] 模块

import
     cgi, sys
  cfg_dict = {
    'target': 
    '<STDOUT>'}
  sys.stderr = sys.stdout
  form = cgi.FieldStorage()
  
    if
     form.has_key(
    'source'):
   cfg_dict[
    'source'] = form[
    'source'].value

在以上几行中,要注意几处细节。我们所使用的一个技巧是设置 sys.stderr = sys.stdout。如果我们的脚本遇到了一个未捕获的错误,通过这个操作,追溯就显示回到客户机浏览器。这可以节省许多调试 CGI 应用程序的时间。但您也许不想让用户看到这些(或者如果它们可能向您报告问题详细信息,您也许会向用户显示这些内容)。接着,我们将 HTML 表单值读取到类似于字典的 form 实例中。表单有一个 .has_key() 方法,这与真正的 Python 字典非常相似。但是,与 Python 字典不同,要真正获取键中的值,我们必须查看键的 .value 属性。

至此,HTML 表单中的所有东西都成了纯 Python 变量,而且我们可以在其它任何 Python 程序中处理它们。

使用 urllib 模块

就像大多数 Python 模块一样,urllib 以直观且简单的方法处理许多复杂事物。urllib 中的 urlopen() 函数可以处理任何远程资源 -- 无论是 http:、ftp: 还是 gopher: --就将它当作是本地文件。如果使用 urlopen() 抓取了一个远程(伪)文件对象,可以将它当作本地(只读)文件的文件对象来进行处理:
使用 Python [urllib] 模块

from
     urllib 
    import
     urlopen
  
    import
     string
  source = cfg_dict[
    'source']
  
    if
     source == 
    '<STDIN>':
   fhin = sys.stdin
  
    else
    :
   
    try
    :
   fhin = urlopen(source)
   
    except
    :
   ErrReport(source+
    ' could not be opened!', cfg_dict)
   
    return
  
     doc = 
    ''
  
    for
     line 
    in
     fhin.readlines(): 
    # Need to normalize line endings!
   doc = doc+string.rstrip(line)+
    '\n'

我曾遇到过一个小问题,由于生成资源的平台和您的平台使用不同的行结束约定,生成的文本中可能会发生某些奇怪的事(这似乎是 urllib 中的错误)。这个问题的解决方法是在以上代码中执行小小的 .readlines() 循环。不管该资源原来是什么样子,这个操作会给您一个字符串,它都有所使用平台的正确行结束约定(估计可能合理)。

使用 re 模块

由于本文篇幅限制,这里只讨论一部分规则表达式。参考资料中列出了许多关于该主题的参考书籍。 re 模块在 Txt2Html 中的使用很广泛,用于标识源文本中的各种文本模式。我们查看一个比较复杂的例子:
使用 Python [re] 模块

import
     re
  
    def
      URLify
    (txt):
   txt = re.sub(
    '((?:http|ftp|gopher|file)://(?:[^ \n\r<\)]+))(\s)',
   
    '<a href="\\1">\\1</a>\\2', txt)
   
    return
     txt

URLify() 是一个小巧函数,它的功能正如其名称所示。如果在“智能 ASCII”文件中遇到了类似于 URL 的字符串,它将转换成 HTML 输出中同一个 URL 真正的热链接。让我们看看 re.sub() 的作用。首先,从最主要的角度来看,函数的目的是“查找与第一个模式中的值匹配的字符串,然后通过将第三个变量作为要操作的字符串,以第二个模式替换匹配结果”。非常好,从这些角度来看,它与 string.replace() 没有什么区别。

第一个模式有几个元素。首先,请注意括号:最高层次由两对括号组成:(\S) 前复杂的字符串。括号对与可能组成部分替换模式的“子表达式”匹配。第二个子表达式 (\s) 只表示“查找与任何空格字符匹配的字符串,让我们回过来看什么与之匹配”。因此,让我们研究一下第一个子表达式。

Python 规则表达式有它自己的一些技巧。其中一个技巧是子表达式开始处的 ?: 运算符。这表示“查找子模式的匹配字符串,但不包括反向引用中的匹配结果”。那么,让我们检查一下这个子表达式:

    ((?:http|ftp|gopher|file)://(?:[^ \n\r<\)]+))。

首先,请注意这个子表达式本身由两个子表达式组成,两者之间的字符并不属于任何一方。但是,每个子表达式都由 ?: 开头,它表示这两者匹配,但不是为了满足引用目的。第一个“非引用”子表达式只表示“查找类似于 http 或ftp 或 其它值的字符串”。接着,我们看到字符串 :// ,它表示查找与它完全相似的字符串(简单吗?)。最后,我们看到第二个子表达式,它除了有由方括号组成的“不引用”运算符外,还有一个加号。

在规则表达式中,方括号只表示“查找与方括号中任何字符的匹配字符”。但是,如果第一个字符是插入记号 (^),含义则相反,它表示“查找任何与后面的字符 不匹配的字符”。因此,我们正在查找 不 是空格、CR、LF、"<" 或 ")" 的字符(还请注意,可以通过在字符前面加 "\",避开对于规则表达式有特殊意义的字符)。结尾处的加号表示“查找最后字符串的一个或多个匹配”(星号表示“零个或多个”,问号表示“零个或一个”)。

这个规则表达式有许多内容有待理解,但如果多看几遍,您就会明白这就是 URL 的格式。

接着就是替换部分。这就更简单了。看上去类似 \\1 和 \\2 (或者 \\3 、 \\4 等,如果需要的话)的部分就是刚谈到的“反向引用”。\\1(或 \\2)表示模式与匹配表达式的第一个(或第二个)子表达式相匹配。替换部分的其余组成部分没有什么特别含义:只是一些容易识别为 HTML 代码的字符。有一件事比较麻烦,那就是匹配 \\2 -- 它看上去就是一个空格字符。也许有人会问:“有什么麻烦的?为什么不直接插入一个空格字符?”问得好,实际上我们不 需要 执行对 HTML 的操作。但从美学角度来看,最好让 HTML 输出尽可能保留转换成 HTML 标记之前的源文本文件外观。尤其是,让我们将换行符保留成换行符,空格就是空格(Tab 键就是 Tab 键)。

Python 相关文章推荐
Python环境下搭建属于自己的pip源的教程
May 05 Python
python中使用正则表达式的连接符示例代码
Oct 10 Python
Python3 加密(hashlib和hmac)模块的实现
Nov 23 Python
Tensorflow卷积神经网络实例
May 24 Python
浅谈python的dataframe与series的创建方法
Nov 12 Python
python 用所有标点符号分隔句子的示例
Jul 15 Python
Python递归调用实现数字累加的代码
Feb 25 Python
浅谈pandas.cut与pandas.qcut的使用方法及区别
Mar 03 Python
在Python IDLE 下调用anaconda中的库教程
Mar 09 Python
python中执行smtplib失败的处理方法
Jul 01 Python
python 输入字符串生成所有有效的IP地址(LeetCode 93号题)
Oct 15 Python
pytorch 实现多个Dataloader同时训练
May 29 Python
详解Python中DOM方法的动态性
Apr 11 #Python
将Python中的数据存储到系统本地的简单方法
Apr 11 #Python
Python中的进程分支fork和exec详解
Apr 11 #Python
Python json模块使用实例
Apr 11 #Python
Python进程通信之匿名管道实例讲解
Apr 11 #Python
Python multiprocessing模块中的Pipe管道使用实例
Apr 11 #Python
Python httplib模块使用实例
Apr 11 #Python
You might like
网络资源
2006/10/09 PHP
PHP新手上路(八)
2006/10/09 PHP
php xml 入门学习资料
2011/01/01 PHP
php实现简单文件下载的方法
2015/01/30 PHP
yii2实现根据时间搜索的方法
2016/05/25 PHP
php支付宝APP支付功能
2020/07/29 PHP
小型js框架veryide.librar源代码
2009/03/05 Javascript
javascript 构建一个xmlhttp对象池合理创建和使用xmlhttp对象
2010/01/15 Javascript
推荐40款强大的 jQuery 导航插件和教程(上篇)
2012/09/14 Javascript
jQuery查询数据返回object和字符串影响原因是什么
2013/08/09 Javascript
Javascript让DEDECMS告别手写Tag
2014/09/01 Javascript
jQuery对于显示和隐藏等常用状态的判断方法
2014/12/13 Javascript
node.js中的fs.futimes方法使用说明
2014/12/17 Javascript
javascript+canvas制作九宫格小程序
2014/12/28 Javascript
JS用斜率判断鼠标进入DIV四个方向的方法
2016/11/07 Javascript
利用node.js实现反向代理的方法详解
2017/07/24 Javascript
不得不看之JavaScript构造函数及new运算符
2017/08/21 Javascript
Bootbox将后台JSON数据填充Form表单的实例代码
2018/09/10 Javascript
javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法
2020/05/14 Javascript
浅谈vue 多个变量同时赋相同值互相影响
2020/08/05 Javascript
在Python中操作文件之read()方法的使用教程
2015/05/24 Python
利用python生成一个导出数据库的bat脚本文件的方法
2016/12/30 Python
使用python 爬虫抓站的一些技巧总结
2018/01/10 Python
python实现生命游戏的示例代码(Game of Life)
2018/01/24 Python
使用python编写udp协议的ping程序方法
2018/04/22 Python
pandas中去除指定字符的实例
2018/05/18 Python
浅谈Python编程中3个常用的数据结构和算法
2019/04/30 Python
在Python中画图(基于Jupyter notebook的魔法函数)
2019/10/28 Python
Flask中endpoint的理解(小结)
2019/12/11 Python
如何在python中判断变量的类型
2020/07/29 Python
详解CSS3 filter:drop-shadow滤镜与box-shadow区别与应用
2020/08/24 HTML / CSS
护理学应聘自荐书范文
2014/02/05 职场文书
语文教学感言
2014/02/06 职场文书
初中生物教学随笔
2015/08/15 职场文书
解决jupyter notebook启动后没有token的坑
2021/04/24 Python
Python类方法总结讲解
2021/07/26 Python