質問編集履歴
1
test
CHANGED
File without changes
|
test
CHANGED
@@ -1,189 +1,260 @@
|
|
1
1
|
### 前提・実現したいこと
|
2
2
|
|
3
|
-
|
4
|
-
|
5
3
|
Python・flaskで作ったwebアプリをherokuでデプロイし成功したものの、URLに飛ぶとApplication errorとなってしまう。
|
6
4
|
|
7
|
-
|
8
|
-
|
9
5
|
プログラミング初心者です。分かりづらい点があるかと思います、ご容赦ください。
|
10
6
|
|
11
|
-
|
12
|
-
|
13
7
|
### 発生している問題・エラーメッセージ
|
14
|
-
|
15
8
|
エラーコードはH10。問題箇所と思われるメッセージはこちらです。
|
16
|
-
|
17
|
-
```
|
9
|
+
```
|
18
|
-
|
19
10
|
Failed to find attribute 'application' in 'app'.
|
20
|
-
|
21
|
-
```
|
11
|
+
```
|
22
|
-
|
23
|
-
|
24
12
|
|
25
13
|
### 該当のソースコード
|
26
|
-
|
27
14
|
該当ファイルの構成は以下です。
|
28
15
|
|
29
|
-
|
30
|
-
|
31
16
|
アプリのファイル
|
32
|
-
|
33
17
|
|ーーapp.py
|
34
|
-
|
35
18
|
|ーーProcfile
|
36
|
-
|
37
19
|
|ーーrequirements.txt
|
38
|
-
|
39
20
|
|ーーruntime.txt
|
40
|
-
|
41
21
|
|ーー__pycache__
|
42
|
-
|
43
22
|
|ーーtemplates
|
44
|
-
|
45
23
|
|ーーvenv
|
46
|
-
|
24
|
+
|ーー.env
|
47
|
-
|
25
|
+
|ーー.env.sample
|
48
26
|
|
49
27
|
□「heroku logs --tail --app 自分のアプリ名」の実行結果
|
50
28
|
|
51
|
-
|
52
|
-
|
53
|
-
```
|
29
|
+
```
|
54
|
-
|
55
30
|
2022-01-11T11:06:47.556703+00:00 heroku[web.1]: State changed from crashed to starting
|
56
|
-
|
57
31
|
2022-01-11T11:06:52.498156+00:00 heroku[web.1]: Starting process with command `gunicorn app : app --log-file -`
|
58
|
-
|
59
32
|
2022-01-11T11:06:53.838531+00:00 app[web.1]: [2022-01-11 11:06:53 +0000] [4] [INFO] Starting gunicorn 20.1.0
|
60
|
-
|
61
33
|
2022-01-11T11:06:53.838940+00:00 app[web.1]: [2022-01-11 11:06:53 +0000] [4] [INFO] Listening at: http://0.0.0.0:24665 (4)
|
62
|
-
|
63
34
|
2022-01-11T11:06:53.838980+00:00 app[web.1]: [2022-01-11 11:06:53 +0000] [4] [INFO] Using worker: sync
|
64
|
-
|
65
35
|
2022-01-11T11:06:53.842165+00:00 app[web.1]: [2022-01-11 11:06:53 +0000] [9] [INFO] Booting worker with pid: 9
|
66
|
-
|
67
36
|
2022-01-11T11:06:53.853462+00:00 app[web.1]: [2022-01-11 11:06:53 +0000] [10] [INFO] Booting worker with pid: 10
|
68
|
-
|
69
37
|
2022-01-11T11:06:54.366422+00:00 heroku[web.1]: State changed from starting to up
|
70
|
-
|
71
38
|
2022-01-11T11:06:55.711302+00:00 app[web.1]: Failed to find attribute 'application' in 'app'.
|
72
|
-
|
73
39
|
2022-01-11T11:06:55.711529+00:00 app[web.1]: [2022-01-11 11:06:55 +0000] [9] [INFO] Worker exiting (pid: 9)
|
74
|
-
|
75
40
|
2022-01-11T11:06:55.711605+00:00 app[web.1]: Failed to find attribute 'application' in 'app'.
|
76
|
-
|
77
41
|
2022-01-11T11:06:55.711847+00:00 app[web.1]: [2022-01-11 11:06:55 +0000] [10] [INFO] Worker exiting (pid: 10)
|
78
|
-
|
79
42
|
2022-01-11T11:06:56.299718+00:00 app[web.1]: [2022-01-11 11:06:56 +0000] [4] [WARNING] Worker with pid 10 was terminated due to signal 15
|
80
|
-
|
81
43
|
2022-01-11T11:06:56.395344+00:00 app[web.1]: [2022-01-11 11:06:56 +0000] [4] [INFO] Shutting down: Master
|
82
|
-
|
83
44
|
2022-01-11T11:06:56.395413+00:00 app[web.1]: [2022-01-11 11:06:56 +0000] [4] [INFO] Reason: App failed to load.
|
84
|
-
|
85
45
|
2022-01-11T11:06:56.557625+00:00 heroku[web.1]: Process exited with status 4
|
86
|
-
|
87
46
|
2022-01-11T11:06:56.610039+00:00 heroku[web.1]: State changed from up to crashed
|
88
|
-
|
89
47
|
2022-01-11T11:06:58.000000+00:00 app[api]: Build succeeded
|
90
|
-
|
91
48
|
2022-01-11T11:07:02.198343+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/" host=foodbook135.herokuapp.com request_id=fc34bf91-5ec2-44a0-ac3d-fc6e828ba59c fwd="106.154.137.195" dyno= connect= service= status=503 bytes= protocol=https
|
92
|
-
|
93
49
|
2022-01-11T11:07:02.822734+00:00 heroku[router]: at=error code=H10 desc="App crashed" method=GET path="/favicon.ico" host=foodbook135.herokuapp.com request_id=fe1c5296-7855-4f26-b958-f03f77e4fc6d fwd="106.154.137.195" dyno= connect= service= status=503 bytes= protocol=https
|
94
|
-
|
95
|
-
```
|
50
|
+
```
|
96
|
-
|
97
|
-
|
98
51
|
|
99
52
|
```Procfile
|
100
|
-
|
101
53
|
web: gunicorn app : app --log-file -
|
102
|
-
|
103
|
-
```
|
54
|
+
```
|
104
|
-
|
105
|
-
|
106
55
|
|
107
56
|
```requirements
|
108
|
-
|
109
57
|
beautifulsoup4==4.10.0
|
110
|
-
|
111
58
|
cachetools==4.2.4
|
112
|
-
|
113
59
|
certifi==2021.10.8
|
114
|
-
|
115
60
|
charset-normalizer==2.0.9
|
116
|
-
|
117
61
|
click==8.0.3
|
118
|
-
|
119
62
|
Flask==2.0.2
|
120
|
-
|
121
63
|
google-api-core==2.3.2
|
122
|
-
|
123
64
|
google-api-python-client==2.33.0
|
124
|
-
|
125
65
|
google-auth==2.3.3
|
126
|
-
|
127
66
|
google-auth-httplib2==0.1.0
|
128
|
-
|
129
67
|
googleapis-common-protos==1.54.0
|
130
|
-
|
131
68
|
gunicorn==20.1.0
|
132
|
-
|
133
69
|
httplib2==0.20.2
|
134
|
-
|
135
70
|
idna==3.3
|
136
|
-
|
137
71
|
itsdangerous==2.0.1
|
138
|
-
|
139
72
|
Jinja2==3.0.3
|
140
|
-
|
141
73
|
MarkupSafe==2.0.1
|
142
|
-
|
143
74
|
protobuf==3.19.1
|
144
|
-
|
145
75
|
pyasn1==0.4.8
|
146
|
-
|
147
76
|
pyasn1-modules==0.2.8
|
148
|
-
|
149
77
|
pyparsing==3.0.6
|
150
|
-
|
151
78
|
requests==2.26.0
|
152
|
-
|
153
79
|
rsa==4.8
|
154
|
-
|
155
80
|
six==1.16.0
|
156
|
-
|
157
81
|
soupsieve==2.3.1
|
158
|
-
|
159
82
|
uritemplate==4.1.1
|
160
|
-
|
161
83
|
urllib3==1.26.7
|
162
|
-
|
163
84
|
Werkzeug==2.0.2
|
164
85
|
|
165
|
-
|
166
|
-
|
167
|
-
```
|
86
|
+
```
|
87
|
+
|
168
|
-
|
88
|
+
```app.py
|
169
|
-
|
89
|
+
# -*- coding: utf-8 -*-
|
90
|
+
from bs4 import BeautifulSoup
|
91
|
+
import re
|
92
|
+
from flask import Flask, render_template, request, session
|
93
|
+
import requests
|
94
|
+
import settings
|
95
|
+
from requests.exceptions import Timeout
|
96
|
+
# 返却された検索結果の読み取りにつかう
|
97
|
+
from googleapiclient.discovery import build
|
98
|
+
|
99
|
+
# =================================
|
100
|
+
app = Flask(__name__)
|
101
|
+
|
102
|
+
# カスタム検索エンジンID
|
103
|
+
CUSTOM_SEARCH_ENGINE_ID = "87dabc623cf5e8624"
|
104
|
+
# API キー
|
105
|
+
API_KEY = settings.AP
|
106
|
+
|
107
|
+
# APIにアクセスして結果をもらってくるメソッド
|
108
|
+
def get_search_results(query):
|
109
|
+
|
110
|
+
# APIでやりとりするためのリソースを構築
|
111
|
+
search = build(
|
112
|
+
"customsearch",
|
113
|
+
"v1",
|
114
|
+
developerKey = API_KEY
|
115
|
+
)
|
116
|
+
|
117
|
+
# Google Custom Search から結果を取得
|
118
|
+
result = search.cse().list(
|
119
|
+
q = query,
|
120
|
+
cx = CUSTOM_SEARCH_ENGINE_ID,
|
121
|
+
lr = 'lang_ja',
|
122
|
+
num = 5,
|
123
|
+
start = 1
|
124
|
+
).execute()
|
125
|
+
|
126
|
+
# 受け取ったjsonをそのまま返却
|
127
|
+
return result
|
128
|
+
|
129
|
+
# 検索結果の情報をSearchResultに格納してリストで返す
|
130
|
+
def summarize_search_results(result):
|
131
|
+
|
132
|
+
# 結果のjsonから検索結果の部分を取り出しておく
|
133
|
+
result_items_part = result['items']
|
134
|
+
|
135
|
+
# 抽出した検索結果の情報はこのリストにまとめる
|
136
|
+
result_items = []
|
137
|
+
|
138
|
+
# 今回は (start =) 1 個目の結果から (num =) 10 個の結果を取得した
|
139
|
+
for i in range(0, 5):
|
140
|
+
# i番目の検索結果の部分
|
141
|
+
result_item = result_items_part[i]
|
142
|
+
# i番目の検索結果からそれぞれの属性の情報をResultクラスに格納して
|
143
|
+
# result_items リストに追加する
|
144
|
+
s_r = SearchResult(
|
145
|
+
title = result_item['title'],
|
146
|
+
url = result_item['link'],
|
147
|
+
snippet = result_item['snippet']
|
148
|
+
)
|
149
|
+
|
150
|
+
result_items.append(s_r.Making_d())
|
151
|
+
|
152
|
+
# 結果を格納したリストを返却
|
153
|
+
return result_items
|
154
|
+
|
155
|
+
# 検索結果の情報を格納するクラス
|
156
|
+
class SearchResult:
|
157
|
+
def __init__(self, title, url, snippet):
|
158
|
+
self.title = title
|
159
|
+
self.url = url
|
160
|
+
self.snippet = snippet
|
161
|
+
|
162
|
+
def __str__(self):
|
163
|
+
# コマンドライン上での表示形式はご自由にどうぞ
|
164
|
+
return "[title] " + self.title + "\n\t[detail] " + self.snippet + "\n\t[url]" + self.url
|
165
|
+
|
166
|
+
def Making_d(self):
|
167
|
+
d_info = dict(title=self.title, url=self.url, detail=self.snippet)
|
168
|
+
return d_info
|
169
|
+
|
170
|
+
# htmlとやり取りするパート
|
171
|
+
app = Flask(__name__)
|
172
|
+
|
173
|
+
app.secret_key = 'abcdefghijklmn'
|
174
|
+
|
175
|
+
@app.route('/', methods=['GET'])
|
176
|
+
def get():
|
177
|
+
return render_template('index.html')
|
178
|
+
|
179
|
+
# フォームを読み込んで検索結果を出す =page2
|
180
|
+
@app.route('/templates/page2', methods=['POST'])
|
181
|
+
def post():
|
182
|
+
name = request.form.get('name')
|
183
|
+
prefecture = request.form.get('prefectures')
|
184
|
+
query = name
|
185
|
+
query2 = prefecture
|
186
|
+
query3 = '"食べログ"'
|
187
|
+
the_word = query + " " + query2 + " " + query3
|
188
|
+
|
189
|
+
# APIから検索結果を取得
|
190
|
+
result = get_search_results(the_word)
|
191
|
+
# result には 返却されたjsonが入る
|
192
|
+
|
193
|
+
# 検索結果情報からタイトル, URL, スニペット, 検索結果の順位を抽出してまとめる
|
194
|
+
result_items_list = summarize_search_results(result)
|
195
|
+
# result_items_list には SearchResult のリストが入る
|
196
|
+
|
197
|
+
# 他のページへ渡せるようにsessionを使う
|
198
|
+
session['result_items_list'] = result_items_list
|
199
|
+
|
200
|
+
# コマンドラインに検索結果の情報を1個分だけ表示
|
201
|
+
print(result_items_list[0])
|
202
|
+
# print(query2)
|
203
|
+
# 第2引数で、htmlファイル上の変数にここで用いたリストを代入している
|
204
|
+
return render_template('page2.html', rst_info=result_items_list)
|
205
|
+
|
206
|
+
# 1つ目の情報が誤っていたときに2つ目の検索結果を表示する=page3
|
207
|
+
@app.route('/templates/page3', methods=["get"])
|
208
|
+
def page3():
|
209
|
+
# indexからpage2への移動時に作成された検索結果のリストをsessionを使って呼び出す
|
210
|
+
searched_list = session.get('result_items_list', None)
|
211
|
+
|
212
|
+
print(searched_list[1])
|
213
|
+
return render_template('page3.html', rst_info=searched_list)
|
214
|
+
|
215
|
+
# page2での情報でOKだったときに、その店の詳細を表示させる=goal
|
216
|
+
@app.route('/templates/goal', methods=["get"])
|
217
|
+
def goal():
|
218
|
+
|
219
|
+
searched_list = session.get('result_items_list', None)
|
220
|
+
#↓OKが出た店の食べログURLでスクレイピング
|
221
|
+
url_t = searched_list[0]['url']
|
222
|
+
r = requests.get(url_t)
|
223
|
+
soup = BeautifulSoup(r.text, 'html.parser')
|
224
|
+
name_rst = soup.find('div', class_='rstinfo-table__name-wrap').text
|
225
|
+
station_rst = soup.find('span', class_='linktree__parent-target-text').text
|
226
|
+
tel_rst = soup.find('p', class_='rstdtl-side-yoyaku__tel-number').text
|
227
|
+
|
228
|
+
return render_template('goal.html', searched_list=searched_list, name_rst=name_rst, \
|
229
|
+
station_rst=station_rst, tel_rst=tel_rst)
|
230
|
+
|
231
|
+
# 情報2件が両方とも不適切だった場合、はじめに戻る=fail
|
232
|
+
@app.route('/templates/fail', methods=["get"])
|
233
|
+
def fail():
|
234
|
+
return render_template('fail.html')
|
235
|
+
|
236
|
+
# xxxxxxxxxxxxxxxxxxxxxxxxxx
|
237
|
+
if __name__ == '__main__':
|
238
|
+
# app.run(debug=True)
|
239
|
+
app.debug = True
|
240
|
+
app.run()
|
241
|
+
# xxxxxxxxxxxxxxxxxxxxxxxxxx
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
```
|
170
250
|
|
171
251
|
### 試したこと
|
172
252
|
|
173
|
-
|
174
|
-
|
175
253
|
調べる中でProcfileが特にあやしいと感じ、いろいろと英語のサイトなどにも当たりましたが原因がわかりませんでした。
|
176
254
|
|
177
|
-
|
178
|
-
|
179
255
|
### 特に参考にしたページ
|
180
256
|
|
181
|
-
|
182
|
-
|
183
257
|
https://toukei-lab.com/heroku-python
|
184
|
-
|
185
258
|
https://creepfablic.site/2019/06/15/heroku-procfile-matome/
|
186
259
|
|
187
|
-
|
188
|
-
|
189
260
|
よろしくお願いいたします。
|