security: 安全规范 + 敏感数据脱敏

- CLAUDE.md 新增安全规范章节(禁止提交 / 必须提供 .env.example)
- finance/config/settings.py 硬编码密码移除
- djapi/api/video/audioRead.py API Key 替换为占位符
- 新增 finance/.env.example 示例配置
- .gitignore 解除 .env.example 排除

Co-Authored-By: Simon <simon@doorcome.cn>
This commit is contained in:
2026-06-17 21:20:07 +08:00
co-authored by Simon
parent bb1a9c4470
commit 22ce3a6aea
5 changed files with 494 additions and 439 deletions
+3
View File
@@ -30,6 +30,9 @@ Thumbs.db
*.token *.token
credentials.* credentials.*
# Env examples are safe to commit
!.env.example
# Database # Database
*.sqlite3 *.sqlite3
*.db *.db
+25
View File
@@ -78,3 +78,28 @@ python cli/demo_sentiment_detail.py --ts_code 600519.SH --date 20260603
| NLP | Qwen (DashScope / Ollama) | .env 配置 | | NLP | Qwen (DashScope / Ollama) | .env 配置 |
| Agent | 自研编排器 | finance/agents/ | | Agent | 自研编排器 | finance/agents/ |
| API | Django 5.2 + uWSGI | djapi/ | | API | Django 5.2 + uWSGI | djapi/ |
## 安全规范
### 禁止提交
- `.env`(含真实 key
- API Key`sk-*``TUSHARE_TOKEN` 等)
- Cookie / Session
- Token / 密钥
- 个人隐私数据(手机号、身份证、密码)
### 必须提供
- `.env.example` — 仅含占位符的示例配置,如:
```
TUSHARE_TOKEN=your_token_here
QWEN_API_KEY=sk-your-key-here
MAC_DB_PASSWORD=your_password_here
```
### 提交前检查
```bash
grep -r "sk-\|token\|_H(lU\|password" --include="*.py" --include="*.md" --include="*.yaml" | grep -v ".example\|your_token\|your_password"
```
+438 -438
View File
@@ -1,439 +1,439 @@
import env # 加载 .env 到环境变量 import env # 加载 .env 到环境变量
import os import os
import dashscope import dashscope
import pydub import pydub
from pydub import AudioSegment from pydub import AudioSegment
from pydub.silence import split_on_silence from pydub.silence import split_on_silence
from dashscope.audio.asr import Recognition from dashscope.audio.asr import Recognition
from dashscope import Generation from dashscope import Generation
from http import HTTPStatus from http import HTTPStatus
from mysqlHandle import MySQLDB from mysqlHandle import MySQLDB
import logging import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 设置环境变量 # 设置环境变量
#os.environ["DASHSCOPE_API_KEY"] = "sk-d2d65b726068445b98b88fc3b675dbf1" # 替换为你的API Key # os.environ["DASHSCOPE_API_KEY"] = "sk-your-dashscope-key"
def convert_mp3_to_wav(mp3_path, output_wav_path): def convert_mp3_to_wav(mp3_path, output_wav_path):
""" """
将MP3文件转换为16kHz单声道WAV格式,这是Qwen3-ASR-Flash模型的推荐格式 将MP3文件转换为16kHz单声道WAV格式,这是Qwen3-ASR-Flash模型的推荐格式
参数: 参数:
mp3_path (str): MP3文件路径 mp3_path (str): MP3文件路径
output_wav_path (str): 输出WAV文件路径 output_wav_path (str): 输出WAV文件路径
返回值: 返回值:
str: 转换后的WAV文件路径 str: 转换后的WAV文件路径
""" """
logger.info(f"开始转换MP3到WAV: {mp3_path}") logger.info(f"开始转换MP3到WAV: {mp3_path}")
# 加载MP3文件 # 加载MP3文件
audio = AudioSegment.from_file(mp3_path, format="mp3") audio = AudioSegment.from_file(mp3_path, format="mp3")
# 转换为16kHz采样率、单声道、16位深度 # 转换为16kHz采样率、单声道、16位深度
audio = audio.set_frame_rate(16000).set_channels(1) audio = audio.set_frame_rate(16000).set_channels(1)
# 导出为WAV格式 # 导出为WAV格式
audio.export(output_wav_path, format="wav") audio.export(output_wav_path, format="wav")
logger.info(f"✓ MP3转换完成: {output_wav_path}") logger.info(f"✓ MP3转换完成: {output_wav_path}")
#print(f"✓ MP3转换完成: {output_wav_path}") #print(f"✓ MP3转换完成: {output_wav_path}")
return output_wav_path return output_wav_path
def split_audio_by_fixed_duration(audio_path, chunk_duration, output_folder): def split_audio_by_fixed_duration(audio_path, chunk_duration, output_folder):
""" """
将音频文件按固定时长分割成多个片段 将音频文件按固定时长分割成多个片段
参数: 参数:
audio_path (str): 音频文件路径 audio_path (str): 音频文件路径
chunk_duration (int): 分片时长(毫秒) chunk_duration (int): 分片时长(毫秒)
output_folder (str): 输出文件夹路径 output_folder (str): 输出文件夹路径
返回值: 返回值:
list: 分片文件路径列表 list: 分片文件路径列表
""" """
# 加载音频文件 # 加载音频文件
audio = AudioSegment.from_file(audio_path) audio = AudioSegment.from_file(audio_path)
# 计算总时长(毫秒) # 计算总时长(毫秒)
total_duration = len(audio) total_duration = len(audio)
# 分片数 # 分片数
num_chunks = total_duration // chunk_duration + 1 num_chunks = total_duration // chunk_duration + 1
# 存储分片文件路径 # 存储分片文件路径
chunks = [] chunks = []
# 创建输出文件夹 # 创建输出文件夹
os.makedirs(output_folder, exist_ok=True) os.makedirs(output_folder, exist_ok=True)
logger.info(f"开始音频分割,总时长: {total_duration/1000:.1f}秒,将分割为{num_chunks}个片段") logger.info(f"开始音频分割,总时长: {total_duration/1000:.1f}秒,将分割为{num_chunks}个片段")
for i in range(num_chunks): for i in range(num_chunks):
# 计算当前分片的起始和结束时间 # 计算当前分片的起始和结束时间
start_time = i * chunk_duration start_time = i * chunk_duration
end_time = (i + 1) * chunk_duration end_time = (i + 1) * chunk_duration
# 提取分片音频 # 提取分片音频
chunk = audio[start_time:end_time] chunk = audio[start_time:end_time]
# 生成文件名 # 生成文件名
chunk_name = f"chunk_{i}.wav" chunk_name = f"chunk_{i}.wav"
chunk_path = os.path.join(output_folder, chunk_name) chunk_path = os.path.join(output_folder, chunk_name)
# 导出分片音频 # 导出分片音频
chunk.export(chunk_path, format="wav") chunk.export(chunk_path, format="wav")
chunks.append(chunk_path) chunks.append(chunk_path)
# 打印处理进度 # 打印处理进度
progress = (i + 1) / num_chunks * 100 progress = (i + 1) / num_chunks * 100
logger.info(f"✓ 已完成分片 {i+1}/{num_chunks} ({progress:.1f}%)") logger.info(f"✓ 已完成分片 {i+1}/{num_chunks} ({progress:.1f}%)")
logger.info(f"✓ 音频分割完成,共生成{len(chunks)}个分片文件") logger.info(f"✓ 音频分割完成,共生成{len(chunks)}个分片文件")
return chunks return chunks
def split_audio_by_smart_silence(audio_path, min_silence_len, silence_thresh, output_folder): def split_audio_by_smart_silence(audio_path, min_silence_len, silence_thresh, output_folder):
""" """
将音频文件按智能静音检测方式分割成多个片段,每段不超过3分钟 将音频文件按智能静音检测方式分割成多个片段,每段不超过3分钟
参数: 参数:
audio_path (str): 音频文件路径 audio_path (str): 音频文件路径
min_silence_len (int): 最小静音长度(毫秒) min_silence_len (int): 最小静音长度(毫秒)
silence_thresh (int): 静音阈值(dBFS) silence_thresh (int): 静音阈值(dBFS)
output_folder (str): 输出文件夹路径 output_folder (str): 输出文件夹路径
返回值: 返回值:
list: 分片文件路径列表 list: 分片文件路径列表
""" """
# 加载音频文件 # 加载音频文件
audio = AudioSegment.from_file(audio_path, format="wav") audio = AudioSegment.from_file(audio_path, format="wav")
# 按静音分割 # 按静音分割
segments = split_on_silence( segments = split_on_silence(
audio, audio,
# 静音超过700毫秒则分割 # 静音超过700毫秒则分割
min_silence_len=min_silence_len, min_silence_len=min_silence_len,
# 静音阈值为-40dBFS # 静音阈值为-40dBFS
silence_thresh=silence_thresh, silence_thresh=silence_thresh,
# 保留静音部分 # 保留静音部分
keep_silence=400 keep_silence=400
) )
logger.info(f"✓ 静音分割完成,共{len(segments)}个初始片段") logger.info(f"✓ 静音分割完成,共{len(segments)}个初始片段")
# 合并过短的片段 # 合并过短的片段
merged_segments = [] merged_segments = []
current_segment = None current_segment = None
for segment in segments: for segment in segments:
if current_segment is None: if current_segment is None:
current_segment = segment current_segment = segment
else: else:
# 合并当前片段和新片段 # 合并当前片段和新片段
temp_segment = current_segment + segment temp_segment = current_segment + segment
# 如果合并后的片段超过3分钟,则单独保存当前片段 # 如果合并后的片段超过3分钟,则单独保存当前片段
if len(temp_segment) > 180000: # 3分钟=180,000毫秒 if len(temp_segment) > 180000: # 3分钟=180,000毫秒
merged_segments.append(current_segment) merged_segments.append(current_segment)
current_segment = segment current_segment = segment
else: else:
current_segment = temp_segment current_segment = temp_segment
# 添加最后一个片段 # 添加最后一个片段
if current_segment is not None: if current_segment is not None:
merged_segments.append(current_segment) merged_segments.append(current_segment)
logger.info(f"✓ 片段合并完成,共{len(merged_segments)}个最终片段") logger.info(f"✓ 片段合并完成,共{len(merged_segments)}个最终片段")
# 存储分片文件路径 # 存储分片文件路径
chunks = [] chunks = []
# 创建输出文件夹 # 创建输出文件夹
os.makedirs(output_folder, exist_ok=True) os.makedirs(output_folder, exist_ok=True)
logger.info(f"开始导出音频片段到: {output_folder}") logger.info(f"开始导出音频片段到: {output_folder}")
for i, segment in enumerate(merged_segments): for i, segment in enumerate(merged_segments):
# 生成文件名 # 生成文件名
chunk_name = f"chunk_{i}.wav" chunk_name = f"chunk_{i}.wav"
chunk_path = os.path.join(output_folder, chunk_name) chunk_path = os.path.join(output_folder, chunk_name)
# 导出分片音频 # 导出分片音频
segment.export(chunk_path, format="wav") segment.export(chunk_path, format="wav")
chunks.append(chunk_path) chunks.append(chunk_path)
# 打印处理进度 # 打印处理进度
progress = (i + 1) / len(merged_segments) * 100 progress = (i + 1) / len(merged_segments) * 100
logger.info(f"✓ 已完成分片 {i+1}/{len(merged_segments)} ({progress:.1f}%)") logger.info(f"✓ 已完成分片 {i+1}/{len(merged_segments)} ({progress:.1f}%)")
logger.info(f"✓ 智能静音分割完成,共生成{len(chunks)}个分片文件") logger.info(f"✓ 智能静音分割完成,共生成{len(chunks)}个分片文件")
return chunks return chunks
def transcribe_audio(audio_path): def transcribe_audio(audio_path):
""" """
使用Paraformer实时语音识别模型(通过本地文件)转录音频文件 使用Paraformer实时语音识别模型(通过本地文件)转录音频文件
参数: 参数:
audio_path (str): 音频文件路径(必须是16kHz单声道WAV) audio_path (str): 音频文件路径(必须是16kHz单声道WAV)
返回值: 返回值:
str: 识别文本,如果失败返回空字符串 str: 识别文本,如果失败返回空字符串
""" """
try: try:
# 确保音频文件存在 # 确保音频文件存在
if not os.path.exists(audio_path): if not os.path.exists(audio_path):
logger.error(f"音频文件不存在: {audio_path}") logger.error(f"音频文件不存在: {audio_path}")
return ['', ''] return ['', '']
dashscope.api_key = os.getenv('DASHSCOPE_API_KEY', '') dashscope.api_key = os.getenv('DASHSCOPE_API_KEY', '')
# 创建识别对象 # 创建识别对象
recognition = Recognition( recognition = Recognition(
model=os.getenv('DASHSCOPE_ASR_MODEL', 'paraformer-realtime-v2'), model=os.getenv('DASHSCOPE_ASR_MODEL', 'paraformer-realtime-v2'),
format='wav', format='wav',
sample_rate=16000, sample_rate=16000,
language_hints=['zh','en'], # 中文和英文 language_hints=['zh','en'], # 中文和英文
callback=None callback=None
) )
# 调用识别 # 调用识别
logger.info(f"开始识别音频: {audio_path}") logger.info(f"开始识别音频: {audio_path}")
result = recognition.call(audio_path) result = recognition.call(audio_path)
text=[] text=[]
if result.status_code == HTTPStatus.OK: if result.status_code == HTTPStatus.OK:
# 提取识别结果 # 提取识别结果
logger.info(f"{audio_path} 识别成功") logger.info(f"{audio_path} 识别成功")
sentence = result.get_sentence() sentence = result.get_sentence()
text.append(merge_transcripts(sentence)) text.append(merge_transcripts(sentence))
logger.info(f"识别文本长度: {len(text[0])}") logger.info(f"识别文本长度: {len(text[0])}")
text.append(analyze_and_correct_text(text[0])) text.append(analyze_and_correct_text(text[0]))
return text return text
else: else:
logger.error(f"❌ 任务失败: {result.message}") logger.error(f"❌ 任务失败: {result.message}")
return ['', ''] return ['', '']
except Exception as e: except Exception as e:
logger.error(f"识别过程中发生异常: {e}") logger.error(f"识别过程中发生异常: {e}")
return ['', ''] return ['', '']
def merge_transcripts(transcripts): def merge_transcripts(transcripts):
""" """
将多段识别文本合并成完整句子(保留原始段落逻辑,用空格连接) 将多段识别文本合并成完整句子(保留原始段落逻辑,用空格连接)
参数: 参数:
transcripts (list): 识别结果列表,每个元素为字典{'text': '识别文本'} transcripts (list): 识别结果列表,每个元素为字典{'text': '识别文本'}
返回: 返回:
str: 合并后的完整文本 str: 合并后的完整文本
""" """
# 输入参数检查 # 输入参数检查
if not transcripts: if not transcripts:
return "" return ""
# 确保transcripts是可迭代对象 # 确保transcripts是可迭代对象
if not hasattr(transcripts, '__iter__'): if not hasattr(transcripts, '__iter__'):
return "" return ""
try: try:
# 提取所有有效的text字段 # 提取所有有效的text字段
texts = [] texts = []
for t in transcripts: for t in transcripts:
try: try:
# 检查是否为字典类型且包含text字段 # 检查是否为字典类型且包含text字段
if isinstance(t, dict) and 'text' in t and t['text']: if isinstance(t, dict) and 'text' in t and t['text']:
text = t['text'] text = t['text']
# 确保text是字符串类型 # 确保text是字符串类型
if isinstance(text, str) and text.strip(): if isinstance(text, str) and text.strip():
texts.append(text.strip()) texts.append(text.strip())
except (KeyError, TypeError, AttributeError): except (KeyError, TypeError, AttributeError):
# 忽略单个元素的处理错误,继续处理其他元素 # 忽略单个元素的处理错误,继续处理其他元素
continue continue
# 用空格连接所有段落(根据实际需求可调整连接符) # 用空格连接所有段落(根据实际需求可调整连接符)
return " ".join(texts) if texts else "" return " ".join(texts) if texts else ""
except Exception as e: except Exception as e:
logger.error(f"合并转录文本时发生错误: {e}") logger.error(f"合并转录文本时发生错误: {e}")
return "" return ""
def text_correction(text): def text_correction(text):
""" """
使用通义千问模型修正文本中的错误和标点符号 使用通义千问模型修正文本中的错误和标点符号
参数: 参数:
text (str): 需要修正的文本 text (str): 需要修正的文本
返回值: 返回值:
str: 修正后的文本 str: 修正后的文本
""" """
logger.info("开始文本修正...") logger.info("开始文本修正...")
# 构建修正提示词 # 构建修正提示词
correction_prompt = """请仔细检查以下文本,修正其中的错误: correction_prompt = """请仔细检查以下文本,修正其中的错误:
1. 错别字和语法错误 1. 错别字和语法错误
2. 标点符号使用错误 2. 标点符号使用错误
3. 语句不通顺的地方 3. 语句不通顺的地方
4. 逻辑不清晰的部分 4. 逻辑不清晰的部分
请直接返回修正后的完整文本,不要添加任何解释说明。""" 请直接返回修正后的完整文本,不要添加任何解释说明。"""
# 构建消息列表 # 构建消息列表
messages = [ messages = [
{"role": "system", "content": "你是一个专业的文本校对助手,擅长修正文本中的各种错误。"}, {"role": "system", "content": "你是一个专业的文本校对助手,擅长修正文本中的各种错误。"},
{"role": "user", "content": correction_prompt}, {"role": "user", "content": correction_prompt},
{"role": "user", "content": text} {"role": "user", "content": text}
] ]
logger.info("调用通义千问模型进行文本修正...") logger.info("调用通义千问模型进行文本修正...")
# 调用DashScope文本生成接口 # 调用DashScope文本生成接口
response = Generation.call( response = Generation.call(
model=os.getenv('DASHSCOPE_LLM_MODEL', 'qwen-plus'), model=os.getenv('DASHSCOPE_LLM_MODEL', 'qwen-plus'),
messages=messages, messages=messages,
max_tokens=30000, max_tokens=30000,
temperature=0.1, # 使用较低的温度以提高确定性 temperature=0.1, # 使用较低的温度以提高确定性
top_p=0.5 top_p=0.5
) )
# 检查API调用是否成功 # 检查API调用是否成功
if response.status_code != 200: if response.status_code != 200:
logger.warning(f"❌ 文本修正API调用失败: {response.message}") logger.warning(f"❌ 文本修正API调用失败: {response.message}")
raise Exception(f"文本修正API调用失败: {response.message}") raise Exception(f"文本修正API调用失败: {response.message}")
logger.info("✓ 文本修正完成") logger.info("✓ 文本修正完成")
# 返回修正后的文本 # 返回修正后的文本
return response.output.text return response.output.text
def analyze_and_correct_text(text): def analyze_and_correct_text(text):
""" """
分析文本并自动修正错误 分析文本并自动修正错误
参数: 参数:
text (str): 待分析和修正的文本 text (str): 待分析和修正的文本
prompt (str): 分析提示词 prompt (str): 分析提示词
返回值: 返回值:
tuple: (修正后的文本, 分析结果) tuple: (修正后的文本, 分析结果)
""" """
logger.info("开始文本分析和修正流程...") logger.info("开始文本分析和修正流程...")
# 首先修正文本错误 # 首先修正文本错误
corrected_text = text_correction(text) corrected_text = text_correction(text)
if corrected_text is None: if corrected_text is None:
logger.warning("文本修正返回 None,使用原始文本") logger.warning("文本修正返回 None,使用原始文本")
corrected_text = text corrected_text = text
logger.info(f"原始文本长度: {len(text)}") logger.info(f"原始文本长度: {len(text)}")
logger.info(f"修正后文本长度: {len(corrected_text)}") logger.info(f"修正后文本长度: {len(corrected_text)}")
# 使用修正后的文本进行分析 # 使用修正后的文本进行分析
# analysis_result = analyze_text(corrected_text, prompt) # analysis_result = analyze_text(corrected_text, prompt)
return corrected_text return corrected_text
def analyze_text(text, prompt): def analyze_text(text, prompt):
""" """
使用通义千问模型分析文本 使用通义千问模型分析文本
参数: 参数:
text (str): 待分析文本 text (str): 待分析文本
prompt (str): 分析提示词 prompt (str): 分析提示词
返回值: 返回值:
str: 分析结果 str: 分析结果
""" """
logger.info("开始文本分析...") logger.info("开始文本分析...")
# 设置系统提示 # 设置系统提示
system_prompt = "你是一个专业的文本分析助手,擅长根据提示词对长文本进行深入分析。" system_prompt = "你是一个专业的文本分析助手,擅长根据提示词对长文本进行深入分析。"
# 构建消息列表 # 构建消息列表
messages = [ messages = [
{"role": "system", "content": system_prompt}, {"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}, {"role": "user", "content": prompt},
{"role": "user", "content": text} {"role": "user", "content": text}
] ]
logger.info("调用通义千问模型进行文本分析...") logger.info("调用通义千问模型进行文本分析...")
# 调用DashScope文本生成接口 # 调用DashScope文本生成接口
response = Generation.call( response = Generation.call(
model=os.getenv('DASHSCOPE_LLM_MODEL', 'qwen-plus'), model=os.getenv('DASHSCOPE_LLM_MODEL', 'qwen-plus'),
messages=messages, messages=messages,
max_tokens=8190, # 控制生成文本的最大长度 max_tokens=8190, # 控制生成文本的最大长度
temperature=0.3, # 控制生成文本的确定性 temperature=0.3, # 控制生成文本的确定性
top_p=0.7 # 控制生成文本的多样性 top_p=0.7 # 控制生成文本的多样性
) )
# 检查API调用是否成功 # 检查API调用是否成功
if response.status_code != 200: if response.status_code != 200:
logger.error(f"❌ API调用失败: {response.message}") logger.error(f"❌ API调用失败: {response.message}")
raise Exception(f"API调用失败: {response.message}") raise Exception(f"API调用失败: {response.message}")
logger.info("✓ 文本分析完成") logger.info("✓ 文本分析完成")
# 返回分析结果 # 返回分析结果
return response.output.text return response.output.text
def process_long_audio(mp3_path, output_folder, date_str): def process_long_audio(mp3_path, output_folder, date_str):
""" """
处理长音频文件,分割、识别并分析 处理长音频文件,分割、识别并分析
参数: 参数:
mp3_path (str): MP3文件路径 mp3_path (str): MP3文件路径
prompt (str): 分析提示词 prompt (str): 分析提示词
output_folder (str): 输出文件夹路径 output_folder (str): 输出文件夹路径
返回值: 返回值:
str: 分析结果 str: 分析结果
""" """
logger.info("开始处理长音频...") logger.info("开始处理长音频...")
# 转换MP3为WAV格式 # 转换MP3为WAV格式
logger.info("步骤1/4: 转换MP3为WAV格式") logger.info("步骤1/4: 转换MP3为WAV格式")
wav_path = convert_mp3_to_wav( wav_path = convert_mp3_to_wav(
mp3_path, os.path.join(output_folder, "input.wav") mp3_path, os.path.join(output_folder, "input.wav")
) )
# 分割音频 # 分割音频
# 可以选择固定分片或智能静音分割 # 可以选择固定分片或智能静音分割
# chunks = split_audio_by_fixed_duration(wav_path, 180000, output_folder) # chunks = split_audio_by_fixed_duration(wav_path, 180000, output_folder)
logger.info("步骤2/4: 智能静音分割音频") logger.info("步骤2/4: 智能静音分割音频")
chunks = split_audio_by_smart_silence( chunks = split_audio_by_smart_silence(
wav_path, 700, -40, output_folder wav_path, 700, -40, output_folder
) )
# 存储所有识别文本 # 存储所有识别文本
transcribed_text = "" transcribed_text = ""
# 识别每个分片 # 识别每个分片
logger.info(f"步骤3/4: 开始识别音频分片,共{len(chunks)}个分片") logger.info(f"步骤3/4: 开始识别音频分片,共{len(chunks)}个分片")
for i, chunk_path in enumerate(chunks): for i, chunk_path in enumerate(chunks):
try: try:
logger.info(f"识别进度: {i+1}/{len(chunks)} ({((i+1)/len(chunks)*100):.1f}%)") logger.info(f"识别进度: {i+1}/{len(chunks)} ({((i+1)/len(chunks)*100):.1f}%)")
# 调用音频识别API # 调用音频识别API
text = transcribe_audio(chunk_path) text = transcribe_audio(chunk_path)
""" """
if not text[1].startswith('今天的新闻联播节目播送完毕'): if not text[1].startswith('今天的新闻联播节目播送完毕'):
prompt='请分析所给文本的新闻内容,返回一个简短标题' prompt='请分析所给文本的新闻内容,返回一个简短标题'
text.append(analyze_text(text[1],prompt)) text.append(analyze_text(text[1],prompt))
else: else:
text.append('') text.append('')
""" """
# 新闻标题留空 # 新闻标题留空
text.append('') text.append('')
# 添加到总文本 # 添加到总文本
#transcribed_text += text + "\n" #transcribed_text += text + "\n"
# 删除临时文件 # 删除临时文件
os.remove(chunk_path) os.remove(chunk_path)
# 初始化数据库连接 # 初始化数据库连接
db = MySQLDB() # 使用默认参数连接数据库 db = MySQLDB() # 使用默认参数连接数据库
try: try:
# 插入数据示例 # 插入数据示例
user_data = { user_data = {
"news_days": date_str, "news_days": date_str,
"daily_sub_id": i, "daily_sub_id": i,
"news_raw": text[0], "news_raw": text[0],
"news_improve": text[1], "news_improve": text[1],
"news_title": text[2] "news_title": text[2]
} }
user_id = db.insert_data("xwlb_daily", user_data) user_id = db.insert_data("xwlb_daily", user_data)
finally: finally:
# 关闭连接 # 关闭连接
db.close() db.close()
except Exception as e: except Exception as e:
logger.error(f"识别失败: {chunk_path}, 错误: {e}") logger.error(f"识别失败: {chunk_path}, 错误: {e}")
# 可以在这里添加重试逻辑 # 可以在这里添加重试逻辑
# 分析识别文本 # 分析识别文本
""" """
print("步骤4/4: 分析识别文本") print("步骤4/4: 分析识别文本")
print(f"识别文本长度: {len(transcribed_text)}") print(f"识别文本长度: {len(transcribed_text)}")
print(f"识别文本内容: {transcribed_text}") print(f"识别文本内容: {transcribed_text}")
if len(transcribed_text) < 1: if len(transcribed_text) < 1:
print("识别文本为空,跳过分析处理") print("识别文本为空,跳过分析处理")
return "识别文本为空,无法进行分析" return "识别文本为空,无法进行分析"
analysis_result = analyze_text(transcribed_text, prompt) analysis_result = analyze_text(transcribed_text, prompt)
""" """
logger.info("✓ 长音频处理完成") logger.info("✓ 长音频处理完成")
# 返回分析结果 # 返回分析结果
return "" return ""
# 使用示例 # 使用示例
if __name__ == "__main__": if __name__ == "__main__":
# MP3文件路径 # MP3文件路径
mp3_path = "20251002.mp3" mp3_path = "20251002.mp3"
# 分析提示词 # 分析提示词
prompt = "请总结这段由中国中央电视台新闻联播音频转为文字的文本,理解其主要内容并提取其中的关键信息。" prompt = "请总结这段由中国中央电视台新闻联播音频转为文字的文本,理解其主要内容并提取其中的关键信息。"
# 输出文件夹 # 输出文件夹
output_folder = "audio_processing" output_folder = "audio_processing"
# 处理长音频 # 处理长音频
try: try:
result = process_long_audio( result = process_long_audio(
mp3_path, prompt, output_folder mp3_path, prompt, output_folder
) )
# 打印分析结果 # 打印分析结果
print("分析结果:\n") print("分析结果:\n")
print(result) print(result)
except Exception as e: except Exception as e:
print(f"处理失败: {e}") print(f"处理失败: {e}")
+27
View File
@@ -0,0 +1,27 @@
# ── Qwen API 配置(DashScope) ─────────────────────────────
# API Key 获取:https://dashscope.console.aliyun.com/apiKey
QWEN_API_KEY=sk-your-key-here
QWEN_MODEL=qwen-turbo
# ── 本地 Qwen 部署(可选,优先级高于 API) ──────────────────
# QWEN_LOCAL_BASE_URL=http://localhost:11434/v1
# QWEN_LOCAL_MODEL=qwen2.5:7b
# ── Tushare 数据源 ─────────────────────────────────────────
# Token 获取:https://tushare.pro 注册后在 "个人主页→接口TOKEN" 复制
TUSHARE_TOKEN=your_token_here
# ── 数据库 ──────────────────────────────────────────────────
MAC_DB_HOST=127.0.0.1
MAC_DB_PORT=13306
MAC_DB_USER=myquant
MAC_DB_PASSWORD=your_password_here
MAC_DB_NAME=myquant
# ── 情绪分析范围 ────────────────────────────────────────────
SENTIMENT_SCOPE_TYPE=index
SENTIMENT_SCOPE_INDEXES=000300,000905
SENTIMENT_MAX_NEWS_PER_STOCK=30
# ── MCP 新闻服务(可选) ────────────────────────────────────
# NEWS_MCP_URL=http://192.168.1.160:3333/mcp
+1 -1
View File
@@ -18,7 +18,7 @@ MARIADB_CONFIG = {
"host": os.getenv("MAC_DB_HOST", "127.0.0.1"), "host": os.getenv("MAC_DB_HOST", "127.0.0.1"),
"port": int(os.getenv("MAC_DB_PORT", "13306")), "port": int(os.getenv("MAC_DB_PORT", "13306")),
"user": os.getenv("MAC_DB_USER", "myquant"), "user": os.getenv("MAC_DB_USER", "myquant"),
"password": os.getenv("MAC_DB_PASSWORD", "_H(lU1_fF*9baRTp"), "password": os.getenv("MAC_DB_PASSWORD", ""),
"database": os.getenv("MAC_DB_NAME", "myquant"), "database": os.getenv("MAC_DB_NAME", "myquant"),
"charset": "utf8mb4", "charset": "utf8mb4",
"pool_size": 5, "pool_size": 5,