股票查询系统

使用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软件的下载地址:

Tiny RDM | Redis桌面管理客户端

---------------------------

使用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 进程号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值