Skip to main content
 首页 » 编程设计

python之静态 HTML + Flask + Gunicorn 在本地工作;推到 heroku 时中断

2025年05月04日7wuhuacong

我刚刚制作了一个小 flask 应用程序,它在本地工作,但是当我尝试将它推送到 heroku 时,在提供静态 HTML 时出现了一些问题( self 诊断,当我让 flask 只返回文本而不是静态 html 文件时一切都按预期工作。)

我试着按照这个指南.. https://devcenter.heroku.com/articles/getting-started-with-python 这是我目录中的 LS

Procfile        account.pyc     base.pyc        requirements.txt    venv 
account.py      base.py         home.html       templates 

这里是一些相关文件的代码。

需求.txt

Flask==0.10.1 
Jinja2==2.7.1 
MarkupSafe==0.18 
Werkzeug==0.9.4 
gunicorn==18.0 
heroku==0.1.4 
httplib2==0.8 
itsdangerous==0.23 
oauth2==1.5.211 
python-dateutil==1.5 
requests==2.0.0 
wsgiref==0.1.2 

混帐

venv 
*.pyc 

简介

web: gunicorn base:app 

account.py(从我的股票经纪人那里获取数据的东西)

 token    = oauth.Token(key=_ACCESS_TOKEN_, secret=_ACCESS_TOKEN_SECRET_) 
 consumer = oauth.Consumer(key=_CONSUMER_KEY_, secret=_CONSUMER_SECRET_) 
 request_holdings_url = "%s/accounts/%s/holdings.json" % (_BASE_URL_, _ACCOUNT_NUMBER_) 
 request_values_url = "%s/market/ext/quotes.xml?" % _BASE_URL_ 
 #creating the client 
 client = oauth.Client(consumer, token=token) 
 
 #making the request via the client 
 
 stocksym = {} 
 
 
 def urlquery(): 
 # get all of my tradeking info 
     resp, content = client.request(request_holdings_url, "GET") 
     content =  json.loads(content) 
     return content 
 
 
 def getstocksym(json): 
     # returns the holdings under my TK acct 
     allholdings = json[u"response"][u"accountholdings"][u"holding"] 
     for stock in allholdings: 
         sym = stock[u"instrument"][u"sym"] 
         stocksym.update({sym: 0}) 
     return stocksym 
 
 
 def stockvalue(stocks): 
     # returns a dictionary with stock: price: change: 
     stockvalues = {} 
     for stock in stocks: 
         resp, content = client.request("https://api.tradeking.com/v1/market/ext/quotes.json?symbols=%s" % stock, "GET") 
         content = json.loads(content) 
         change = content[u"response"][u"quotes"][u"quote"][u"chg_sign"] 
         price = content[u"response"][u"quotes"][u"quote"][u"ask"] 
         stockvalues.update({stock: {'price':price, 'change':change}}) 
     return stockvalues 
 
 def parsevalues(stockvalues): 
     #parses library, returns whether or not a stock in the dictionary is changing, and what price it is. 
     values = [] 
     for stock in stockvalues: 
         price = stockvalues[stock][u"price"] 
         if stockvalues[stock][u"change"] == 'u': 
             return "%s is going up! and the price is: %s" % (stock, price) 
         elif stockvalues[stock][u"change"] == 'd': 
            return "%s is going down! and the price is %s" % (stock, price) 
 
 
 if __name__ == "__main__": 
    parsevalues(stockvalue(getstocksym(urlquery()))) 

基础.py

from flask import Flask, jsonify, render_template 
import account 
app = Flask(__name__) 
 
 
@app.route("/") 
def home(): 
   values = account.stockvalue(account.getstocksym(account.urlquery())) 
   json = jsonify(values) 
   json = values 
   return render_template('home.html', json=json) 
 
if __name__ == "__main__": 
   app.run() 

主页.html

 <!doctype html> 
 <html> 
     <head> 
         <title>  
            stock app!  
         </title> 
     </head> 
     <body> 
         <ul> 
             {% for stock, attr in json.iteritems() %} 
                 <li> 
 
                 {{ stock }} - {{attr.price}} <div style="display: inline-block; width: 10px; height: 10px; border-radius: 5px; background-color: {% if attr.    change == 'u' %} green  {% else %} red {% endif %}"></div> 
 
                 </li> 
 
             {% endfor %} 
         </ul> 
     </body> 
 </html> 

应用程序现在在本地成功运行,并由工头启动。

(venv)Eriks-MacBook-Pro:stockheroku erikmingo$ foreman start 
17:57:31 web.1  | started with pid 752 
17:57:32 web.1  | 2013-10-21 17:57:32 [752] [INFO] Starting gunicorn 18.0 
17:57:32 web.1  | 2013-10-21 17:57:32 [752] [INFO] Listening at: http://0.0.0.0:5000 (752) 
17:57:32 web.1  | 2013-10-21 17:57:32 [752] [INFO] Using worker: sync 
17:57:32 web.1  | 2013-10-21 17:57:32 [755] [INFO] Booting worker with pid: 755 

所以此时我运行:

git add . 
git commit -m "this works locally, maybe it will work on heroku!" 
git push heroku master 

还给我这个:

Counting objects: 9, done. 
Delta compression using up to 2 threads. 
Compressing objects: 100% (5/5), done. 
Writing objects: 100% (5/5), 463 bytes, done. 
Total 5 (delta 3), reused 0 (delta 0) 
 
-----> Python app detected 
-----> No runtime.txt provided; assuming python-2.7.4. 
-----> Using Python runtime (python-2.7.4) 
-----> Installing dependencies using Pip (1.3.1) 
       Cleaning up... 
-----> Discovering process types 
       Procfile declares types -> web 
 
