您当前位置:首页 > 深入 Python > HTML 处理 > 整合所有内容 | << >> | ||||
深入 Python从 Python 新手到专家 |
现在是时候把你学到的所有东西都好好利用起来了。我希望你一直在认真听讲。
def translate(url, dialectName="chef"):import urllib
sock = urllib.urlopen(url)
htmlSource = sock.read() sock.close()
![]() |
translate 函数有一个可选参数 dialectName,它是一个指定你要使用的方言的字符串。你马上就会看到它是如何使用的。 |
![]() |
等等,这个函数里有一个import 语句!这在 Python 中是完全合法的。你习惯于在程序的开头看到 import 语句,这意味着导入的模块在程序的任何地方都可以使用。但是你也可以在函数中导入模块,这意味着导入的模块只能在函数中使用。如果你有一个模块只在一个函数中使用,这是一种使你的代码更加模块化的简单方法。(当你发现你的周末黑客作品已经变成了一个 800 行的艺术品,并决定把它分成十几个可重用的模块时,你就会体会到这一点。) |
![]() |
现在你获取给定 URL 的源代码。 |
parserName = "%sDialectizer" % dialectName.capitalize()parserClass = globals()[parserName]
parser = parserClass()
![]()
![]() |
capitalize 是一个你以前没见过的字符串方法;它只是将字符串的第一个字母大写,并强制将其他所有字母小写。结合一些字符串格式化,你已经将方言的名称转换为相应的 Dialectizer 类的名称。如果 dialectName 是字符串 'chef',那么 parserName 将是字符串 'ChefDialectizer'。 |
![]() |
你有一个字符串形式的类名 (parserName),并且你有一个字典形式的全局命名空间 (globals ())。结合起来,你可以获得对字符串所命名的类的引用。(记住,类是对象,它们可以像任何其他对象一样被赋值给变量。)如果 parserName 是字符串 'ChefDialectizer',那么 parserClass 将是类 ChefDialectizer。 |
![]() |
最后,你有一个类对象 (parserClass),并且你想要一个该类的实例。好吧,你已经知道如何做到这一点:像函数一样调用该类。该类被存储在一个局部变量中这一事实绝对没有区别;你只需像函数一样调用该局部变量,就会弹出一个该类的实例。如果 parserClass 是类 ChefDialectizer,那么 parser 将是类 ChefDialectizer 的一个实例。 |
为什么要这么麻烦?毕竟,只有 3 个 Dialectizer 类;为什么不直接使用 case 语句呢?(好吧,Python 中没有 case 语句,但为什么不直接使用一系列的 if 语句呢?)一个原因是:可扩展性。translate 函数完全不知道你定义了多少个 Dialectizer 类。想象一下,如果你明天定义了一个新的 FooDialectizer;translate 将通过传递 'foo' 作为 dialectName 来工作。
更好的是,想象一下将 FooDialectizer 放在一个单独的模块中,并使用 from module import 导入它。你已经看到这把它包含在 globals() 中,所以 translate 仍然可以正常工作,即使 FooDialectizer 在一个单独的文件中。
现在想象一下,方言的名称来自程序外部的某个地方,可能是来自数据库,也可能是来自用户在表单中输入的值。你可以使用任意数量的服务器端 Python 脚本架构来动态生成网页;这个函数可以在网页请求的查询字符串中获取一个 URL 和一个方言名称(都是字符串),并输出“翻译”后的网页。
最后,想象一下一个具有插件架构的 Dialectizer 框架。你可以将每个 Dialectizer 类放在一个单独的文件中,只在 dialect.py 中保留 translate 函数。假设有一个一致的命名方案,那么 translate 函数就可以根据方言名称,从相应的文件中动态导入相应的类。(你还没有见过动态导入,但我保证在后面的章节中会介绍它。)要添加一个新的方言,你只需在插件目录中添加一个命名适当的文件(比如包含 FooDialectizer 类的 foodialect.py)。使用方言名称 'foo' 调用 translate 函数将找到模块 foodialect.py,导入类 FooDialectizer,然后你就可以开始了。
parser.feed(htmlSource)parser.close()
return parser.output()
![]()
![]() |
在经历了所有这些想象之后,这看起来会很无聊,但 feed 函数是完成整个转换的函数。你将整个 HTML 源代码放在一个字符串中,所以你只需要调用一次 feed。但是,你可以根据需要多次调用 feed,解析器会继续解析。所以,如果你担心内存使用量(或者你知道你要处理非常大的 HTML 页面),你可以把它设置在一个循环中,每次读取几字节的 HTML 并将其提供给解析器。结果将是一样的。 |
![]() |
因为 feed 维护着一个内部缓冲区,所以当你完成时,你应该始终调用解析器的 close 方法(即使你像刚才那样一次性地提供了所有数据)。否则,你可能会发现你的输出缺少最后几字节。 |
![]() |
记住,output 是你在 BaseHTMLProcessor 上定义的函数,它将你缓冲的所有输出片段连接起来,并以单个字符串的形式返回它们。 |
就这样,你“翻译”了一个网页,只给出了一个 URL 和一个方言名称。
<< 介绍 dialect.py |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
总结 >> |