From 99e07243909557c1dc59ad573248cd8d362ecd6e Mon Sep 17 00:00:00 2001 From: zy123 <646228430@qq.com> Date: Wed, 15 Jan 2025 15:45:25 +0800 Subject: [PATCH] =?UTF-8?q?1.15=20=E5=BA=9F=E6=A0=87+=E6=88=AA=E5=8F=96pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flask_app/general/table_content_extraction.py | 6 +- flask_app/general/截取pdf通用函数.py | 16 +- flask_app/general/无效标和废标公共代码.py | 505 ++++++------------ .../old_version/无效标和废标公共代码_old.py | 30 +- flask_app/routes/货物标解析main.py | 4 +- flask_app/testdir/待合并代码.py | 162 +++--- flask_app/工程标/截取pdf工程标版.py | 12 +- flask_app/货物标/截取pdf货物标版.py | 14 +- 8 files changed, 280 insertions(+), 469 deletions(-) diff --git a/flask_app/general/table_content_extraction.py b/flask_app/general/table_content_extraction.py index f5a2071..10985ca 100644 --- a/flask_app/general/table_content_extraction.py +++ b/flask_app/general/table_content_extraction.py @@ -130,9 +130,9 @@ def process_all_part1_pdfs(folder_path, output_folder): extract_tables_main(file_path, subfolder_path) if __name__ == "__main__": - path =r"C:\Users\Administrator\Desktop\new招标文件\工程标\tmp\\HBDL-2024-0017-001-招标文件_evaluation_method.pdf" - output_folder = "C:\\Users\\Administrator\\Desktop\\招标文件\\new_test" # 前附表json文件 - res=extract_tables_main("", output_folder) + path =r"C:\Users\Administrator\Desktop\货物标\output4\094定稿-湖北工业大学轻武器模拟射击设备采购项目招标文件_tobidders_notice_part1.docx" + output_folder = r"C:\Users\Administrator\Desktop\货物标\output4\tmp" # 前附表json文件 + res=extract_tables_main(path, output_folder) print(res) # folder_path='C:\\Users\\Administrator\\Desktop\\货物标\\output4' # output_folder="C:\\Users\\Administrator\\Desktop\\货物标\\output4\\tmp2" diff --git a/flask_app/general/截取pdf通用函数.py b/flask_app/general/截取pdf通用函数.py index 8c86e61..37c1d4a 100644 --- a/flask_app/general/截取pdf通用函数.py +++ b/flask_app/general/截取pdf通用函数.py @@ -14,7 +14,7 @@ def get_start_and_common_header(input_path,end_page): common_header = extract_common_header(input_path) last_begin_index = 0 begin_pattern = regex.compile( - r'.*(? 0 and sentences[0]: + # 只取第一句,保留标点 + cleaned_text = data[:start_pos] + sentences[0] # eg:经采购人允许,潜在投标人可进入项目现场进行考察,但潜在投标人不得因此使采购人承担有关责任和蒙受损失。潜在投标人应自行承担现场考察的全部费用、责任和风险。 + # 经采购人允许,潜在投标人可进入项目现场进行考察,但潜在投标人不得因此使采购人承担有关责任和蒙受损失。 + else: + cleaned_text = data # 如果没有标点,使用整个字符串 + else: + # 如果没有找到关键词,保留原文本 + cleaned_text = data + # 删除空格 + cleaned_text_no_spaces = cleaned_text.replace(' ', '').replace(' ', '') + # 如果长度大于8,则添加到结果列表 + if len(cleaned_text_no_spaces) > 8: + all_texts.append(cleaned_text_no_spaces) - last_column_values = [] - - # 读取文档中的所有表格 - if not doc.tables: - print("No tables found in the document.") - return last_column_values - - # 遍历文档中的每个表格 - for table in doc.tables: - # 获取表格的最后一列 - for row in table.rows: - last_cell = row.cells[-1] # 获取最后一个单元格 - # 去除内容前后空白并删除文本中的所有空格 - cleaned_text = last_cell.text.strip().replace(' ', '') - last_column_values.append(cleaned_text) - - return last_column_values - -# 完整读取文件中所有表格(适合pdf转docx价格便宜的情况,优先推荐,内容完整) -def read_tables_from_docx(file_path): - # print(file_path) - # 尝试打开文档 - try: - doc = Document(file_path) - except Exception as e: - print(f"Error opening file: {e}") - return [] - - # 初始化列表来保存符合条件的单元格内容 - cell_contents = [] - - # 读取文档中的所有表格 - if not doc.tables: - print("No tables found in the document.") - return [] - - # 遍历文档中的每个表格 - for table_idx, table in enumerate(doc.tables): - # 遍历表格中的每一行 - for row_idx, row in enumerate(table.rows): - # 遍历每一行中的单元格 - for cell in row.cells: - cell_text = cell.text.strip() # 去除单元格内容前后空白 - if len(cell_text) > 8: # 检查文字数量是否大于8 - cell_contents.append(cell_text) - - # 返回符合条件的单元格内容 - return cell_contents + else: + # print(text_list) + # print("*********") + # 用于处理结构化文本,清理掉不必要的序号,并将分割后的段落合并,最终形成更简洁和格式化的输出。 + pattern = r'^\s*(?:[((]\d+[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]+|[一二三四五六七八九十]+、|[A-Z][))]\s+|[A-Z]\.\s*)' + data = re.sub(pattern, '', text_list[0]).strip() # 去除序号 + # 将修改后的第一个元素和剩余的元素连接起来 + text_list[0] = data # 更新列表中的第一个元素 + joined_text = "\n".join(text_list) # 如果列表中有多个元素,则连接它们 + # 删除空格 + joined_text_no_spaces = joined_text.replace(' ', '').replace(' ', '') + all_texts.append(joined_text_no_spaces) # 将每个列表的内容添加到 all_texts 中 + return all_texts # all_texts1要额外用gpt all_text2直接返回结果 #处理跨页的段落 -def preprocess_paragraphs(paragraphs): +def preprocess_paragraphs(elements): processed = [] # 初始化处理后的段落列表 index = 0 flag = False # 初始化标志位 + is_combine_table = False # 定义两个新的正则表达式模式 pattern_numbered = re.compile(r'^\s*([一二三四五六七八九十]{1,2})\s*、\s*') @@ -114,19 +108,23 @@ def preprocess_paragraphs(paragraphs): # 辅助函数:查找上一个非空且非标记的段落 def find_prev_text(current_index): for i in range(current_index - 1, -1, -1): + if isinstance(elements[i], str) and elements[i] != '[$$table_over$$]': + return elements[i], i, True try: - text = paragraphs[i].text.strip() + text = elements[i].text.strip() except AttributeError: continue # 如果段落对象没有 text 属性,跳过 if text and not pattern_marker.search(text): - return text, i - return '', -1 + return text, i, False + return '', -1, False # 辅助函数:查找下一个非空且非标记的段落 def find_next_text(current_index): - for i in range(current_index + 1, len(paragraphs)): + for i in range(current_index + 1, len(elements)): + if isinstance(elements[i], str) and elements[i] != '[$$table_start$$]': + return elements[i], i try: - text = paragraphs[i].text.strip() + text = elements[i].text.strip() except AttributeError: continue # 如果段落对象没有 text 属性,跳过 # 跳过空白段落和页面标记 @@ -138,9 +136,13 @@ def preprocess_paragraphs(paragraphs): return text, i return '', -1 - while index < len(paragraphs): + while index < len(elements): + if isinstance(elements[index], str): + processed.append(elements[index]) + index += 1 + continue try: - current_text = paragraphs[index].text.strip() # 去除当前段落的前后空白 + current_text = elements[index].text.strip() # 去除当前段落的前后空白 except AttributeError: # 如果段落对象没有 text 属性,跳过该段落 index += 1 @@ -149,7 +151,7 @@ def preprocess_paragraphs(paragraphs): # 检查当前段落是否为页面标记 if pattern_marker.search(current_text): # 动态查找前一个非空段落 - prev_text, prev_index = find_prev_text(index) + prev_text, prev_index, is_combine_table = find_prev_text(index) # 动态查找后一个非空段落 next_text, next_index = find_next_text(index) @@ -160,13 +162,21 @@ def preprocess_paragraphs(paragraphs): if not list_item_pattern.match(next_text) and len(prev_text) > 30: # 合并前后段落 merged_text = prev_text + ' ' + next_text # 为了可读性添加空格 - if prev_index < len(paragraphs): + if prev_index < len(elements): # 移除 processed 中的前一个段落 + if is_combine_table: + while(processed[-1] != '[$$table_over$$]'): + processed.pop() + processed.pop() if processed and processed[-1] == prev_text: processed.pop() # 添加合并后的文本 processed.append(merged_text) - + if is_combine_table: + while(elements[index] != '[$$table_start$$]'): + index += 1 + index += 1 + # is_combine_table = False # 跳过标记以及前后所有空白段落,直到 next_index index = next_index + 1 continue # 继续下一个循环 @@ -175,9 +185,11 @@ def preprocess_paragraphs(paragraphs): # 计算下一个需要处理的索引 # 从当前 index 向下,跳过所有连续的空白段落和标记 skip_index = index + 1 - while skip_index < len(paragraphs): + while skip_index < len(elements): + if isinstance(elements[skip_index], str): + break try: - skip_text = paragraphs[skip_index].text.strip() + skip_text = elements[skip_index].text.strip() except AttributeError: skip_index += 1 continue # 如果段落对象没有 text 属性,跳过 @@ -240,14 +252,9 @@ def preprocess_paragraphs(paragraphs): return processed -#如果当前段落有序号,则向下匹配直接遇到相同的序号样式 -#如果当前段落无序号,则向下匹配序号,把若干同类的序号都摘出来。 -def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): - +def extract_text_with_keywords(processed_paragraphs, keywords, follow_up_keywords): if isinstance(keywords, str): keywords = [keywords] - - doc = Document(doc_path) extracted_paragraphs = OrderedDict() continue_collecting = False current_section_pattern = None @@ -266,6 +273,13 @@ def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): return current_index if continue_collecting: + # 如果是收集状态,并且下面有表格,则把表格内容全部追加到active_key中去 + if text == '[$$table_start$$]': + current_index += 1 + while (processed_paragraphs[current_index] != '[$$table_over$$]'): + extracted_paragraphs[active_key].append(processed_paragraphs[current_index]) + current_index += 1 + return current_index if current_section_pattern and re.match(current_section_pattern, text): continue_collecting = False active_key = None @@ -356,254 +370,77 @@ def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): return current_index - processed_paragraphs = preprocess_paragraphs(doc.paragraphs) index = 0 while index < len(processed_paragraphs): # print(processed_paragraphs[index].strip()) index = extract_from_text(processed_paragraphs[index].strip(), index) # print("--------------") index += 1 - return extracted_paragraphs -""" -eg: -text_list = ["这是第一句。 1. 接下来是第二句! (3) 最后一句。"] -new_text_list = ["这是第一句。", "1. 接下来是第二句!", "(3) 最后一句。"] -""" -def preprocess_text_list(text_list): - new_text_list = [] - # 正则表达式匹配中文字符或标点后的空格,该空格后紧跟字母、数字或带括号的数字 - split_pattern = re.compile(r'(?<=[\u4e00-\u9fff。;!??!;])(?=\s+[a-zA-Z\d]|\s+\([1-9]\d*\)|\s+\([1-9]\d*\))') #。;!??!; - for text in text_list: - # 使用正则表达式检查并拆分元素 - parts = split_pattern.split(text) - new_text_list.extend(part.strip() for part in parts if part.strip()) # 添加非空字符串检查 - - return new_text_list - -def clean_dict_datas(extracted_contents, keywords, excludes): # 让正则表达式提取到的东西格式化 - all_texts1 = [] - all_texts2 = [] - # 定义用于分割句子的正则表达式,包括中文和西文的结束标点 - split_pattern = r'(?<=[。!?\!\?])' - - for key, text_list in extracted_contents.items(): - if len(text_list) == 1: - for data in text_list: - # print(data) - # 检查是否包含任何需要排除的字符串 - if any(exclude in data for exclude in excludes): - continue # 如果包含任何排除字符串,跳过这个数据 - # 去掉开头的序号,eg:1 | (1) |(2) | 1. | 2.(全角点)| 3、 | 1.1 | 2.3.4 | A1 | C1.1 | 一、 - pattern = r'^\s*(?:[((]\d+[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]+|[一二三四五六七八九十]+、|[A-Z][))\.、.]?\s*)' - data = re.sub(pattern, '', data).strip() - keyword_match = re.search(keywords, data) - if keyword_match: - # 从关键词位置开始查找结束标点符号 - start_pos = keyword_match.start() - # 截取从关键词开始到后面的内容 - substring = data[start_pos:] - # 按定义的结束标点分割 - sentences = re.split(split_pattern, substring, 1) - if len(sentences) > 0 and sentences[0]: - # 只取第一句,保留标点 - cleaned_text = data[:start_pos] + sentences[0] # eg:经采购人允许,潜在投标人可进入项目现场进行考察,但潜在投标人不得因此使采购人承担有关责任和蒙受损失。潜在投标人应自行承担现场考察的全部费用、责任和风险。 - # 经采购人允许,潜在投标人可进入项目现场进行考察,但潜在投标人不得因此使采购人承担有关责任和蒙受损失。 - else: - cleaned_text = data # 如果没有标点,使用整个字符串 - else: - # 如果没有找到关键词,保留原文本 - cleaned_text = data - # 删除空格 - cleaned_text_no_spaces = cleaned_text.replace(' ', '').replace(' ', '') - # 如果长度大于8,则添加到结果列表 - if len(cleaned_text_no_spaces) > 8: - all_texts1.append(cleaned_text_no_spaces) - - else: - # print(text_list) - # print("*********") - # new_text_list = preprocess_text_list(text_list) - # 用于处理结构化文本,清理掉不必要的序号,并将分割后的段落合并,最终形成更简洁和格式化的输出。 - pattern = r'^\s*(?:[((]\d+[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]+|[一二三四五六七八九十]+、|[A-Z][))]\s+|[A-Z]\.\s*)' - data = re.sub(pattern, '', text_list[0]).strip() # 去除序号 - # 将修改后的第一个元素和剩余的元素连接起来 - text_list[0] = data # 更新列表中的第一个元素 - joined_text = "\n".join(text_list) # 如果列表中有多个元素,则连接它们 - # 删除空格 - joined_text_no_spaces = joined_text.replace(' ', '').replace(' ', '') - all_texts2.append(joined_text_no_spaces) # 将每个列表的内容添加到 all_texts 中 - - return all_texts1, all_texts2 # all_texts1要额外用gpt all_text2直接返回结果 - - -#从表格中提取数据 -def extract_table_with_keywords(data, keywords, follow_up_keywords,flag=False): - """遍历列表中的每个元素,查找并返回包含关键词的句子列表,并根据是否存在后续关键词分别存储到两个列表中。""" - sentences1 = [] # 保存没有后续关键词的情况 - sentences2 = [] # 保存有后续关键词的情况 - - # 编译关键词的正则表达式,提高匹配性能 - keywords_pattern = re.compile(keywords, re.IGNORECASE) - follow_up_patterns = [re.compile(fu, re.IGNORECASE) for fu in follow_up_keywords] - - # 检查是否包含 '无效报价' 的关键词 - check_invalid_bidding = bool(re.search(r'无\s*效\s*报\s*价', keywords, re.IGNORECASE)) - +# 分割表格中单元格文本 +def split_cell_text(text): # 定义用于提取括号内容的正则表达式,支持中英文括号 bracket_pattern = re.compile(r'[((][^(()))]+[))]') - # 遍历列表中的每个字符串元素 - for item in data: - # 只有在 keywords 包含 '无效报价' 时,才检查 "无效报价" - if check_invalid_bidding and re.search(r'无\s*效\s*报\s*价', item, re.IGNORECASE): - sentences1.append(item.strip()) - continue + # 1. 先提取并替换括号内容 + bracket_contents = [] - # 先检查 item 是否包含任意关键词,如果不包含,则跳过分割 - if not keywords_pattern.search(item): - continue + def replace_bracket_content(match): + bracket_contents.append(match.group(0)) # 保存括号内容 + return f"" # 使用占位符替换括号内容 - # 1. 先提取并替换括号内容 - bracket_contents = [] + item_with_placeholders = bracket_pattern.sub(replace_bracket_content, text) + # 2. 分割句子,保证句子完整性(按标点符号和序号分割) + split_sentences = regex.split( + r'(?<=[。!?!?\?])|' # 在中文句号、感叹号、问号后分割 + r'(?" # 使用占位符替换括号内容 + # 3. 还原括号内容 + split_sentences = [re.sub(r"", lambda m: bracket_contents[int(m.group(1))], s) for s in + split_sentences] + # 4. 过滤空字符串 + split_sentences = [s for s in split_sentences if s.strip()] - item_with_placeholders = bracket_pattern.sub(replace_bracket_content, item) + return split_sentences - # 2. 分割句子,保证句子完整性(按标点符号和序号分割) - split_sentences = regex.split( - r'(?<=[。!?!?\?])|' # 在中文句号、感叹号、问号或分号后面分割 - r'(?=\d+(?:[..]\d+)+)(?!\s*[号条款节章项例页段部步点年月日时分秒个元千万])|' # 在类似1.1 1.1.1 的数字序号前分割 - r'(?", lambda m: bracket_contents[int(m.group(1))], s) for s in - split_sentences] - - # 接下来是处理包含和不包含后续关键词的情况 - i = 0 - # 清洗模式 - clean_pattern = r'^\s*(?:[((]\s*\d+\s*[)))]|[A-Za-z]?\d+(?:\.\s*\d+)*[\s\.、.)\)]+|[一二三四五六七八九十]+、|[A-Z][))\.、.]?\s*)' - while i < len(split_sentences): - sentence = split_sentences[i].strip() - - # 如果匹配关键词 - if keywords_pattern.search(sentence): - # 检查是否存在后续关键词 - follow_up_present = any(fp.search(sentence) for fp in follow_up_patterns) - if follow_up_present: - # 如果存在后续关键词,则从当前位置开始截取 - start_index = i - end_index = start_index - found_next_section = False - for j in range(start_index + 1, len(split_sentences)): - if re.match(r'\d+[..]\d+([..]\d+)?', split_sentences[j].strip()): - end_index = j - found_next_section = True - break - if found_next_section: - full_text = ' '.join(split_sentences[start_index:end_index]).strip() - else: - full_text = ' '.join(split_sentences[start_index:]).strip() - - full_text = re.sub(clean_pattern, '', full_text).replace(' ', '').strip() - sentences2.append(full_text) # 存储有后续关键词的情况 - i = end_index if found_next_section else len(split_sentences) - else: - # 没有后续关键词的情况 - if flag: - # 当 flag=True 时,简化逻辑,直接添加清洗后的句子 - # 清洗文本 - cleaned_sentence = re.sub(clean_pattern, '', sentence).replace('\n', '').replace(' ', '').strip() - if len(cleaned_sentence) > 8: - sentences1.append(cleaned_sentence) - else: - # 如果 \n 换行符前面满足匹配,如 '无效投标\n',那么不删该 \n - sentence = re.sub(fr'({keywords})(\s*\n)', r'\1[换行标记]', sentence) - - # 清洗文本 - cleaned_sentence = re.sub(clean_pattern, '', sentence).replace('\n', '').replace(' ', '').strip() - - # 恢复保留的换行符 - cleaned_sentence = cleaned_sentence.replace('[换行标记]', '\n') - - # 检查匹配次数和是否需要切分 - matches = list(keywords_pattern.finditer(sentence)) - if len(matches) >= 2: # 如果匹配了两处及以上 - split_points = [] - for match in matches: - start, end = match.span() - print(sentence[end:end + 6]) - if sentence[end:end + 6] == "[换行标记]": - split_points.append(end) - - if len(split_points) >= 1: # 至少有一个有效切分点 - split_index = split_points[0] # 取第一个切分点 - part1 = sentence[:split_index].replace("[换行标记]", "\n").strip() - part2 = sentence[split_index:].replace("[换行标记]", "\n").strip() - # 对 part1 和 part2 进行清洗 - part1 = re.sub(clean_pattern, '', part1).replace('\n', '').replace(' ', '').strip() - part2 = re.sub(clean_pattern, '', part2).replace('\n', '').replace(' ', '').strip() - sentences1.append(part1) # 将前半部分加入结果 - sentences1.append(part2) # 将后半部分加入结果 - else: - # 如果没有足够的有效切分点,直接保留完整句子 - if len(cleaned_sentence) > 8: - sentences1.append(cleaned_sentence) - else: - # 如果只有一个匹配点或不足两处匹配 - if len(cleaned_sentence) > 8: - sentences1.append(cleaned_sentence) - - i += 1 - else: - i += 1 - return sentences1, sentences2 # 返回两个列表 - -def extract_values_if_contains(data, includes): - """ - 递归检查字典中的值是否包含列表 'includes' 中的内容。 - 如果包含,将这些值添加到一个列表中并返回。 - - 参数: - data (dict): 字典或从 JSON 解析得到的数据。 - includes (list): 包含要检查的关键词的列表。 - - 返回: - list: 包含满足条件的值的列表。 - """ - included_values = [] # 初始化结果列表 - - # 定义递归函数来处理嵌套字典 - def recursive_search(current_data): - if isinstance(current_data, dict): - for key, value in current_data.items(): - if isinstance(value, dict): - # 如果值是字典,递归搜索 - recursive_search(value) - elif isinstance(value, str): - # 如果值是字符串,检查是否包含任何 includes 中的关键词 - if any(include in value for include in includes): - included_values.append(value) - elif isinstance(current_data, list): - for item in current_data: - # 如果是列表,递归每个元素 - recursive_search(item) - - # 开始递归搜索 - recursive_search(data) - - return included_values + # 遍历文件元素 + for element in doc_elements: + # 如果是段落 + if element.tag.endswith('}p'): + doc_contents.append(doc_paragraphs[paragraph_index]) + paragraph_index += 1 + # 如果是表格 + elif element.tag.endswith('}tbl'): + doc_contents.append('[$$table_start$$]') + table = doc_tables[tables_index] + for row_idx, row in enumerate(table.rows): + # 遍历每一行中的单元格 + for cell in row.cells: + cell_text = cell.text.strip() # 去除单元格内容前后空白 + if len(cell_text) > 8: # 检查文字数量是否大于8 + cell_text = split_cell_text(cell_text) + doc_contents += cell_text + doc_contents.append('[$$table_over$$]') + tables_index += 1 + return doc_contents def handle_query(file_path, user_query, output_file, result_key, keywords): try: @@ -616,22 +453,20 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): r'其\s*他.*?情\s*形\s*[::]', r'包\s*括' ] - extracted_contents = extract_text_with_keywords(file_path, [keywords], follow_up_keywords) # 字典结果 - all_texts1, all_texts2 = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 - # print(all_texts2) - # table_data_list=read_docx_last_column(file_path) #从投标人须知前附表中提取信息生成列表data,每个元素为'一行信息' - table_data_list = read_tables_from_docx(file_path) - # print(table_data_list) - all_tables1, all_tables2 = extract_table_with_keywords(table_data_list, keywords, follow_up_keywords) - qianwen_txt = all_texts1 + all_tables1 + + doc_contents = extract_file_elements(file_path) + processed_paragraphs = preprocess_paragraphs(doc_contents) + extracted_contents = extract_text_with_keywords(processed_paragraphs, [keywords], follow_up_keywords) + all_texts = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 + + # print(all_texts) # Proceed only if there is content to write selected_contents = [] # 使用列表保持顺序 seen_contents = set() # 使用集合跟踪已添加的内容以去重 - - if qianwen_txt: + if all_texts: with open(output_file, 'w', encoding='utf-8') as file: counter = 1 - for content in qianwen_txt: + for content in all_texts: # 使用内容的前25个字符作为去重的依据 key = content[:25] # 提取前25个字符 if key not in seen_contents: # 如果前30个字符未出现过 @@ -649,19 +484,11 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): print(result_key + "选中的序号:" + str(num_list)) for index in num_list: - if 1 <= index <= len(qianwen_txt): - content = qianwen_txt[index - 1] + if 1 <= index <= len(all_texts): + content = all_texts[index - 1] # 直接添加到 selected_contents,因为前面已经按前30字符去重 selected_contents.append(content) - # 无论 qianwen_txt 是否为空,都添加 all_texts2 和 all_tables2 的内容 - for item in all_texts2 + all_tables2: - # 同样使用前25个字符判断去重 - key = item[:25] # 提取前30个字符 - if key not in seen_contents: - selected_contents.append(item) - seen_contents.add(key) - # 如果 selected_contents 不为空,则返回结果,否则返回空字符串 if selected_contents: res = {result_key: list(selected_contents)} @@ -672,14 +499,6 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): print(f"handle_query 在处理 {result_key} 时发生异常: {e}") return {result_key: ""} -# 你是一个文本助手,文本内的信息以'...............'分割,你负责准确筛选所需的信息并返回,每块信息要求完整,不遗漏,你不得擅自进行总结或删减。 -# 以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,请你根据该内容回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选符合的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,x为符合的信息的序号。 -# 以上是原文内容,文本内的信息以'...............'分割,请你根据该信息回答:否决投标或拒绝投标或无效投标或使投标失效的情况有哪些?文本中可能存在无关的信息,请你准确筛选所需的信息并返回。最终结果以json列表格式返回给我,键名为'否决和无效投标情形',你的回答完全忠于原文内容,且回答内容与原文内容一致,要求完整与准确,不能擅自总结或者概括。", - -#"以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形或是禁止投标的情形,在这些信息中,我作为投标方,需要关注和我相关的信息,请你筛选主语是投标人或中标人或供应商或联合体投标各方或磋商小组的信息,不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情况不存在,返回[]。", -#"以上是从招标文件中摘取的内容,文本内之间的信息以'...............'分割,每条信息规定了各方不得存在的情形,请回答:在这些信息中,主语是投标人或中标人或供应商或联合体投标各方或磋商小组的信息有哪些?不要返回主语是招标人或采购人或评标委员会的信息,请你筛选所需的信息并将它的序号返回。请以[x,x,x]格式返回给我结果,示例返回为[1,4,6],若情况不存在,返回[]。", - - def combine_find_invalid(invalid_docpath, output_dir): os.makedirs(output_dir, exist_ok=True) queries = [ @@ -792,13 +611,13 @@ if __name__ == '__main__': # clause_path = "D:\\flask_project\\flask_app\\static\\output\\output1\\77a48c63-f39f-419b-af2a-7b3dbf41b70b\\clause1.json" # doc_path="C:\\Users\\Administrator\\Desktop\\货物标\\zbfilesdocx\\磋商文件(1).docx" # doc_path = r'C:\Users\Administrator\Desktop\new招标文件\tmp\2024-贵州-贵州省罗甸县 2024 年度广州市协作资金龙坪镇、边阳镇产业路硬化建设项目.docx' - pdf_path = r'C:\Users\Administrator\Desktop\货物\test\磋商采购文件-恩施市森林火灾风险普查样品检测服务_invalid.pdf' + pdf_path = r'C:\Users\Administrator\Desktop\货物\test5\磋商采购文件-恩施市森林火灾风险普查样品检测服务.pdf' - output_dir = r"D:\flask_project\flask_app\static\output\output1\f91db70d-8d96-44a5-b840-27d2f1ecbe95\tmp" + output_dir = r"C:\Users\Administrator\Desktop\货物\test5" # invalid_added = insert_mark(pdf_path) # invalid_added_docx = pdf2docx(invalid_added) - invalid_added_docx=r'D:\flask_project\flask_app\static\output\output1\8a662477-a954-4b84-b9c2-d68ebd4f537b\invalid_added.docx' + invalid_added_docx=r'C:\Users\Administrator\Desktop\货物\test3\invalid_added.docx' results = combine_find_invalid(invalid_added_docx, output_dir) - end_time = time.time() - print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) - print("Elapsed time:", str(end_time - start_time)) \ No newline at end of file + # end_time = time.time() + # print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) + # print("Elapsed time:", str(end_time - start_time)) \ No newline at end of file diff --git a/flask_app/old_version/无效标和废标公共代码_old.py b/flask_app/old_version/无效标和废标公共代码_old.py index 8610886..d046093 100644 --- a/flask_app/old_version/无效标和废标公共代码_old.py +++ b/flask_app/old_version/无效标和废标公共代码_old.py @@ -366,11 +366,6 @@ def extract_text_with_keywords(doc_path, keywords, follow_up_keywords): return extracted_paragraphs -""" -eg: -text_list = ["这是第一句。 1. 接下来是第二句! (3) 最后一句。"] -new_text_list = ["这是第一句。", "1. 接下来是第二句!", "(3) 最后一句。"] -""" def preprocess_text_list(text_list): new_text_list = [] # 正则表达式匹配中文字符或标点后的空格,该空格后紧跟字母、数字或带括号的数字 @@ -476,12 +471,12 @@ def extract_table_with_keywords(data, keywords, follow_up_keywords,flag=False): # 2. 分割句子,保证句子完整性(按标点符号和序号分割) split_sentences = regex.split( - r'(?<=[。!?!?\?])|' # 在中文句号、感叹号、问号或分号后面分割 - r'(?=\d+(?:[..]\d+)+)(?!\s*[号条款节章项例页段部步点年月日时分秒个元千万])|' # 在类似1.1 1.1.1 的数字序号前分割 - r'(?" # 使用占位符替换括号内容 item_with_placeholders = bracket_pattern.sub(replace_bracket_content, text) - # 2. 分割句子,保证句子完整性(按标点符号和序号分割) split_sentences = regex.split( - r'(?<=[。!?!?\?])|' # 在中文句号、感叹号、问号或分号后面分割 - r'(?=\d+(?:[..]\d+)+)(?!\s*[号条款节章项例页段部步点年月日时分秒个元千万])|' # 在类似1.1 1.1.1 的数字序号前分割 - r'(?", lambda m: bracket_contents[int(m.group(1))], s) for s in split_sentences] + # 4. 过滤空字符串 + split_sentences = [s for s in split_sentences if s.strip()] return split_sentences @@ -457,22 +459,22 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): extracted_contents = extract_text_with_keywords(processed_paragraphs, [keywords], follow_up_keywords) all_texts = clean_dict_datas(extracted_contents, keywords, excludes) # 列表 - print(all_texts) + # print(all_texts) # Proceed only if there is content to write selected_contents = [] # 使用列表保持顺序 seen_contents = set() # 使用集合跟踪已添加的内容以去重 - # if qianwen_txt: - # with open(output_file, 'w', encoding='utf-8') as file: - # counter = 1 - # for content in qianwen_txt: - # # 使用内容的前25个字符作为去重的依据 - # key = content[:25] # 提取前25个字符 - # if key not in seen_contents: # 如果前30个字符未出现过 - # file.write(f"{counter}. {content}\n") - # file.write("..............." + '\n') - # seen_contents.add(key) # 标记前30个字符为已写入 - # counter += 1 - # + if all_texts: + with open(output_file, 'w', encoding='utf-8') as file: + counter = 1 + for content in all_texts: + # 使用内容的前25个字符作为去重的依据 + key = content[:25] # 提取前25个字符 + if key not in seen_contents: # 如果前30个字符未出现过 + file.write(f"{counter}. {content}\n") + file.write("..............." + '\n') + seen_contents.add(key) # 标记前30个字符为已写入 + counter += 1 + # # 生成用户查询 # user_query = generate_full_user_query(output_file, user_query) # model_ans = qianwen_plus(user_query) # 豆包模型返回结果 @@ -482,19 +484,11 @@ def handle_query(file_path, user_query, output_file, result_key, keywords): # print(result_key + "选中的序号:" + str(num_list)) # # for index in num_list: - # if 1 <= index <= len(qianwen_txt): - # content = qianwen_txt[index - 1] + # if 1 <= index <= len(all_texts): + # content = all_texts[index - 1] # # 直接添加到 selected_contents,因为前面已经按前30字符去重 # selected_contents.append(content) # - # # 无论 qianwen_txt 是否为空,都添加 all_texts2 和 all_tables2 的内容 - # for item in all_content: - # # 同样使用前25个字符判断去重 - # key = item[:25] # 提取前30个字符 - # if key not in seen_contents: - # selected_contents.append(item) - # seen_contents.add(key) - # # # 如果 selected_contents 不为空,则返回结果,否则返回空字符串 # if selected_contents: # res = {result_key: list(selected_contents)} @@ -537,54 +531,54 @@ def combine_find_invalid(invalid_docpath, output_dir): os.path.join(output_dir, "temp1.txt"), "否决和无效投标情形" ), -# ( -# r'废\s*标', -# """以下是从招标文件中摘取的内容,文本中序号分明,文本内之间的信息以'...............'分割。 -# 任务目标: -# 请根据以下内容,筛选出 废标项的情况 (明确描述导致 废标 的情况)并返回对应的序号。 -# 要求与指南: -# 文本中可能存在无关的信息,请准确筛选符合条件的信息,并将符合条件的信息的序号返回。 -# 输出格式: -# 返回结果以 [x, x, x] 的形式,其中 x 为符合条件的信息的序号,为自然数。 -# 如果文本中没有任何符合条件的废标情况,请返回 []。 -# 示例输出,仅供格式参考: -# [1,3,4,6] -# 文本内容:{full_text} -# """, -# os.path.join(output_dir, "temp2.txt"), -# "废标项" -# ), -# ( -# r'不\s*得(?!\s*(分|力))|禁\s*止\s*投\s*标', -# """以下是从招标文件中摘取的内容,文本中序号分明,文本内的条款以'...............'分割。条款规定了各方不得存在的情形。请根据以下要求进行筛选: -# **投标相关主体与非投标相关主体的定义**: -# 投标相关主体:包括但不限于“投标人”、“中标人”、“供应商”、“联合体投标各方”、“响应人”、“应答人”或其他描述投标方的词语。 -# 非投标相关主体:包括但不限于“招标人”、“采购人”、“评标委员会”或其他描述非投标方的词语。 -# **筛选要求**: -# 1. **仅筛选**明确描述投标相关主体禁止情形或不得存在的情形的条款,不包含笼统或未具体说明情形的条款。例如: -# 若条款内容包含'投标人不得存在的其他关联情形'这样的笼统描述,而未说明具体的情形,则无需添加该条款。 -# 2. **排除**仅描述非投标相关主体行为限制或禁止情形的条款,例如“招标人不得泄露信息”或“评标委员会不得收受贿赂”,则无需返回。 -# 3. 若条款同时描述了对投标相关主体与非投标相关主体的行为限制、禁止情形,也需返回。 -# 4. **特殊情况**:如果条款中包含“磋商小组”、”各方“等既能指代投标相关主体又能指代非投标相关主体的词汇: -# 若在语境中其指代或包含投标相关主体,则应将其考虑在内;否则,排除该条款。 -# -# **输出格式**: -# 返回结果以 [x, x, x] 的形式,其中 x 为符合条件的条款的序号,为自然数。 -# 如果没有符合条件的条款,返回 `[]`。 -# **示例**: -# - **符合条件**: -# - `1. 投标人不得...` → 包含,返回序号 1。 -# - `3. 联合体投标各方不得...` → 包含,返回序号 3。 -# - **不符合条件**: -# - `2. 采购人不得...` → 主语为“采购人”,排除。 -# -示例输出: [1,3] -# 请根据上述筛选要求,阅读以下文本内容,并返回符合条件的条款序号, -# -# 文本内容:{full_text} -# """, -# os.path.join(output_dir, "temp3.txt"), -# "不得存在的情形" -# ) + ( + r'废\s*标', + """以下是从招标文件中摘取的内容,文本中序号分明,文本内之间的信息以'...............'分割。 +任务目标: +请根据以下内容,筛选出 废标项的情况 (明确描述导致 废标 的情况)并返回对应的序号。 +要求与指南: + 文本中可能存在无关的信息,请准确筛选符合条件的信息,并将符合条件的信息的序号返回。 +输出格式: + 返回结果以 [x, x, x] 的形式,其中 x 为符合条件的信息的序号,为自然数。 + 如果文本中没有任何符合条件的废标情况,请返回 []。 +示例输出,仅供格式参考: + [1,3,4,6] +文本内容:{full_text} + """, + os.path.join(output_dir, "temp2.txt"), + "废标项" + ), + ( + r'不\s*得(?!\s*(分|力))|禁\s*止\s*投\s*标', + """以下是从招标文件中摘取的内容,文本中序号分明,文本内的条款以'...............'分割。条款规定了各方不得存在的情形。请根据以下要求进行筛选: +**投标相关主体与非投标相关主体的定义**: + 投标相关主体:包括但不限于“投标人”、“中标人”、“供应商”、“联合体投标各方”、“响应人”、“应答人”或其他描述投标方的词语。 + 非投标相关主体:包括但不限于“招标人”、“采购人”、“评标委员会”或其他描述非投标方的词语。 +**筛选要求**: +1. **仅筛选**明确描述投标相关主体禁止情形或不得存在的情形的条款,不包含笼统或未具体说明情形的条款。例如: + 若条款内容包含'投标人不得存在的其他关联情形'这样的笼统描述,而未说明具体的情形,则无需添加该条款。 +2. **排除**仅描述非投标相关主体行为限制或禁止情形的条款,例如“招标人不得泄露信息”或“评标委员会不得收受贿赂”,则无需返回。 +3. 若条款同时描述了对投标相关主体与非投标相关主体的行为限制、禁止情形,也需返回。 +4. **特殊情况**:如果条款中包含“磋商小组”、”各方“等既能指代投标相关主体又能指代非投标相关主体的词汇: + 若在语境中其指代或包含投标相关主体,则应将其考虑在内;否则,排除该条款。 + +**输出格式**: + 返回结果以 [x, x, x] 的形式,其中 x 为符合条件的条款的序号,为自然数。 + 如果没有符合条件的条款,返回 `[]`。 +**示例**: +- **符合条件**: + - `1. 投标人不得...` → 包含,返回序号 1。 + - `3. 联合体投标各方不得...` → 包含,返回序号 3。 +- **不符合条件**: + - `2. 采购人不得...` → 主语为“采购人”,排除。 +-示例输出: [1,3] +请根据上述筛选要求,阅读以下文本内容,并返回符合条件的条款序号, + +文本内容:{full_text} + """, + os.path.join(output_dir, "temp3.txt"), + "不得存在的情形" + ) ] results = [] @@ -617,13 +611,13 @@ if __name__ == '__main__': # clause_path = "D:\\flask_project\\flask_app\\static\\output\\output1\\77a48c63-f39f-419b-af2a-7b3dbf41b70b\\clause1.json" # doc_path="C:\\Users\\Administrator\\Desktop\\货物标\\zbfilesdocx\\磋商文件(1).docx" # doc_path = r'C:\Users\Administrator\Desktop\new招标文件\tmp\2024-贵州-贵州省罗甸县 2024 年度广州市协作资金龙坪镇、边阳镇产业路硬化建设项目.docx' - pdf_path = r'C:\Users\Administrator\Desktop\货物\test\磋商采购文件-恩施市森林火灾风险普查样品检测服务_invalid.pdf' + pdf_path = r'C:\Users\Administrator\Desktop\货物\test5\磋商采购文件-恩施市森林火灾风险普查样品检测服务.pdf' - output_dir = r"D:\flask_project\flask_app\static\output\output1\f91db70d-8d96-44a5-b840-27d2f1ecbe95\tmp" + output_dir = r"C:\Users\Administrator\Desktop\货物\test5" # invalid_added = insert_mark(pdf_path) # invalid_added_docx = pdf2docx(invalid_added) - invalid_added_docx=r'D:\flask_project\flask_app\static\output\output1\8a662477-a954-4b84-b9c2-d68ebd4f537b\invalid_added.docx' + invalid_added_docx=r'C:\Users\Administrator\Desktop\货物\test3\invalid_added.docx' results = combine_find_invalid(invalid_added_docx, output_dir) - end_time = time.time() - print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) - print("Elapsed time:", str(end_time - start_time)) \ No newline at end of file + # end_time = time.time() + # print("Results:", json.dumps(results, ensure_ascii=False, indent=4)) + # print("Elapsed time:", str(end_time - start_time)) \ No newline at end of file diff --git a/flask_app/工程标/截取pdf工程标版.py b/flask_app/工程标/截取pdf工程标版.py index 0b822bc..f8749b1 100644 --- a/flask_app/工程标/截取pdf工程标版.py +++ b/flask_app/工程标/截取pdf工程标版.py @@ -129,11 +129,11 @@ def truncate_pdf_main_engineering(input_path, output_folder, selection, logger, ), ( regex.compile( - r'.*(?