1.20 pypdf2读取失败可转为使用fitz库

This commit is contained in:
zy123 2025-01-21 17:05:40 +08:00
parent 94e4e36cc5
commit 430e34b9f9
2 changed files with 163 additions and 166 deletions

View File

@ -54,16 +54,6 @@ def get_patterns_for_evaluation_method():
r'(?<!对应\s*)(?<!根据\s*)(?<!按照\s*)(?<!见\s*)(?<!与\s*)(?<!同\s*)(?<!"\s*)(?<!“\s*)(?<!”\s*)第[一二三四五六七八九十1-9]+(?:章|部分)\s*[\u4e00-\u9fff、()]+\s*$', regex.MULTILINE)
return begin_pattern, end_pattern
def get_patterns_for_notice():
begin_pattern = regex.compile(r'^第[一二三四五六七八九十百千]+(?:章|部分).*?(?:公告|邀请书|邀请函).*')
end_pattern = regex.compile(
# r'^(?:第[一二三四五六七八九十百千]+(?:章|部分)\s*(?:投标人须知|磋商须知|供应商须知)+|(?:一\s*、\s*)?(?:投标人须知|磋商须知|供应商须知)前附表)',
r'^第[一二三四五六七八九十百千]+(?:章|部分)\s*[\u4e00-\u9fff]+',
regex.MULTILINE
)
return begin_pattern, end_pattern
def extract_pages_qualification(pdf_path, begin_page, common_header):
pdf_document = PdfReader(pdf_path)
# 开始匹配模式,仅匹配“附录”、“附件”或“附表”
@ -167,8 +157,6 @@ def extract_pages_twice(pdf_path, output_folder, output_suffix, common_header, b
patterns = [get_patterns_for_procurement()]
elif output_suffix == "evaluation_method" or output_suffix == "qualification2" or output_suffix == "qualification3":
patterns = [get_patterns_for_evaluation_method()]
elif output_suffix == "notice":
patterns = [get_patterns_for_notice()]
elif output_suffix == "qualification1":
start_page, end_page = extract_pages_qualification(pdf_path, begin_page, common_header)
if patterns:

View File

@ -105,7 +105,15 @@ def generate_key_paths(data):
# 如果原键名有后缀,需要记录以便后续重命名
if key != base:
keys_to_rename[key] = base
elif isinstance(value, dict):
elif isinstance(value, dict): #如果嵌套键只有一个且为'系统功能' 直接删去,替换为[]
if len(value) == 1 and '系统功能' in value:
current_dict[key] = []
# 同时将路径添加到 key_paths 和 good_list 中
current_path = '.'.join(path + [base])
key_paths.append(current_path)
if base not in good_list:
good_list.append(base)
else:
# 继续递归处理
recurse(value, path + [base])
else:
@ -292,14 +300,18 @@ def generate_prompt(judge_res, full_text=None):
如果 `judge_res` 不包含 '' 且有 `full_text`则添加文件内容部分
"""
base_prompt = '''
任务你负责解析采购文件提取采购需求并以JSON格式返回不要遗漏该项目需要采购的货物设备或系统或服务
任务你负责解析采购文件提取采购需求告诉我该项目需要采购的货物设备或系统或服务,并以JSON格式返回你无需返回每个采购标的的具体要求或说明
**输出格式**
1.JSON格式外层键名为需要采购的货物设备或系统或服务名称(一级键)
2.层次关系用嵌套键值对表示:
-采购活动可能将目标划分为多个系统或货物若文档通过大标题或表格结构或序号标明这种归属关系请在JSON中以嵌套形式表示
-层级规则系统作为一级键包含的子系统或货物作为二级键
2.最内层键值应为空列表[]
3.无层次关系的内容处理
对于文件中未表现出明显层次结构或归属关系的采购需求直接以一级键表示无需嵌套
4.层次关系用嵌套键值对表示:
-采购活动可能将目标划分为多个系统或货物若有归属关系(注意归属关系是通过原文大标题或表格结构或序号关系得来的请勿根据系统的诸如'说明''规格''参数''描述'等列来擅自归纳其拥有的子系统或货物)请在JSON中以嵌套形式表示
-层级规则系统作为一级键其下的子系统或货物名作为二级键,注意二级键名也必须和原文保持一致,不可擅自总结
-系统功能说明:如果系统提到"系统功能"(即系统的整体功能说明)那么在该系统下中添加"系统功能"作为二级键具体内容不展开;系统可以只有系统功能而不包含具体货物
-层数限制: 最多到二级键.
-示例输入:"""
扫描系统
系统功能支持多种扫描模式
@ -307,32 +319,29 @@ def generate_prompt(judge_res, full_text=None):
1.1 激光器xxx
2. 红外扫描系统xxx
通信系统
系统功能提供高效数据传输
1. 通讯天线
"""
-示例输出:
{
"扫描系统": {
"系统功能": [],
"激光扫描系统": {
"激光器": []
},
"激光扫描系统": [],
"红外扫描系统": []
},
"通信系统": {
"系统功能": []
"通讯天线": []
}
}
3.防止重复键名规则:
5.防止重复键名规则:
-若同一层级下存在名称相同但采购要求如型号参数功能说明不同的采购标的请在名称后添加编号以作区分防止出现重复键名默认情况下无需在名称后添加编号只有在名称相同时才需要添加编号编号规则'名称-编号'编号从1递增例子若同层级下存在两种名称相同但不同型号或参数的'交换机'那么键名分别是'交换机-1''交换机-2'如果名称不同如路由器和交换机则无需编号
4.最内层键值应为空列表[]
**特殊情况**
-如果文件中未明确说明需要采购的货物设备或系统或服务请直接返回空字典 {{}}不要生成任何无关内容
**要求与指南**
1. 精准定位请运用文档理解能力定位文件中的采购需求部分
-若有采购清单请直接根据采购清单上的货物或系统或服务名称给出结果若没有采购清单则从表格或文本中摘取采购信息
-注意采购目标通常在诸如'名称'且每个目标占据一个单元格你无需提取诸如'说明''规格''参数''描述'等其他列的内容即你不需要给出详细的采购要求更不要将这些单元格内的描述拆分作为其的子键你仅返回采购的货物或系统或模块或服务名称
-若有采购清单请直接根据采购清单上的货物或系统或服务名称给出结果若没有采购清单则从表格或文本中提取采购需求
-注意采购目标通常在诸如'名称'且每个目标占据一个单元格你无需提取诸如'说明''规格''参数''描述'等其他列的内容即你不需要给出该目标详细的采购要求**不要**将这些单元格内包含的内容拆分作为其的二级键
2. 采购标的采购种类通常有硬件如设备货物和软件如系统软件应用APP和服务(如人力)一次采购活动可以同时包含这三种类型
3. 软件类采购对于软件系统或应用采购若有多个系统且序号分明请不要遗漏若明确列出系统模块提取模块名称并作为系统的子键无需在模块下再细分功能
4. 完整性
@ -522,142 +531,142 @@ def get_technical_requirements(invalid_path,processed_filepath,model_type=1):
model_res=qianwen_plus(user_query)
print(model_res)
cleaned_res = clean_json_string(model_res) #转字典
if not cleaned_res:
print("本项目没有采购需求!!!")
return {"采购需求": {}}
preprocessed_data=preprocess_data(cleaned_res) #确保最内层为[]
processed_data=truncate_system_keys(preprocessed_data) #限制深度
key_paths, grouped_paths, good_list, data_copy= generate_key_paths(processed_data) # 提取需要采购的货物清单 key_list交通监控视频子系统.高清视频抓拍像机 ... grouped_paths是同一系统下同时有'交换机-1'和'交换机-2',提取'交换机' 输出eg:{'交通标志.标志牌铝板', '交通信号灯.交换机'}
modified_data=rename_keys(data_copy)
user_query_template = """请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。请以 JSON 格式返回结果,键名为\"{}\",键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★或其他特殊符号(若有)和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号。
**重要限制**
- **仅提取技术参数或采购要求不包括任何商务要求**商务要求通常涉及供应商资格报价条款交货时间质保等内容是整体的要求而技术参数或采购要求则具体描述产品的技术规格功能性能指标等
- **商务要求的关键词示例**仅供参考不限于此报价交货合同资质认证服务保修期等如果内容包含上述关键词请仔细甄别是否属于商务要求
要求与指南
1. 你的键值应该全面不要遗漏
-a.若技术参数或采购要求在表格中那么单元格内的内容基本都要涵盖
-对于单元格内以序号分隔的各条参数要求应逐条提取并分别作为键值中的字符串列表项
-对于无序号标明且在同一单元格内的参数要求或功能说明也要根据语义分别添加进键值中
-b.若技术参数或采购要求在正文部分应准确定位到与目标货物设备系统功能模块相关的内容将其后的技术参数或采购要求或功能说明完整提取逐一添加到键值的字符串列表中不得擅自添加或修改序号
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
3. 字符串中的内容为具体的技术参数要求或采购要求请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容
4. 如果该货物没有相关采购要求或技术参数要求键值应为空列表[]
### 示例输出1如下
{{
"摄像机控制键盘": [
"1、▲支持串行 RS232/RS422 和 IP 混合控制,允许在一个控制器上使用 RS232/RS422/IP 控制单个系统中的摄像机;",
"2、支持 2 组 RS422 串口 VISCA 协议菊花链控制 2x7 台摄像机。",
"★能够自动对焦,提供检测报告"
]
}}
### 示例输出2如下包含嵌套结构
{{
"摄像机": [
"摄像机有效像素≥900W像素",
"摄像机最低照度彩色≤0.001lx",
"协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
]
}}
{}
"""
user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在 {} 种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\";键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★或其他特殊符号(若有)和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号。
要求与指南
1. 你的键值应该全面不要遗漏
-a.若技术参数或采购要求在表格中那么单元格内的内容基本都要涵盖
-对于单元格内以序号分隔的各条参数要求应逐条提取并分别作为键值中的字符串列表项
-对于无序号标明且在同一单元格内的参数要求或功能说明也要根据语义分别添加进键值中
-b.若技术参数或采购要求在正文部分应准确定位到与目标货物设备系统功能模块相关的内容将其后的技术参数或采购要求或功能说明完整提取逐一添加到键值的字符串列表中不得擅自添加或修改序号
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
3. 字符串中的内容为具体的技术参数要求或采购要求请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容
4. 如果该货物没有相关采购要求或技术参数要求键值应为空列表[]
### 示例输出1如下
{{
"交换机-1": [
"★1、支持固化千兆电口≥8 个固化千兆光口≥2 个,桌面型设备;",
"2、支持静态链路聚合"
],
"交换机-2": [
"1、交换容量≥52Gbps包转发率≥38.69Mpps",
"2、提供国家强制性产品认证证书及测试报告3C",
"★能实现信号控制独立传输"
]
}}
### 示例输出2如下包含嵌套结构
{{
"摄像机-1": [
"摄像机有效像素≥900W像素",
"摄像机最低照度彩色≤0.001lx",
"协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
],
"摄像机-2": [
"支持夜视", "支持云存储"
]
}}
{}
"""
queries = []
for key in key_paths:
# 将键中的 '.' 替换为 '下的'
modified_key = key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
if model_type==1:
new_query = user_query_template.format(modified_key, key, modified_key,f"文件内容:{full_text}") #转豆包后取消注释
else:
new_query = user_query_template.format(modified_key, key, modified_key,"")
queries.append(new_query)
# 处理 grouped_paths 中的项,应用 user_query_template_two
for grouped_dict in grouped_paths:
for grouped_key, grouped_key_cnt in grouped_dict.items():
# 将键中的 '.' 替换为 '下的'
modified_grouped_key = grouped_key.replace('.', '下的')
if model_type==1:
new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key,
modified_grouped_key, f"文件内容:{full_text}")
else:
new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key,
modified_grouped_key, "")
queries.append(new_query)
if model_type==1:
results = multi_threading(queries, "", "", 3,True) # 豆包
else:
results = multi_threading(queries, "", file_id, 2,True) # qianwen-long
temp_final={}
if not results:
print("errror!未获得大模型的回答!")
else:
# 第一步:收集需要调用 `continue_answer` 的问题和解析结果
questions_to_continue = [] # 存储需要调用 continue_answer 的 (question, parsed)
max_tokens=3900 if model_type==1 else 5900
for question, response in results:
message=response[0]
parsed = clean_json_string(message)
total_tokens=response[1]
if not parsed and total_tokens>max_tokens:
questions_to_continue.append((question, message))
else:
temp_final.update(parsed)
# 第二步:多线程处理需要调用 `continue_answer` 的问题
if questions_to_continue:
continued_results = process_continue_answers(questions_to_continue, model_type, file_id)
temp_final.update(continued_results)
"""根据所有键是否已添加处理技术要求"""
# 更新原始采购需求字典
final_res=combine_and_update_results(modified_data, temp_final)
ffinal_res=all_postprocess(final_res)
ffinal_res["货物列表"] = good_list
# 输出最终的 JSON 字符串
return {"采购需求":ffinal_res}
# cleaned_res = clean_json_string(model_res) #转字典
# if not cleaned_res:
# print("本项目没有采购需求!!!")
# return {"采购需求": {}}
# preprocessed_data=preprocess_data(cleaned_res) #确保最内层为[]
# processed_data=truncate_system_keys(preprocessed_data) #限制深度
# key_paths, grouped_paths, good_list, data_copy= generate_key_paths(processed_data) # 提取需要采购的货物清单 key_list交通监控视频子系统.高清视频抓拍像机 ... grouped_paths是同一系统下同时有'交换机-1'和'交换机-2',提取'交换机' 输出eg:{'交通标志.标志牌铝板', '交通信号灯.交换机'}
# modified_data=rename_keys(data_copy)
# user_query_template = """请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。请以 JSON 格式返回结果,键名为\"{}\",键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★或其他特殊符号(若有)和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号。
# **重要限制**
# - **仅提取技术参数或采购要求,不包括任何商务要求**。商务要求通常涉及供应商资格、报价条款、交货时间、质保等内容,是整体的要求;而技术参数或采购要求则具体描述产品的技术规格、功能、性能指标等。
# - **商务要求的关键词示例**(仅供参考,不限于此):报价、交货、合同、资质、认证、服务、保修期等。如果内容包含上述关键词,请仔细甄别是否属于商务要求
#
# 要求与指南
# 1. 你的键值应该全面,不要遗漏
# -a.若技术参数或采购要求在表格中,那么单元格内的内容基本都要涵盖
# -对于单元格内以序号分隔的各条参数要求,应逐条提取,并分别作为键值中的字符串列表项。
# -对于无序号标明且在同一单元格内的参数要求或功能说明,也要根据语义分别添加进键值中。
# -b.若技术参数或采购要求在正文部分,应准确定位到与目标货物(设备、系统、功能模块)相关的内容,将其后的技术参数或采购要求或功能说明完整提取,逐一添加到键值的字符串列表中,不得擅自添加或修改序号。
# 2. 如果存在嵌套结构且原文为Markdown 的表格语法,如'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起,生成一条完整且清晰的技术参数(或采购要求)描述,作为列表中的一个字符串。如"摄像机有效像素≥900W像素"
# 3. 字符串中的内容为具体的技术参数要求或采购要求,请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容。
# 4. 如果该货物没有相关采购要求或技术参数要求,键值应为空列表[]。
#
# ### 示例输出1如下
# {{
# "摄像机控制键盘": [
# "1、▲支持串行 RS232/RS422 和 IP 混合控制,允许在一个控制器上使用 RS232/RS422/IP 控制单个系统中的摄像机;",
# "2、支持 2 组 RS422 串口 VISCA 协议菊花链控制 2x7 台摄像机。",
# "★能够自动对焦,提供检测报告"
# ]
# }}
#
# ### 示例输出2如下包含嵌套结构
# {{
# "摄像机": [
# "摄像机有效像素≥900W像素",
# "摄像机最低照度彩色≤0.001lx",
# "协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
# ]
# }}
#
# {}
# """
# user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在 {} 种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\";键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求或功能说明的字符串,请按原文内容回答,保留三角▲、五角★或其他特殊符号(若有)和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号。
#
# 要求与指南
# 1. 你的键值应该全面,不要遗漏
# -a.若技术参数或采购要求在表格中,那么单元格内的内容基本都要涵盖
# -对于单元格内以序号分隔的各条参数要求,应逐条提取,并分别作为键值中的字符串列表项。
# -对于无序号标明且在同一单元格内的参数要求或功能说明,也要根据语义分别添加进键值中。
# -b.若技术参数或采购要求在正文部分,应准确定位到与目标货物(设备、系统、功能模块)相关的内容,将其后的技术参数或采购要求或功能说明完整提取,逐一添加到键值的字符串列表中,不得擅自添加或修改序号。
# 2. 如果存在嵌套结构且原文为Markdown 的表格语法,如'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起,生成一条完整且清晰的技术参数(或采购要求)描述,作为列表中的一个字符串。如"摄像机有效像素≥900W像素"
# 3. 字符串中的内容为具体的技术参数要求或采购要求,请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容。
# 4. 如果该货物没有相关采购要求或技术参数要求,键值应为空列表[]。
#
# ### 示例输出1如下
# {{
# "交换机-1": [
# "★1、支持固化千兆电口≥8 个固化千兆光口≥2 个,桌面型设备;",
# "2、支持静态链路聚合"
# ],
# "交换机-2": [
# "1、交换容量≥52Gbps包转发率≥38.69Mpps",
# "2、提供国家强制性产品认证证书及测试报告3C",
# "★能实现信号控制独立传输"
# ]
# }}
#
# ### 示例输出2如下包含嵌套结构
# {{
# "摄像机-1": [
# "摄像机有效像素≥900W像素",
# "摄像机最低照度彩色≤0.001lx",
# "协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
# ],
# "摄像机-2": [
# "支持夜视", "支持云存储"
# ]
# }}
#
# {}
# """
# queries = []
# for key in key_paths:
# # 将键中的 '.' 替换为 '下的'
# modified_key = key.replace('.', '下的')
# # 使用修改后的键填充第一个占位符,原始键填充第二个占位符
# if model_type==1:
# new_query = user_query_template.format(modified_key, key, modified_key,f"文件内容:{full_text}") #转豆包后取消注释
# else:
# new_query = user_query_template.format(modified_key, key, modified_key,"")
# queries.append(new_query)
#
# # 处理 grouped_paths 中的项,应用 user_query_template_two
# for grouped_dict in grouped_paths:
# for grouped_key, grouped_key_cnt in grouped_dict.items():
# # 将键中的 '.' 替换为 '下的'
# modified_grouped_key = grouped_key.replace('.', '下的')
# if model_type==1:
# new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key,
# modified_grouped_key, f"文件内容:{full_text}")
# else:
# new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt, grouped_key,
# modified_grouped_key, "")
# queries.append(new_query)
# if model_type==1:
# results = multi_threading(queries, "", "", 3,True) # 豆包
# else:
# results = multi_threading(queries, "", file_id, 2,True) # qianwen-long
# temp_final={}
# if not results:
# print("errror!未获得大模型的回答!")
# else:
# # 第一步:收集需要调用 `continue_answer` 的问题和解析结果
# questions_to_continue = [] # 存储需要调用 continue_answer 的 (question, parsed)
# max_tokens=3900 if model_type==1 else 5900
# for question, response in results:
# message=response[0]
# parsed = clean_json_string(message)
# total_tokens=response[1]
# if not parsed and total_tokens>max_tokens:
# questions_to_continue.append((question, message))
# else:
# temp_final.update(parsed)
# # 第二步:多线程处理需要调用 `continue_answer` 的问题
# if questions_to_continue:
# continued_results = process_continue_answers(questions_to_continue, model_type, file_id)
# temp_final.update(continued_results)
#
# """根据所有键是否已添加处理技术要求"""
# # 更新原始采购需求字典
# final_res=combine_and_update_results(modified_data, temp_final)
# ffinal_res=all_postprocess(final_res)
# ffinal_res["货物列表"] = good_list
# # 输出最终的 JSON 字符串
# return {"采购需求":ffinal_res}
def test_all_files_in_folder(input_folder, output_folder):
# 确保输出文件夹存在
@ -687,10 +696,10 @@ if __name__ == "__main__":
# invalid_path="D:\\flask_project\\flask_app\\static\\output\\output1\\e7dda5cb-10ba-47a8-b989-d2993d34bb89\\ztbfile.pdf"
# file_id = upload_file(truncate_file)
truncate_file=r'C:\Users\Administrator\Desktop\新建文件夹 (3)\需求\“天府粮仓”青白江区“一园三片”数字化提升和标准化体系建设项目(三次)招标文件N510113202400010820250102001_procurement.pdf'
invalid_path=r"D:\flask_project\flask_app\static\output\output1\f91db70d-8d96-44a5-b840-27d2f1ecbe95\invalid_del.docx"
invalid_path=r"C:\Users\Administrator\Desktop\货物标\extract_files\107国道.txt"
# file_id=upload_file(truncate_file)
# processed_filepath = convert_file_to_markdown(truncate_file)
processed_filepath=r"C:\Users\Administrator\Desktop\新建文件夹 (3)\需求\extract1.txt"
processed_filepath=r"C:\Users\Administrator\Desktop\货物标\extract_files\107国道.txt"
res=get_technical_requirements(invalid_path,processed_filepath,1)
json_string = json.dumps(res, ensure_ascii=False, indent=4)
print(json_string)