Python生成HTML输出(第一次尝试),几个问题(包含代码)
我玩Python已经几个月了(只是个爱好者),但对网页编程了解得很少(会一点HTML,完全不会JavaScript等等)。不过,我现在有一个项目,让我第一次接触网页编程。这让我想问:
感谢大家的回答,我有了一些进展。目前,我只在用Python和HTML。我不能发布我的项目代码,所以我写了一个小例子,使用了Twitter搜索(请看下面)。
我有几个问题:
我这样做会不会很傻?我觉得
WebOutput()这个方法虽然清晰,但效率不高。如果我用JavaScript的话,是不是可以写一个HTML模板文件,然后只更新数据?对吧?这样做会更好吗?对于这样的应用程序,什么时候使用框架比较合适?会不会太复杂了?
抱歉问这些基础问题——但我不想花太多时间走错路。
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 个回答
字符串格式化可以让代码看起来更整洁,也更不容易出错。
举个简单的例子,%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 的缓存选项……不过这个回答变得有点长了……
用框架来做这样的事情并不是多余的;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>
你会发现这样做的好处是把输出(在网页术语中叫“视图层”)和获取及格式化数据的代码(“模型层”和“控制层”)分开了。这将使你在未来更容易修改脚本的输出。
(注意:我没有测试这里提供的代码,如果有不对的地方请见谅。不过基本上应该是可以工作的。)