无法理解 flask url 使用表单输入的路由
Trouble understanding flask url routing with input from form
我一直在玩弄 Flask,并创建了一个简单的网络抓取应用程序,用于收集新闻文章,然后将它们放入 dataTables JQuery 插件中,以便在 HTML 中输出。当 ticker
被硬编码时,stockNews(ticker) 函数工作得很好。但是,我想更进一步,通过 html 表单检索 ticker
的用户输入。这是我遇到问题的地方,我尝试查找教程并阅读烧瓶文档,但似乎无法使应用程序按预期工作。
我想要达到的目标:
- 在页面加载时呈现 index.html
- 仅当用户通过 html 表单
提交股票代码时 运行 函数 stockNews(ticker)
- 路由到新页面
/<ticker>
作为新页面 URL
当前情况:
AttributeError: 'NoneType' object has no attribute 'find_all'
这是由我的函数 运行ning 在用户以 html 表格输入股票代码之前引起的。
app.py
import pandas as pd
import datetime as dt
import time
import requests
from tabulate import tabulate
from bs4 import BeautifulSoup
from flask import Flask, render_template, request
import json
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
# retreives user input
ticker = request.form.get('ticker')
return render_template('index.html', ticker=ticker)
@app.route('/<ticker>', methods=['GET', 'POST'])
index()
# scrapes stock news from finviz
def stockNews(ticker):
url = 'https://finviz.com/quote.ashx?t=' + ticker
html = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
soup = BeautifulSoup(html.text, 'lxml')
# finds news table within finviz website
match = soup.find('table', class_="fullview-news-outer")
dates = []
time = []
# appends dates in html to list
for d in match.find_all("td", width="130"):
if len(d.text.split(' ')) == 2:
dates.append(d.text.split(' ')[0])
time.append(d.text.split(' ')[1])
elif len(d.text.split(' ')) == 1:
dates.append('None')
time.append(d.text.split(' ')[0])
# uses an assignment expression to replace 'None' with previous element in list
dates = [current:=d if d != 'None' else current for d in dates]
articles = []
# appends new title to titles list
for t in match.find_all("a", class_="tab-link-news"):
match.find(class_='tab-link-news')['class'] = "news-link"
articles.append(str(t))
df_news = pd.DataFrame(list(zip(dates, time, articles)), columns=['Date', 'Time', 'Article'])
# formats Date column/datetime string in dataframe
df_news['Date'] = pd.to_datetime(df_news['Date'], errors='ignore').dt.strftime('%Y-%m-%d')
json_news = json.loads(df_news.to_json(orient='records'))
return render_template('index.html', json_news=json_news)
index.html
{% extends 'base.html' %}
{% block title %}
<title>Stock Info</title>
{% endblock %}
{% block body %}
<center>
<form method="POST">
<input name="ticker">
<input type="submit">
</form>
</center>
<div class="container">
<h1 class="header">News</h1>
<table class="table table-striped table-sm" id='news' style="width: 100%;">
<thead style='position: relative; width: 100%;'>
<tr>
<th>Date</th>
<th>Time</th>
<th>Article</th>
</tr>
</thead>
</table>
</div>
<script>
var news = {{ json_news | safe }};
$(document).ready(function() {
$('#news').DataTable( {
"data": news,
"scrollY": 600,
"paging": false,
"scrollCollapse": true,
"order": [[ 0, "desc" ]],
"columns": [
{ "data": "Date" },
{ "data": "Time" },
{ "data": "Article" },
]
} )
} );
</script>
{% endblock %}
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- CSS only -->
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/slate/bootstrap.min.css" rel="stylesheet" integrity="sha384-idNH3UIOiZbCf8jxqu4iExnH34y5UovfW/Mg8T5WfNvoJolDvknoNqR69V2OexgF" crossorigin="anonymous">
<link href="https://cdn.datatables.net/1.10.21/css/dataTables.bootstrap4.min.css" rel="stylesheet"/>
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css" rel="stylesheet"/>
<link href="https://cdn.datatables.net/1.10.21/js/dataTables.bootstrap4.min.js" rel="stylesheet" type="text/css">
{% block title %} {% endblock %}
</head>
<body>
{% block body %} {% endblock %}
<!-- JS, Popper.js, and jQuery -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.js"></script>
</body>
</html>
属性错误
我认为你试图获得 td 的方式是问题所在,
这样的事情可能更容易做到:
match = soup.findAll('table', {'class':'fullview-news-outer'})
rows = match.findAll('tr')
for row in rows:
k = row.findAll('td') #This (k) is the td
路由选择
你遗漏了一件我认为可以解决你问题的主要事情。
在定义路由时,它的方法如 POST 或 GET,您需要满足它们。
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
ticker = request.form['ticker']
#data processing or loading tables etc.
return render_template('index.html', ticker=ticker)
else:
#Normal Page Load
return render_template("index.html", ticker=None)
您可能还希望在您的 html 中使用 if statemen 允许 none 类型的自动收报机:
{% if ticker == None %} No news {% endif %}
我一直在玩弄 Flask,并创建了一个简单的网络抓取应用程序,用于收集新闻文章,然后将它们放入 dataTables JQuery 插件中,以便在 HTML 中输出。当 ticker
被硬编码时,stockNews(ticker) 函数工作得很好。但是,我想更进一步,通过 html 表单检索 ticker
的用户输入。这是我遇到问题的地方,我尝试查找教程并阅读烧瓶文档,但似乎无法使应用程序按预期工作。
我想要达到的目标:
- 在页面加载时呈现 index.html
- 仅当用户通过 html 表单 提交股票代码时 运行 函数
- 路由到新页面
/<ticker>
作为新页面 URL
stockNews(ticker)
当前情况:
AttributeError: 'NoneType' object has no attribute 'find_all'
这是由我的函数 运行ning 在用户以 html 表格输入股票代码之前引起的。
app.py
import pandas as pd
import datetime as dt
import time
import requests
from tabulate import tabulate
from bs4 import BeautifulSoup
from flask import Flask, render_template, request
import json
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
# retreives user input
ticker = request.form.get('ticker')
return render_template('index.html', ticker=ticker)
@app.route('/<ticker>', methods=['GET', 'POST'])
index()
# scrapes stock news from finviz
def stockNews(ticker):
url = 'https://finviz.com/quote.ashx?t=' + ticker
html = requests.get(url, headers={'User-Agent': 'Mozilla/5.0'})
soup = BeautifulSoup(html.text, 'lxml')
# finds news table within finviz website
match = soup.find('table', class_="fullview-news-outer")
dates = []
time = []
# appends dates in html to list
for d in match.find_all("td", width="130"):
if len(d.text.split(' ')) == 2:
dates.append(d.text.split(' ')[0])
time.append(d.text.split(' ')[1])
elif len(d.text.split(' ')) == 1:
dates.append('None')
time.append(d.text.split(' ')[0])
# uses an assignment expression to replace 'None' with previous element in list
dates = [current:=d if d != 'None' else current for d in dates]
articles = []
# appends new title to titles list
for t in match.find_all("a", class_="tab-link-news"):
match.find(class_='tab-link-news')['class'] = "news-link"
articles.append(str(t))
df_news = pd.DataFrame(list(zip(dates, time, articles)), columns=['Date', 'Time', 'Article'])
# formats Date column/datetime string in dataframe
df_news['Date'] = pd.to_datetime(df_news['Date'], errors='ignore').dt.strftime('%Y-%m-%d')
json_news = json.loads(df_news.to_json(orient='records'))
return render_template('index.html', json_news=json_news)
index.html
{% extends 'base.html' %}
{% block title %}
<title>Stock Info</title>
{% endblock %}
{% block body %}
<center>
<form method="POST">
<input name="ticker">
<input type="submit">
</form>
</center>
<div class="container">
<h1 class="header">News</h1>
<table class="table table-striped table-sm" id='news' style="width: 100%;">
<thead style='position: relative; width: 100%;'>
<tr>
<th>Date</th>
<th>Time</th>
<th>Article</th>
</tr>
</thead>
</table>
</div>
<script>
var news = {{ json_news | safe }};
$(document).ready(function() {
$('#news').DataTable( {
"data": news,
"scrollY": 600,
"paging": false,
"scrollCollapse": true,
"order": [[ 0, "desc" ]],
"columns": [
{ "data": "Date" },
{ "data": "Time" },
{ "data": "Article" },
]
} )
} );
</script>
{% endblock %}
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- CSS only -->
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/slate/bootstrap.min.css" rel="stylesheet" integrity="sha384-idNH3UIOiZbCf8jxqu4iExnH34y5UovfW/Mg8T5WfNvoJolDvknoNqR69V2OexgF" crossorigin="anonymous">
<link href="https://cdn.datatables.net/1.10.21/css/dataTables.bootstrap4.min.css" rel="stylesheet"/>
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/simplebar@latest/dist/simplebar.css" rel="stylesheet"/>
<link href="https://cdn.datatables.net/1.10.21/js/dataTables.bootstrap4.min.js" rel="stylesheet" type="text/css">
{% block title %} {% endblock %}
</head>
<body>
{% block body %} {% endblock %}
<!-- JS, Popper.js, and jQuery -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.js"></script>
</body>
</html>
属性错误
我认为你试图获得 td 的方式是问题所在, 这样的事情可能更容易做到:
match = soup.findAll('table', {'class':'fullview-news-outer'})
rows = match.findAll('tr')
for row in rows:
k = row.findAll('td') #This (k) is the td
路由选择
你遗漏了一件我认为可以解决你问题的主要事情。 在定义路由时,它的方法如 POST 或 GET,您需要满足它们。
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
ticker = request.form['ticker']
#data processing or loading tables etc.
return render_template('index.html', ticker=ticker)
else:
#Normal Page Load
return render_template("index.html", ticker=None)
您可能还希望在您的 html 中使用 if statemen 允许 none 类型的自动收报机:
{% if ticker == None %} No news {% endif %}