import os # 导入操作系统模块,用于文件系统操作
import re # 导入正则表达式模块,用于文本匹配
import pandas as pd # 导入pandas库,用于数据处理和Excel输出
from bs4 import BeautifulSoup # 导入BeautifulSoup库,用于HTML解析
# 定义主字段和嵌套字段结构
MAIN_FIELDS = [
'主体名称', '行业门类', '行业信息', # 定义企业基本信息字段
'登记状态', '法定代表人', '经营场所', '实际经营地址', '经营范围' # 定义企业登记信息字段
]
NESTED_FIELDS = {
'网店信息': ['所属平台', '网店名称', '网店链接', '开店时间'] # 定义网店相关的嵌套字段
}
def format_nested_data(group, items):
"""将嵌套字段数据格式化为指定字符串格式"""
if not items: # 如果没有数据项,返回空字符串
return ""
if group == '网店信息': # 如果是网店信息组
# 只提取网店名称,并用顿号分隔
store_names = [item.get('网店名称', '').strip() for item in items if item.get('网店名称', '').strip()] # 从每项数据中提取网店名称并去空格
# 去除重复的网店名称
unique_store_names = list(dict.fromkeys(store_names)) # 使用字典的键唯一性去重,并保持原有顺序
return "、".join(unique_store_names) # 用顿号连接所有唯一的网店名称
return "" # 对于其他组,返回空字符串
def extract_field(soup, field):
# 使用更精确的CSS选择器定位
selector_map = {
'主体名称': '.company-name, .enterprise-name, .ent-name, .enterprise-title, h1, .title', # 定义主体名称可能的CSS选择器
}
if field in selector_map: # 如果字段有对应的选择器
elements = soup.select(selector_map[field]) # 使用选择器查找元素
if elements: # 如果找到元素
for elem in elements: # 遍历所有找到的元素
text = elem.text.strip() # 获取元素文本并去除首尾空格
if text and ('主体名称' == field or not re.match(r'^\d+$', text)): # 确保文本不为空且不是纯数字(除非是主体名称)
return text # 返回找到的文本
# 回退到原始文本搜索
text_nodes = soup.find_all(text=re.compile(field)) # 查找包含字段名的文本节点
for node in text_nodes: # 遍历所有匹配的文本节点
# 检查父元素的下一个元素
next_elem = node.parent.find_next() # 获取文本节点父元素的下一个元素
if next_elem and next_elem.name not in ['script', 'style']: # 确保下一个元素不是脚本或样式元素
return next_elem.text.strip() # 返回下一个元素的文本内容
# 检查文本节点是否包含冒号,可能是"主体名称:XXX"的形式
if ':' in node or ':' in node: # 检查节点是否包含英文冒号或中文冒号
text = node.strip() # 获取节点文本并去除首尾空格
if ':' in text: # 如果包含英文冒号
parts = text.split(':', 1) # 以英文冒号分割文本(最多分割一次)
else: # 如果包含中文冒号
parts = text.split(':', 1) # 以中文冒号分割文本(最多分割一次)
if len(parts) > 1 and field in parts[0]: # 确保分割后有两部分,且第一部分包含字段名
return parts[1].strip() # 返回冒号后的值并去除首尾空格
# 尝试查找可能包含字段的表格
tables = soup.find_all('table') # 查找所有表格
for table in tables: # 遍历所有表格
rows = table.find_all('tr') # 获取表格所有行
for row in rows: # 遍历每一行
cells = row.find_all(['th', 'td']) # 查找行中的所有表头和单元格
if len(cells) >= 2: # 确保至少有两个单元格(键值对)
for idx in range(len(cells) - 1): # 遍历除最后一个单元格外的所有单元格
if field in cells[idx].text: # 如果单元格文本包含字段名
return cells[idx+1].text.strip() # 返回下一个单元格的值
return "" # 如果未找到值,返回空字符串
def extract_nested_table_data(soup, group_name, subfields):
"""提取嵌套表格数据"""
items = [] # 存储提取到的数据项
debug_info = [] # 存储调试信息
# 打印调试信息
debug_info.append(f"正在查找: {group_name}") # 记录当前正在查找的组名
# 先尝试直接通过文本内容查找
group_headers = soup.find_all(string=lambda text: text and group_name in text) # 查找包含组名的文本节点
if not group_headers: # 如果没找到
# 如果没找到,尝试使用正则表达式查找
group_headers = soup.find_all(string=re.compile(group_name)) # 使用正则表达式查找
debug_info.append(f"找到{len(group_headers)}个可能的标题元素") # 记录找到的标题数量
if not group_headers: # 如果依然没有找到
# 如果依然找不到,尝试查找可能包含此信息的div或表格
if group_name == '网店信息': # 特殊处理网店信息
# 尝试查找可能包含网店信息的表格
tables = soup.find_all('table') # 查找所有表格
for table in tables: # 遍历所有表格
rows = table.find_all('tr') # 获取表格中的所有行
if len(rows) > 1: # 假设表格至少有一个标题行和一个数据行
cells = rows[1].find_all('td') # 获取第二行(第一个数据行)的所有单元格
if len(cells) >= 4: # 假设至少有4列对应我们需要的字段
# 检查表格内容是否看起来像网店信息
text_content = ' '.join([cell.text for cell in cells]) # 合并所有单元格文本
if any(keyword in text_content for keyword in ['平台', '网店', '链接']): # 检查是否包含关键词
debug_info.append(f"通过关键词匹配找到可能的网店信息表格") # 记录找到的表格
# 处理每一行数据
for row in rows[1:]: # 跳过标题行,遍历数据行
cells = row.find_all('td') # 获取行中的所有单元格
if len(cells) >= 4: # 确保有足够的单元格
item = { # 创建数据项字典
'所属平台': cells[0].text.strip(), # 第一列为所属平台
'网店名称': cells[1].text.strip(), # 第二列为网店名称
'网店链接': cells[2].text.strip() if len(cells) > 2 else '', # 第三列为网店链接
'开店时间': cells[3].text.strip() if len(cells) > 3 else '' # 第四列为开店时间
}
items.append(item) # 将数据项添加到结果列表
# 处理找到的每个标题元素
for header_idx, header in enumerate(group_headers): # 遍历所有找到的标题元素
debug_info.append(f"正在处理第{header_idx+1}个标题元素") # 记录当前处理的标题索引
# 查找表格 - 尝试多种方法
tables = [] # 存储找到的表格
# 方法1: 查找标题元素的下一个表格
next_table = header.find_next('table') # 获取标题元素后的第一个表格
if next_table: # 如果找到表格
tables.append(next_table) # 添加到表格列表
debug_info.append("找到下一个表格") # 记录日志
# 方法2: 查找父元素中的表格
parent = header.parent # 获取标题元素的父元素
for _ in range(5): # 向上查找5层
if parent: # 如果父元素存在
parent_tables = parent.find_all('table') # 查找父元素中的所有表格
if parent_tables: # 如果找到表格
tables.extend(parent_tables) # 将表格添加到列表
debug_info.append(f"在父元素中找到{len(parent_tables)}个表格") # 记录日志
parent = parent.parent # 继续向上查找父元素
else:
break # 如果没有父元素,退出循环
# 方法3: 如果标题在表格标题中,直接找该表格
current = header # 从标题元素开始
for _ in range(3): # 向上查找3层
if current.name == 'table': # 如果当前元素是表格
tables.append(current) # 将表格添加到列表
debug_info.append("标题本身在表格中") # 记录日志
break # 退出循环
elif current.parent: # 如果有父元素
current = current.parent # 移动到父元素
else:
break # 如果没有父元素,退出循环
debug_info.append(f"总共找到{len(tables)}个相关表格") # 记录找到的表格总数
# 处理找到的每个表格
for table in tables: # 遍历所有找到的表格
# 尝试多种选择器来找表格行
selectors = [ # 定义可能的CSS选择器
'tr.el-table__row', # Element UI表格行
'tr', # 普通表格行
'.el-table__row', # Element UI表格行(不一定在tr中)
'tbody tr' # 表格体中的行
]
for selector in selectors: # 遍历所有选择器
rows = table.select(selector) # 使用选择器查找行
debug_info.append(f"使用选择器'{selector}'找到{len(rows)}行") # 记录找到的行数
if rows: # 如果找到行
# 处理每一行
for row_elem in rows: # 遍历每一行
# 跳过标题行
if row_elem.find('th'): # 如果行中包含表头单元格
continue # 跳过这一行
# 尝试多种方式获取单元格
cells = row_elem.select('td') # 使用CSS选择器查找单元格
if not cells: # 如果没有找到单元格
cells = row_elem.find_all('td') # 使用find_all方法查找单元格
if len(cells) >= len(subfields): # 确保单元格数量不少于子字段数
item = {} # 创建数据项字典
for field_idx, field in enumerate(subfields): # 遍历子字段
# 尝试多种方式获取单元格内容
cell = cells[field_idx] # 获取对应索引的单元格
# 先查找div,然后查找任何子元素,最后使用文本
content_elem = cell.select_one('div') or cell.find() or cell # 获取内容元素
cell_content = content_elem.text.strip() # 获取元素文本并去除首尾空格
item[field] = cell_content # 将值存入数据项字典
items.append(item) # 将数据项添加到结果列表
# 如果找到了行,就跳出选择器循环
if items: # 如果已经提取到数据项
break # 退出选择器循环
# 如果已经从这个表格中找到了项目,就跳出表格循环
if items: # 如果已经提取到数据项
debug_info.append(f"成功从表格中提取了{len(items)}项数据") # 记录提取成功
break # 退出表格循环
print(f"\n===调试信息: {group_name}===") # 打印调试信息标题
for line in debug_info: # 遍历所有调试信息
print(line) # 打印每行调试信息
print(f"提取到{len(items)}项数据") # 打印提取到的数据项数量
if items: # 如果有数据项
print(f"第一项数据示例: {items[0]}") # 打印第一个数据项作为示例
print("==============\n") # 打印分隔线
return items # 返回提取到的所有数据项
if __name__ == "__main__": # 如果直接运行此脚本
data = [] # 初始化数据列表
html_files = [f for f in os.listdir('.') if f.endswith('.html')] # 获取当前目录下所有HTML文件
if not html_files: # 如果没有找到HTML文件
print("当前目录中没有找到HTML文件。请确保HTML文件与脚本在同一目录。") # 打印提示信息
else: # 如果找到HTML文件
print(f"找到{len(html_files)}个HTML文件: {', '.join(html_files)}") # 打印找到的HTML文件数量和名称
for file in html_files: # 遍历每个HTML文件
print(f"\n正在处理文件: {file}") # 打印当前处理的文件名
with open(file, 'r', encoding='utf-8', errors='ignore') as f: # 打开HTML文件,忽略编码错误
html_content = f.read() # 读取文件内容
soup = BeautifulSoup(html_content, 'html.parser') # 创建BeautifulSoup对象解析HTML
item_data = {} # 初始化数据项字典
# 提取主字段
for field in MAIN_FIELDS: # 遍历主字段
value = extract_field(soup, field) # 从HTML中提取字段值
item_data[field] = value # 将值存入数据项字典
if field in ['主体名称']: # 如果是主体名称字段
print(f"{field}: {value}") # 打印主体名称的值
# 提取嵌套字段
for group, subfields in NESTED_FIELDS.items(): # 遍历嵌套字段组
nested_items = extract_nested_table_data(soup, group, subfields) # 提取嵌套表格数据
formatted_data = format_nested_data(group, nested_items) # 格式化嵌套数据
item_data[group] = formatted_data # 将格式化后的数据存入数据项字典
print(f"{group} 提取结果: {formatted_data if formatted_data else '空'}") # 打印提取结果
data.append(item_data) # 将数据项添加到数据列表
# 验证数据是否有效
if data: # 如果有数据
# 保留主字段和格式化后的嵌套字段
columns = MAIN_FIELDS + list(NESTED_FIELDS.keys()) # 合并主字段和嵌套字段组名作为列
df = pd.DataFrame(data, columns=columns) # 创建DataFrame
df.to_excel('output.xlsx', index=False) # 导出为Excel文件
print(f"\n成功提取{len(data)}个文件的数据,已保存到output.xlsx") # 打印成功信息
else: # 如果没有数据
print("\n未找到任何数据,请检查HTML结构或字段名称") # 打印错误提示
Post Views: 13