Python生成HTML输出(第一次尝试),几个问题(包含代码)

2 投票
4 回答
3018 浏览
提问于 2025-04-15 11:18

我玩Python已经几个月了(只是个爱好者),但对网页编程了解得很少(会一点HTML,完全不会JavaScript等等)。不过,我现在有一个项目,让我第一次接触网页编程。这让我想问:

把Python脚本的输出放到网页上,最简单的方法是什么?

感谢大家的回答,我有了一些进展。目前,我只在用Python和HTML。我不能发布我的项目代码,所以我写了一个小例子,使用了Twitter搜索(请看下面)。

我有几个问题:

  1. 我这样做会不会很傻?我觉得WebOutput()这个方法虽然清晰,但效率不高。如果我用JavaScript的话,是不是可以写一个HTML模板文件,然后只更新数据?对吧?这样做会更好吗?

  2. 对于这样的应用程序,什么时候使用框架比较合适?会不会太复杂了?

抱歉问这些基础问题——但我不想花太多时间走错路。

import simplejson, urllib, time

#query, results per page 
query = "swineflu"
rpp = 25
jsonURL = "http://search.twitter.com/search.json?q=" + query + "&rpp=" + str(rpp)

#currently storing all search results, really only need most recent but want the data avail for other stuff
data = []

#iterate over search results
def SearchResults():
    jsonResults = simplejson.load(urllib.urlopen(jsonURL))
    for tweet in jsonResults["results"]:
        try:
            #terminal output
            feed = tweet["from_user"] + " | " + tweet["text"]
            print feed
            data.append(feed)
        except:
            print "exception??"

# writes latest tweets to file/web
def WebOutput():
    f = open("outw.html", "w")
    f.write("<html>\n")
    f.write("<title>python newb's twitter search</title>\n")
    f.write("<head><meta http-equiv='refresh' content='60'></head>\n")
    f.write("<body>\n")
    f.write("<h1 style='font-size:150%'>Python Newb's Twitter Search</h1>")
    f.write("<h2 style='font-size:125%'>Searching Twitter for: " + query + "</h2>\n")
    f.write("<h2 style='font-size:125%'>" + time.ctime() + " (updates every 60 seconds)</h2>\n")

    for i in range(1,rpp):
        try:
            f.write("<p style='font-size:90%'>" + data[-i] + "</p>\n")
        except:
            continue

    f.write("</body>\n")
    f.write("</html>\n")
    f.close()

while True:
    print ""
    print "\nSearching Twitter for: " + query + " | current date/time is: " + time.ctime()
    print ""
    SearchResults()
    WebOutput()
    time.sleep(60)

相关问题:

4 个回答

4

我建议你使用模板来生成输出,你可以从内置的 string.Template 开始,或者试试更高级的,比如 Mako(还有 Cheetah、Genshi、Jinja、Kid 等等)。

Python 有很多不错的网页框架,其中最简单的有 web.py 或者 werkzeug

如果你想要一个功能齐全的框架,可以看看 Pylons 或者 Django,不过这些对于这样的项目来说可能有点过于复杂了。

5

字符串格式化可以让代码看起来更整洁,也更不容易出错。

举个简单的例子,%s会被替换成 a title

my_html = "<html><body><h1>%s</h1></body></html>" % ("a title")

或者可以多次使用(标题是一样的,而第二个 %s 现在显示的是“我的内容”):

my_html = "<html><body><h1>%s</h1>%s</body></html>" % ("a title", "my content")

在使用 %s 时,你也可以用命名的键,比如 %(thekey)s,这样你就不用记住 %s 的顺序了。你可以用一个 列表,也可以用一个 字典,字典是把键和对应的值关联起来的:

my_html = "<html><body><h1>%(title)s</h1>%(content)s</body></html>" % {
    "title": "a title",
    "content":"my content"
}

你脚本中最大的问题是,你在使用一个全局变量(data)。一个更好的方法是:

  • 调用 search_results,传入参数“swineflu”
  • search_results 返回一个结果列表,把结果存储在一个变量中
  • 调用 WebOutput,把搜索结果变量作为参数
  • WebOutput 返回一个字符串,包含你的 HTML
  • 把返回的 HTML 写入你的文件

WebOutput 会返回 HTML(作为字符串),并把它写入文件。类似于:

results = SearchResults("swineflu", 25)
html = WebOutput(results)
f = open("outw.html", "w")
f.write(html)
f.close()

最后,只有在你需要登录才能访问的数据时,才需要使用 twitterd 模块。公共时间线是公开的,可以不需要任何认证就能访问,所以你可以去掉 twitterd 的导入和 api = 这一行。如果你想使用 twitterd,你需要对 api 变量做一些处理,比如:

api = twitterd.Api(username='username', password='password')
statuses = api.GetPublicTimeline()

所以,我可能会这样写这个脚本:

import time
import urllib
import simplejson

def search_results(query, rpp = 25): # 25 is default value for rpp
    url = "http://search.twitter.com/search.json?q=%s&%s" % (query, rpp)

    jsonResults = simplejson.load(urllib.urlopen(url))

    data = [] # setup empty list, within function scope
    for tweet in jsonResults["results"]:
        # Unicode!
        # And tweet is a dict, so we can use the string-formmating key thing
        data.append(u"%(from_user)s | %(text)s" % tweet)

    return data # instead of modifying the global data!