-----> Compiled slug size: 29.5MB 
-----> Launching... done, v28 
       http://still-fortress-2740.herokuapp.com deployed to Heroku 
 
To git@heroku.com:still-fortress-2740.git 
   34025ca..ecba67c  master -> master 

当我输入 heroku open 时,出现 500 错误。

这是 heroku 日志:

2013-10-22T01:26:32.722581+00:00 heroku[router]: at=info method=GET path=/ host=still-fortress-2740.herokuapp.com fwd="67.176.81.118" dyno=web.1 connect=1ms service=30ms status=500 bytes=135 
2013-10-22T01:26:32.723565+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception 
2013-10-22T01:26:32.723565+00:00 app[web.1]:     reraise(exc_type, exc_value, tb) 
2013-10-22T01:26:32.723806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app 
2013-10-22T01:26:32.723806+00:00 app[web.1]:     response = self.full_dispatch_request() 
2013-10-22T01:26:32.723806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request 
2013-10-22T01:26:32.723806+00:00 app[web.1]:     rv = self.handle_user_exception(e) 
2013-10-22T01:26:32.723806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception 
2013-10-22T01:26:32.723806+00:00 app[web.1]:     reraise(exc_type, exc_value, tb) 
2013-10-22T01:26:32.723806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request 
2013-10-22T01:26:32.723806+00:00 app[web.1]:     rv = self.dispatch_request() 
2013-10-22T01:26:32.723806+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request 
2013-10-22T01:26:32.723806+00:00 app[web.1]:     return self.view_functions[rule.endpoint](**req.view_args) 
2013-10-22T01:26:32.723983+00:00 app[web.1]:   File "/app/base.py", line 8, in home 
2013-10-22T01:26:32.723983+00:00 app[web.1]:     values = account.stockvalue(account.getstocksym(account.urlquery())) 
2013-10-22T01:26:32.723983+00:00 app[web.1]:   File "/app/account.py", line 28, in urlquery 
2013-10-22T01:26:32.723983+00:00 app[web.1]:     resp, content = client.request(request_holdings_url, "GET") 
2013-10-22T01:26:32.723983+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/oauth2/__init__.py", line 682, in request 
2013-10-22T01:26:32.723983+00:00 app[web.1]:     connection_type=connection_type) 
2013-10-22T01:26:32.723983+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/httplib2/__init__.py", line 1570, in request 
2013-10-22T01:26:32.723983+00:00 app[web.1]:     (response, content) = self._request(conn, authority, uri, request_uri, method, body, headers, redirections, cachekey) 
2013-10-22T01:26:32.723983+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/httplib2/__init__.py", line 1317, in _request 
2013-10-22T01:26:32.723983+00:00 app[web.1]:     (response, content) = self._conn_request(conn, request_uri, method, body, headers) 
2013-10-22T01:26:32.724435+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/httplib2/__init__.py", line 1252, in _conn_request 
2013-10-22T01:26:32.724435+00:00 app[web.1]:     conn.connect() 
2013-10-22T01:26:32.724435+00:00 app[web.1]:   File "/app/.heroku/python/lib/python2.7/site-packages/httplib2/__init__.py", line 1044, in connect 
2013-10-22T01:26:32.724435+00:00 app[web.1]:     raise SSLHandshakeError(e) 
2013-10-22T01:26:32.724435+00:00 app[web.1]: SSLHandshakeError: [Errno 1] _ssl.c:504: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed 

这是我在 stackoverlow 上的第一篇帖子,我尽力遵循发帖指南。让我知道您是否有任何其他信息会有所帮助...就像我上面提到的,当我将“/”路由的返回更改为一个字符串时,该应用程序可以在 heroku 上运行。

提前致谢!

编辑:看来问题出在我的 API 调用上……Heroku 说这是错误的 SSL 握手之类的……但这到底意味着什么?它肯定在我的 account.py 文件中,但我不确定是什么。

请您参考如下方法:

account 模块一定有问题。当我像这样创建虚拟 account 时:

def stockvalue(foo): 
    return {'foo': 'bar'} 
 
def getstocksym(foo): 
    return None 
 
def urlquery(): 
    return None 

一切顺利。

编辑

尝试用这个替换 home 看看你会得到什么:

@app.route("/") 
def home(): 
   try: 
       values = account.stockvalue(account.getstocksym(account.urlquery())) 
   except Exception as e: 
       return jsonify({"error": str(e)}) 
   return jsonify(values) 

或者更好的是在 app = Flask(__name__) 之后添加 app.config['DEBUG'] = True。不要忘记在生产中删除它。虽然它对调试很有用,但并不是很安全

另一个编辑

您似乎遇到了缺少证书的问题。它应该有助于:

  1. 从 curl 站点下载证书:http://curl.haxx.se/ca/cacert.pem
  2. 将 cacert.pem 复制到您应用的根目录(account.py 所在的位置)
  3. git add .
  4. 修改account.py
import os 
import oauth2 as oauth 
# Other imports 
 
token    = oauth.Token(key=_ACCESS_TOKEN_, secret=_ACCESS_TOKEN_SECRET_) 
consumer = oauth.Consumer(key=_CONSUMER_KEY_, secret=_CONSUMER_SECRET_) 
request_holdings_url = "%s/accounts/%s/holdings.json" % (_BASE_URL_, _ACCOUNT_NUMBER_) 
request_values_url = "%s/market/ext/quotes.xml?" % _BASE_URL_ 
 
#creating the client 
client = oauth.Client(consumer, token=token) 
client.ca_certs = os.path.join(os.path.dirname(__file__), 'cacert.pem') 
 
#Rest of your code 
...  

提交并推送到 Heroku。