使用sql创建一个database,名称为:stock,在stock中创建一个表,名称为:stocks,
表中的各个字段为:
stock_code 最大6位整数数字,必须填写
stock_name, 最大20个汉字,必须填写
category, 最大20个汉字
introduction,最大20000个汉字
pinyin_initials, 最大20个字母
请给出SQL语句来。
CREATE DATABASE IF NOT EXISTS stock;
USE stock;
CREATE TABLE IF NOT EXISTS stocks (
stock_code VARCHAR(6) NOT NULL,
stock_name VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
category1 VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
category2 VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
category3 VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
introduction TEXT(20000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
pinyin_initials VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
record_date DATE NULL,
PRIMARY KEY (stock_code)
);
索引优化:确保远程数据库中的 stock_name、stock_code 和 pinyin_initials 字段上有合适的索引。
索引可以加快查询速度,减少数据库的扫描范围。例如,在 MySQL 中可以使用以下语句创建索引:
CREATE INDEX idx_stock_name ON stocks (stock_name);
CREATE INDEX idx_stock_code ON stocks (stock_code);
CREATE INDEX idx_pinyin_initials ON stocks (pinyin_initials);
你可以使用以下 SQL 语句查看 stocks
表中已有的索引:
SHOW INDEX FROM stocks;
用Python写一个程序,可以读取CSV文件,文件的第1列为stock_code,第2列为stock_name,如果有第3列,则第3列为category,
把CSV文件中的各行数据全部导入到上面的数据库中。
连接数据库的信息为:
# 数据库连接配置
db_config = {
'host': '192.168.137.130',
'user': 'root',
'password': '12345678',
'database': 'stock'
}
1. 查询表中数据的数量
-- 切换到 stock 数据库
USE stock;
-- 查询 stocks 表中的记录数量
SELECT COUNT(*) FROM stocks;
2. 查询第一条数据的内容
-- 切换到 stock 数据库
USE stock;
-- 查询按照 stock_code 升序排序后的第一条数据
SELECT * FROM stocks ORDER BY stock_code ASC LIMIT 1;
使用 DELETE 语句 DELETE 语句是一条 DML(数据操作语言)语句,它会逐行删除表中的数据,并且会记录每一行的删除操作到事务日志中。这意味着在删除大量数据时,DELETE 操作可能会比较慢,不过它可以配合 WHERE 子句来有选择性地删除数据。
-- 切换到 stock 数据库
USE stock;
-- 删除 stocks 表中的所有数据
DELETE FROM stocks;
使用 TRUNCATE 语句 TRUNCATE 语句是一条 DDL(数据定义语言)语句,它会直接删除整个表的数据页,而不是逐行删除数据,因此速度通常比 DELETE 快。但它不能使用 WHERE 子句进行条件删除,会直接清空整个表。
-- 切换到 stock 数据库
USE stock;
-- 清空 stocks 表
TRUNCATE TABLE stocks;
SQL删除stocks表和表中的所有的内容
删除表及其所有内容
-- 切换到 stock 数据库
USE stock;
-- 删除 stocks 表
DROP TABLE IF EXISTS stocks;
--------------------------
连接数据库时,配置中如果有'charset': 'utf8mb4',容易导致程序报错,例如:数据库操作或文件操作出错: Character set '255' unsupported,此时删除这一行即可。
# 远程 MySQL 数据库连接配置
mysql_config = {
'host': '118.178.92.143',
'user': 'read_only_user',
'password': 'Reading_Stocks%6688',
'database': 'stock',
'charset': 'utf8mb4'
}
下面的配置,可以正常运行。
# 远程 MySQL 数据库连接配置
mysql_config = {
'host': '118.178.92.143',
'user': 'read_only_user',
'password': 'Reading_Stocks%6688',
'database': 'stock',
'use_unicode': True
}
尤其是在SQLite复制MYSQL5.7数据库的数据时。
--------------------------
备份数据库:
mysqldump -u root -p stock > stock_backup.sql
回车后,输入数据库密码
导入备份文件:使用mysql命令将备份文件中的数据导入到数据库中,命令如下:
mysql -u root -p stock < stock_backup.sql
在恢复过程中,要确保运行命令的用户具有适当的权限,并且备份文件的路径是正确的。
如果数据库中已经存在相同的表,可能需要先删除表或使用--add - drop - table选项来处理,以避免出现错误。
--add-drop-table 选项是在使用 mysqldump 进行备份时使用的,它会在每个 CREATE TABLE 语句之前添加一个 DROP TABLE IF EXISTS 语句。
这样在恢复备份时,会先删除已存在的同名表,避免表冲突问题。
1. 使用 --add-drop-table 进行备份
在备份数据库时加上 --add-drop-table 选项,示例命令如下:
mysqldump -u root -p --add-drop-table stock > stock_backup_droptable.sql
执行该命令后,输入数据库用户 root 的密码,mysqldump 就会把 mydatabase 数据库的结构和数据备份到 mydatabase_backup.sql 文件中,并且在生成的 SQL 文件里,每个 CREATE TABLE 语句前都会有 DROP TABLE IF EXISTS 语句。
2. 恢复使用 --add-drop-table 选项备份的文件
在恢复备份时,使用以下命令:
mysql -u root -p stock < stock_backup_droptable.sql
执行此命令后,同样需要输入 root 用户的密码。mysql 会读取 mydatabase_backup.sql 文件中的 SQL 语句,在恢复每个表之前,会先执行 DROP TABLE IF EXISTS 语句尝试删除已存在的同名表,然后再重新创建表并插入数据。
------------------------------
从SQL缓存到Redis:
从远程SQL缓存到本地Redis的Python代码:
import mysql.connector
import redis
from datetime import date
# 远程 MySQL 数据库连接配置
mysql_config = {
'host': '远程IP地址',
'user':'用户名',
'password': '密码',
'database':'数据库名称',
'use_unicode': True
}
# 本地 Redis 连接配置
redis_config = {
'host': 'localhost',
'port': 6379,
'db': 0,
'password': None # 若本地 Redis 无密码,设为 None
}
def sync_data_from_mysql_to_redis():
try:
# 连接到本地 Redis 数据库
redis_client = redis.Redis(**redis_config)
try:
response = redis_client.ping()
if response:
print("成功连接到本地 Redis 数据库")
# 清空当前数据库中的所有数据
redis_client.flushdb()
print("Redis数据库已清空")
else:
print("Redis 连接成功但 PING 命令响应异常")
except redis.ConnectionError as ce:
print(f"Redis 连接错误: {ce}")
print("请检查 Redis 服务器地址、端口和网络连接是否正常。")
return
except redis.AuthenticationError as ae:
print(f"Redis 身份验证错误: {ae}")
print("请检查 Redis 密码是否正确。")
return
except Exception as e:
print(f"发生其他错误: {e}")
return
# 连接远程 MySQL 数据库
mysql_conn = mysql.connector.connect(**mysql_config)
print(f"成功连接到 MySQL 数据库: {mysql_conn}")
mysql_cursor = mysql_conn.cursor()
# 获取数据库中所有表名
mysql_cursor.execute("SHOW TABLES")
tables = [table[0] for table in mysql_cursor.fetchall()]
# 遍历所有表,复制数据到 Redis
for table in tables:
# 获取表的列名
mysql_cursor.execute(f"SHOW COLUMNS FROM {table}")
columns = [col[0] for col in mysql_cursor.fetchall()]
# 查询表中的所有数据
mysql_cursor.execute(f"SELECT * FROM {table}")
rows = mysql_cursor.fetchall()
for row in rows:
# 假设第一列是主键,根据实际情况调整
primary_key = str(row[0])
stock_name_index = columns.index('stock_name') if'stock_name' in columns else None
pinyin_initials_index = columns.index('pinyin_initials') if 'pinyin_initials' in columns else None
for i, column in enumerate(columns):
value = row[i]
if isinstance(value, date):
# 将 date 类型的数据转换为字符串
value = value.strftime('%Y-%m-%d')
if value is not None:
# 将数据以哈希表的形式存储在 Redis 中
redis_client.hset(primary_key, column, value)
# 以股票名称和拼音首字母为键建立索引
if stock_name_index is not None:
stock_name = row[stock_name_index]
# 对股票名称的每个子串都建立索引
for i in range(len(stock_name)):
for j in range(i + 1, len(stock_name) + 1):
sub_name = stock_name[i:j]
redis_client.sadd(sub_name, primary_key)
if pinyin_initials_index is not None:
pinyin_initials = row[pinyin_initials_index].upper()
# 对拼音首字母的每个子串都建立索引
for i in range(len(pinyin_initials)):
for j in range(i + 1, len(pinyin_initials) + 1):
sub_pinyin = pinyin_initials[i:j]
redis_client.sadd(sub_pinyin, primary_key)
# 关闭连接
mysql_cursor.close()
mysql_conn.close()
print("数据同步完成")
except mysql.connector.Error as err:
print(f"数据库操作出错: {err}")
if __name__ == "__main__":
sync_data_from_mysql_to_redis()
上面的代码,可以实现汉字和字母的双重循环来构建索引,加快查询,但是对于股票代码的数字不行,因数深圳的股票有很多是0开头的,所以不能实现以下功能:
比如:我希望输入600519进行查询时,在用数字查询时,在我输入6时自动查询一次,当我输入60这2个数字时也能查询,当我输入600第3个数字时也能够,依次类推。
这时需要通过使用Redis的不同数据库(db0和db1)来分别存储索引(SET)和详细数据(HASH),完美解决了同名键冲突的问题。db0存储索引(SET),db1存储详细数据(HASH),调整之后的代码如下:
import mysql.connector
import redis
from datetime import date
# 远程 MySQL 数据库连接配置
mysql_config = {
'host': '远程IP地址',
'user':'用户名',
'password': '密码',
'database':'数据库名称',
'use_unicode': True
}
# 本地 Redis 连接配置 - 索引数据库 (db0)
redis_index_config = {
'host': 'localhost',
'port': 6379,
'db': 0,
'password': None # 若本地 Redis 无密码,设为 None
}
# 本地 Redis 连接配置 - 股票详细信息数据库 (db1)
redis_stock_config = {
'host': 'localhost',
'port': 6379,
'db': 1,
'password': None # 若本地 Redis 无密码,设为 None
}
def sync_data_from_mysql_to_redis():
try:
# 连接到本地 Redis 索引数据库 (db0)
redis_index_client = redis.Redis(**redis_index_config)
try:
response = redis_index_client.ping()
if response:
print("成功连接到本地 Redis 索引数据库")
# 清空当前数据库中的所有数据
redis_index_client.flushdb()
print("Redis 索引数据库已清空")
else:
print("Redis 连接成功但 PING 命令响应异常")
except redis.ConnectionError as ce:
print(f"Redis 连接错误: {ce}")
print("请检查 Redis 服务器地址、端口和网络连接是否正常。")
return
except redis.AuthenticationError as ae:
print(f"Redis 身份验证错误: {ae}")
print("请检查 Redis 密码是否正确。")
return
except Exception as e:
print(f"发生其他错误: {e}")
return
# 连接到本地 Redis 股票详细信息数据库 (db1)
redis_stock_client = redis.Redis(**redis_stock_config)
try:
response = redis_stock_client.ping()
if response:
print("成功连接到本地 Redis 股票详细信息数据库")
# 清空当前数据库中的所有数据
redis_stock_client.flushdb()
print("Redis 股票详细信息数据库已清空")
else:
print("Redis 连接成功但 PING 命令响应异常")
except redis.ConnectionError as ce:
print(f"Redis 连接错误: {ce}")
print("请检查 Redis 服务器地址、端口和网络连接是否正常。")
return
except redis.AuthenticationError as ae:
print(f"Redis 身份验证错误: {ae}")
print("请检查 Redis 密码是否正确。")
return
except Exception as e:
print(f"发生其他错误: {e}")
return
# 连接远程 MySQL 数据库
mysql_conn = mysql.connector.connect(**mysql_config)
print(f"成功连接到 MySQL 数据库: {mysql_conn}")
mysql_cursor = mysql_conn.cursor()
# 获取数据库中所有表名
mysql_cursor.execute("SHOW TABLES")
tables = [table[0] for table in mysql_cursor.fetchall()]
# 遍历所有表,复制数据到 Redis
for table in tables:
# 获取表的列名
mysql_cursor.execute(f"SHOW COLUMNS FROM {table}")
columns = [col[0] for col in mysql_cursor.fetchall()]
# 查询表中的所有数据
mysql_cursor.execute(f"SELECT * FROM {table}")
rows = mysql_cursor.fetchall()
for row in rows:
# 假设第一列是主键,根据实际情况调整
primary_key = str(row[0])
stock_name_index = columns.index('stock_name') if'stock_name' in columns else None
pinyin_initials_index = columns.index('pinyin_initials') if 'pinyin_initials' in columns else None
for i, column in enumerate(columns):
value = row[i]
if isinstance(value, date):
# 将 date 类型的数据转换为字符串
value = value.strftime('%Y-%m-%d')
if value is not None:
# 将数据以哈希表的形式存储在 Redis 股票详细信息数据库 (db1) 中
redis_stock_client.hset(primary_key, column, value)
# 以股票名称建立索引,存储在 Redis 索引数据库 (db0) 中
if stock_name_index is not None:
stock_name = row[stock_name_index]
for i in range(len(stock_name)):
for j in range(i + 1, len(stock_name) + 1):
sub_name = stock_name[i:j]
redis_index_client.sadd(sub_name, primary_key)
# 以拼音首字母建立索引,存储在 Redis 索引数据库 (db0) 中
if pinyin_initials_index is not None:
pinyin_initials = row[pinyin_initials_index].upper()
for i in range(len(pinyin_initials)):
for j in range(i + 1, len(pinyin_initials) + 1):
sub_pinyin = pinyin_initials[i:j]
redis_index_client.sadd(sub_pinyin, primary_key)
# 以数字主键建立索引,存储在 Redis 索引数据库 (db0) 中
if primary_key.isdigit():
for i in range(len(primary_key)):
for j in range(i + 1, len(primary_key) + 1):
sub_digit = primary_key[i:j]
redis_index_client.sadd(sub_digit, primary_key)
# 关闭连接
mysql_cursor.close()
mysql_conn.close()
print("数据同步完成")
except mysql.connector.Error as err:
print(f"数据库操作出错: {err}")
def query_stock_info(redis_index_client, redis_stock_client, query):
# 从 Redis 索引数据库 (db0) 的 SET 中获取相关的股票代码
stock_codes = redis_index_client.smembers(query)
stock_info_list = []
for code in stock_codes:
code = code.decode('utf-8')
# 检查 Redis 股票详细信息数据库 (db1) 中键的类型是否为哈希
key_type = redis_stock_client.type(code)
if key_type == b'hash':
# 从 HASH 中获取股票信息
stock_info = redis_stock_client.hgetall(code)
stock_info = {k.decode('utf-8'): v.decode('utf-8') for k, v in stock_info.items()}
stock_info_list.append(stock_info)
else:
print(f"键 {code} 不是哈希类型,跳过该键。")
return stock_info_list
if __name__ == "__main__":
sync_data_from_mysql_to_redis()
redis_index_client = redis.Redis(**redis_index_config)
redis_stock_client = redis.Redis(**redis_stock_config)
query = '600519'
result = query_stock_info(redis_index_client, redis_stock_client, query)
for info in result:
print(info)
-------------------------
Tiny RDM软件的下载地址:
---------------------------
使用redis-cli进入redis数据库,查看有没有成功的命令:
SMEMBERS 6
SMEMBERS 60051
SMEMBERS GZM
SMEMBERS 中国中
----------------------
查看redis数据库有多少条数据的命令:
dbsize
127.0.0.1:6379> dbsize
(integer) 40510
--------------------
查询程序和网页:
app.py
import redis
from flask import Flask, render_template, request
# 本地 Redis 连接配置 - 索引数据库 (db0)
redis_index_config = {
'host': '192.168.137.130',
'port': 6379,
'db': 0,
'password': None
}
# 本地 Redis 连接配置 - 股票详细信息数据库 (db1)
redis_stock_config = {
'host': '192.168.137.130',
'port': 6379,
'db': 1,
'password': None
}
app = Flask(__name__)
# 从 Redis 查询数据
def search_stocks(keyword):
all_results = []
try:
# 连接到 Redis 索引数据库 (db0)
redis_index_client = redis.Redis(**redis_index_config)
# 连接到 Redis 股票详细信息数据库 (db1)
redis_stock_client = redis.Redis(**redis_stock_config)
# 优先处理中文查询
if any('\u4e00' <= char <= '\u9fff' for char in keyword):
stock_codes = redis_index_client.smembers(keyword)
print(f"根据中文关键字 {keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
elif keyword.isdigit():
# 数字查询,从 db0 中查找包含该数字子串的股票代码
stock_codes = redis_index_client.smembers(keyword)
print(f"根据数字关键字 {keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
elif keyword.isalpha():
pinyin_keyword = keyword.upper()
stock_codes = redis_index_client.smembers(pinyin_keyword)
print(f"根据拼音关键字 {pinyin_keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
return all_results
except redis.RedisError as err:
print(f"Redis 操作出错: {err}")
return []
@app.route('/', methods=['GET', 'POST'])
def index():
results = []
if request.method == 'POST':
keyword = request.form.get('keyword')
results = search_stocks(keyword)
return render_template('index.html', results=results)
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)
templates\index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>股票查询系统</title>
<style>
body {
font-family: "微软雅黑", sans-serif;
}
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<h1>股票查询系统</h1>
<input type="text" id="keywordInput" placeholder="输入股票代码、名称或拼音首字母" required>
<table id="resultTable">
<thead>
<tr>
<th>股票代码</th>
<th>股票名称</th>
<th>类别1</th>
<th>类别2</th>
<th>类别3</th>
<th>简介</th>
<th>日期</th>
</tr>
</thead>
<tbody>
{% if results %}
{% for result in results %}
<tr>
<td>{{ result[0] }}</td>
<td>{{ result[1] }}</td>
<td>{{ result[2] }}</td>
<td>{{ result[3] }}</td>
<td>{{ result[4] }}</td>
<td>{{ result[5] }}</td>
<td>{{ result[6] }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
<script>
document.addEventListener('DOMContentLoaded', function () {
const keywordInput = document.getElementById('keywordInput');
const resultTable = document.getElementById('resultTable');
keywordInput.addEventListener('input', function () {
const keyword = keywordInput.value;
if (keyword) {
// 使用 fetch API 发起异步 POST 请求
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `keyword=${encodeURIComponent(keyword)}`
})
.then(response => response.text())
.then(data => {
// 创建一个临时的 DOM 元素来解析响应数据
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data;
// 找到响应数据中的表格内容
const newTableBody = tempDiv.querySelector('#resultTable tbody');
if (newTableBody) {
// 更新页面上表格的内容
resultTable.querySelector('tbody').innerHTML = newTableBody.innerHTML;
}
})
.catch(error => {
console.error('查询出错:', error);
});
}
});
});
</script>
</body>
</html>
----------------------------
部署代码到远程服务器上:
1.安装Python需要的程序包
pip3 install flask redis mysql-connector-python
2.配置防火墙(如果需要): 如果服务器上启用了防火墙(如 firewalld),需要开放 Flask 应用默认使用的端口(默认为 5000)。执行以下命令开放端口:
sudo firewall-cmd --zone=public --add-port=5000/tcp --permanent
sudo firewall-cmd --reload
如果是阿里云,则:
3.运行应用: 进入CENTOS项目目录:cd stock_search_app。 运行 Flask 应用:
python3 app.py
nohup /root/anaconda3/envs/py39/bin/python /你的远程CENTOS目录/app.py > /home/log/applog.txt 2>&1 &
此时应用会在本地启动,默认监听 http://127.0.0.1:5000
4.设置生产环境(可选但推荐): 在生产环境中,不建议直接使用 Flask 的内置开发服务器。可以使用更强大的 Web 服务器和应用服务器,如 Gunicorn 和 Nginx。
安装 Gunicorn:
pip3 install gunicorn
使用 Gunicorn 启动应用:
gunicorn -w 4 -b IP地址:5000 app:app
-w 4 表示使用 4 个工作进程,-b 指定绑定的 IP 地址和端口
第一个app指的是名为app.py的 Python 文件(如果你的文件名是其他的,就相应替换成你的文件名)。这是一个包含 Flask 应用实例的 Python 脚本。
第二个app是指在app.py文件中创建的 Flask 应用实例对象。
gunicorn -w 4 -b 0.0.0.0:5000 app:app
上面的命令是允许所有IP都可以访问5000端口
并且要在app.py目录下运行上面的命令。
app.py的代码:
import redis
import mysql.connector
from flask import Flask, render_template, request, redirect, session
# 本地 Redis 连接配置 - 索引数据库 (db0)
redis_index_config = {
'host': 'localhost',
'port': 6379,
'db': 0,
'password': '数据库登录密码'
}
# 本地 Redis 连接配置 - 股票详细信息数据库 (db1)
redis_stock_config = {
'host': 'localhost',
'port': 6379,
'db': 1,
'password': '数据库登录密码'
}
# MySQL 连接配置
mysql_config = {
'host': 'localhost', # MySQL 服务器地址,根据实际情况修改
'user': '数据库登录密码用户名', # MySQL 用户名,根据实际情况修改
'password': '数据库登录密码', # MySQL 密码,根据实际情况修改
'database': 'user', # 数据库名
'buffered': True # 添加 buffered=True 参数
}
app = Flask(__name__)
app.secret_key = 'your_secret_key'
# 从 Redis 查询数据
def search_stocks(keyword):
all_results = []
try:
# 连接到 Redis 索引数据库 (db0)
redis_index_client = redis.Redis(**redis_index_config)
# 连接到 Redis 股票详细信息数据库 (db1)
redis_stock_client = redis.Redis(**redis_stock_config)
# 优先处理中文查询
if any('\u4e00' <= char <= '\u9fff' for char in keyword):
stock_codes = redis_index_client.smembers(keyword)
print(f"根据中文关键字 {keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
elif keyword.isdigit():
# 数字查询,从 db0 中查找包含该数字子串的股票代码
stock_codes = redis_index_client.smembers(keyword)
print(f"根据数字关键字 {keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
elif keyword.isalpha():
pinyin_keyword = keyword.upper()
stock_codes = redis_index_client.smembers(pinyin_keyword)
print(f"根据拼音关键字 {pinyin_keyword} 在 Redis 中找到 {len(stock_codes)} 个相关股票代码")
for stock_code in stock_codes:
stock_code = stock_code.decode('utf-8')
key_type = redis_stock_client.type(stock_code)
if key_type == b'hash':
result = redis_stock_client.hgetall(stock_code)
result = {k.decode('utf-8'): v.decode('utf-8') for k, v in result.items()}
stock_name = result.get('stock_name')
category1 = result.get('category1')
category2 = result.get('category2')
category3 = result.get('category3')
introduction = result.get('introduction')
record_date = result.get('record_date')
all_results.append((stock_code, stock_name, category1, category2,
category3, introduction, record_date))
else:
print(f"键 {stock_code} 不是哈希类型,跳过该键。")
return all_results
except redis.RedisError as err:
print(f"Redis 操作出错: {err}")
return []
# 从 MySQL 获取用户名和密码
def get_user_credentials(username):
try:
cnx = mysql.connector.connect(**mysql_config)
cursor = cnx.cursor()
query = "SELECT password FROM users WHERE username = %s"
cursor.execute(query, (username,))
result = cursor.fetchone()
cursor.close()
cnx.close()
if result:
return result[0]
return None
except mysql.connector.Error as err:
print(f"MySQL 操作出错: {err}")
return None
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
stored_password = get_user_credentials(username)
if stored_password and stored_password == password:
session['username'] = username
return redirect('/')
else:
return render_template('login.html', error='用户名或密码错误')
return render_template('login.html')
@app.route('/', methods=['GET', 'POST'])
def index():
if 'username' not in session:
return redirect('/login')
results = []
if request.method == 'POST':
keyword = request.form.get('keyword')
results = search_stocks(keyword)
return render_template('index.html', results=results)
@app.route('/logout')
def logout():
session.pop('username', None)
return redirect('/login')
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>股票查询系统</title>
<style>
body {
font-family: "微软雅黑", sans-serif;
}
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.logout {
position: absolute;
top: 10px;
right: 10px;
}
</style>
</head>
<body>
<a href="/logout" class="logout">退出登录</a>
<h1>股票查询系统</h1>
<input type="text" id="keywordInput" placeholder="输入股票代码、名称或拼音首字母" required>
<table id="resultTable">
<thead>
<tr>
<th>股票代码</th>
<th>股票名称</th>
<th>类别1</th>
<th>类别2</th>
<th>类别3</th>
<th>简介</th>
<th>日期</th>
</tr>
</thead>
<tbody>
{% if results %}
{% for result in results %}
<tr>
<td>{{ result[0] }}</td>
<td>{{ result[1] }}</td>
<td>{{ result[2] }}</td>
<td>{{ result[3] }}</td>
<td>{{ result[4] }}</td>
<td>{{ result[5] }}</td>
<td>{{ result[6] }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
<script>
document.addEventListener('DOMContentLoaded', function () {
const keywordInput = document.getElementById('keywordInput');
const resultTable = document.getElementById('resultTable');
keywordInput.addEventListener('input', function () {
const keyword = keywordInput.value;
if (keyword) {
// 使用 fetch API 发起异步 POST 请求
fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `keyword=${encodeURIComponent(keyword)}`
})
.then(response => response.text())
.then(data => {
// 创建一个临时的 DOM 元素来解析响应数据
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data;
// 找到响应数据中的表格内容
const newTableBody = tempDiv.querySelector('#resultTable tbody');
if (newTableBody) {
// 更新页面上表格的内容
resultTable.querySelector('tbody').innerHTML = newTableBody.innerHTML;
}
})
.catch(error => {
console.error('查询出错:', error);
});
}
});
});
</script>
</body>
</html>
login.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<style>
body {
font-family: "微软雅黑", sans-serif;
}
form {
width: 300px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
input {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ddd;
border-radius: 3px;
}
button {
width: 100%;
padding: 10px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
.error {
color: red;
text-align: center;
margin-bottom: 10px;
}
</style>
</head>
<body>
{% if error %}
<p class="error">{{ error }}</p>
{% endif %}
<form method="post">
<input type="text" name="username" placeholder="用户名" required>
<input type="password" name="password" placeholder="密码" required>
<button type="submit">登录</button>
</form>
</body>
</html>
templates文件夹和app.py在同一目录下,templates文件夹下面放置index.html和login.html
后台启动:
nohup /root/anaconda3/envs/py39/bin/gunicorn -w 4 -b 0.0.0.0:5000 app:app > /home/log/stock_search_app_gunicorn_log.txt 2>&1 &
注意:上面的0.0.0.0不能使用阿里云的公网IP。
配置 Nginx 作为反向代理服务器,将请求转发到 Gunicorn。编辑 Nginx 配置文件(通常在 /etc/nginx/sites-available/ 目录下),添加类似以下内容:
如果是阿里云的主私网IP,假设为:172.29.78.91,把请求代理到 172.29.78.91:5000 或者 0.0.0.0:5000。示例配置如下:
server {
listen 80;
server_name your_domain.com; # 替换为你的域名或IP地址
location / {
proxy_pass http://172.29.78.91:5000; # 指向Gunicorn的地址和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
------------------------
1. 修改Nginx配置(关键步骤) 调整 /cx 的代理规则,确保路径重定向后仍保留 /cx 前缀:
location /cx/ {
proxy_pass http://127.0.0.1:5000/; # 结尾的 `/` 会去掉 `/cx/` 前缀
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 修复重定向路径问题(关键!)
proxy_redirect / /cx/;
proxy_cookie_path / /cx/;
}
作用: proxy_redirect / /cx/:将后端返回的重定向路径(如 /login)自动改为 /cx/login。 proxy_cookie_path / /cx/:修复Cookie路径,避免会话丢失。
2. 验证代理结果 访问 http://www.xgxt.pro/cx 应自动跳转到 http://www.xgxt.pro/cx/login。
检查浏览器开发者工具中的「Network」选项卡,确认:
初始请求路径:/cx → 返回 302 到 /cx/login。
静态资源(CSS/JS)路径是否正确加载(如 /cx/static/style.css)。
3. 后端应用适配(可选)
如果应用中有硬编码的绝对路径(如 <a href="/dashboard">),需调整为动态路径:
# Flask示例:使用 url_for 生成路径
from flask import url_for
@app.route("/login")
def login():
return f"<a href='{url_for('dashboard')}'>Dashboard</a>"
常见问题排查 访问 http://www.xgxt.pro/cx 返回404 检查 proxy_pass 是否以 / 结尾(去掉 /cx 前缀)。 确认后端服务监听 127.0.0.1:5000:
curl -v http://127.0.0.1:5000/login
重定向循环 检查 proxy_cookie_path 是否配置正确。
确保Flask的 SESSION_COOKIE_PATH 设置为 /cx(如果使用会话)。
静态资源加载失败 在Nginx中添加单独配置:
location /cx/static/ {
alias /path/to/flask/app/static/;
}
总结 不要强制代理到 /cx/login,而是让Nginx自动处理重定向路径。
关键配置:proxy_redirect 和 proxy_cookie_path 确保路径一致性。
如果仍有问题,请提供: 浏览器访问 http://www.xgxt.pro/cx 的完整截图(包括Network请求)。 Nginx的 error.log 内容。
-----------------------
保存配置文件后,启用配置并重启 Nginx:
sudo ln -s /etc/nginx/sites-available/your_config /etc/nginx/sites-enabled/
sudo systemctl restart nginx
使用下面的命令查看进程:
netstat -anp | grep 5000
结束进程:
kill -9 进程号