def web_output(data, query):
    results_html = ""

    # loop over each index of data, storing the item in "result"
    for result in data:
        # append to string
        results_html += "    <p style='font-size:90%%'>%s</p>\n" % (result)

    html = """<html>
    <head>
    <meta http-equiv='refresh' content='60'>
    <title>python newb's twitter search</title>
    </head>
    <body>
        <h1 style='font-size:150%%'>Python Newb's Twitter Search</h1>
        <h2 style='font-size:125%%'>Searching Twitter for: %(query)s</h2>
        <h2 style='font-size:125%%'> %(ctime)s (updates every 60 seconds)</h2>
    %(results_html)s
    </body>
    </html>
    """ % {
        'query': query,
        'ctime': time.ctime(),
        'results_html': results_html
    }

    return html


def main():
    query_string = "swineflu"
    results = search_results(query_string) # second value defaults to 25

    html = web_output(results, query_string)

    # Moved the file writing stuff to main, so WebOutput is reusable
    f = open("outw.html", "w")
    f.write(html)
    f.close()

    # Once the file is written, display the output to the terminal:
    for formatted_tweet in results:
        # the .encode() turns the unicode string into an ASCII one, ignoring
        # characters it cannot display correctly
        print formatted_tweet.encode('ascii', 'ignore')


if __name__ == '__main__':
    main()
# Common Python idiom, only runs main if directly run (not imported).
# Then means you can do..

# import myscript
# myscript.search_results("#python")

# without your "main" function being run

(2) 在什么情况下框架适合这样的应用?会不会太复杂?

我会说总是使用一个网络框架(虽然有少数例外)

这可能听起来有点奇怪,因为我刚花了很多时间在解释你脚本的修复……但是,经过上述修改后,做起来非常简单,因为一切都已经很好地封装成函数了!

使用 CherryPy,这是一个非常简单的 Python HTTP 框架,你可以轻松地把数据发送到浏览器,而不是不断地写文件。

这假设上面的脚本保存为 twitter_searcher.py

注意我之前从未使用过 CherryPy,这只是 CherryPy 首页上的 HelloWorld 示例,里面有几行是从上面脚本的 main() 函数复制过来的!

import cherrypy

# import the twitter_searcher.py script
import twitter_searcher
# you can now call the the functions in that script, for example:
# twitter_searcher.search_results("something")

class TwitterSearcher(object):
    def index(self):
        query_string = "swineflu"
        results = twitter_searcher.search_results(query_string) # second value defaults to 25
        html = twitter_searcher.web_output(results, query_string)

        return html
    index.exposed = True

cherrypy.quickstart(TwitterSearcher())

保存并运行这个脚本,然后在浏览器中访问 http://0.0.0.0:8080/,你就能看到你的页面了!

这样做的问题是,每次加载页面时都会查询 Twitter API。如果只有你在使用,这没问题,但如果有成百上千(甚至几十)的人在看这个页面,速度就会开始变慢(而且你可能会被 Twitter API 限制或封锁)。

解决方案基本上是回到最开始……你可以把搜索结果写入(缓存)磁盘,如果数据超过大约 60 秒,就重新搜索 Twitter。你也可以看看 CherryPy 的缓存选项……不过这个回答变得有点长了……

8

用框架来做这样的事情并不是多余的;Python的框架通常都很轻便,使用起来也很简单,这样可以让你更容易地为你的小网站添加新功能。不过,这并不是必须的;我假设你是在学习,所以我会讲讲我会如何修改代码。

在你的 WebOutput 函数里,你是在没有模板引擎的情况下做模板处理;Python有很多很不错的模板语言,我最喜欢的是 mako。如果那个函数里的代码变得复杂,我会把它拆分成一个模板;我稍后会给你展示那是什么样子。不过首先,我会用多行字符串来替换掉所有的 f.write,并用字符串替换来代替字符串的拼接:

f.write("""<html>
<title>python newb's twitter search</title>
<head><meta http-equiv='refresh' content='60'></head>
<body>
<h1 style='font-size:150%'>Python Newb's Twitter Search</h1>
<h2 style='font-size:125%'>Searching Twitter for: %s</h2>
<h2 style='font-size:125%'>%s (updates every 60 seconds)</h2>""" % (query, time.ctime()))

for datum in reversed(data):
    f.write("<p style='font-size:90%'>%s</p>" % (datum))

f.write("</body></html>")

另外,我稍微简化了一下你的循环;如果我说的你不太明白,我可以再解释。

如果你想把你的 WebOutput 函数转换成 Mako,你需要在文件的顶部先导入 mako,代码是:

import mako

然后你可以用下面的代码替换掉 WebOutput() 的整个主体:

f = file("outw.html", "w")
data = reversed(data)
t = Template(filename='/path/to/mytmpl.txt').render({"query":query, "time":time.ctime(), "data":data})
f.write(t)

最后,你需要创建一个文件 /path/to/mytmpl.txt,内容如下:

<html>
<title>python newb's twitter search</title>
<head><meta http-equiv='refresh' content='60'></head>
<body>
<h1 style='font-size:150%'>Python Newb's Twitter Search</h1>
<h2 style='font-size:125%'>Searching Twitter for: ${query}</h2>
<h2 style='font-size:125%'>${time} (updates every 60 seconds)</h2>

% for datum in data:
    <p style'font-size:90%'>${datum}</p>
% endfor

</body>
</html>

你会发现这样做的好处是把输出(在网页术语中叫“视图层”)和获取及格式化数据的代码(“模型层”和“控制层”)分开了。这将使你在未来更容易修改脚本的输出。

(注意:我没有测试这里提供的代码,如果有不对的地方请见谅。不过基本上应该是可以工作的。)

撰写回答