Python如何遍历嵌套的ContractorData层级结构并检查状态?
可行的遍历方案
1. 修复递归遍历函数
你的递归雏形存在逻辑问题:sub.subs是子承包商的列表,不能直接传入walk函数,需要遍历每个子项递归调用:
def walk_contractor(contractor): # 处理当前承包商的逻辑 if contractor.status != 'Enrolled': # 执行你的操作,比如标记、触发流程或打印日志 print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})") # 递归遍历所有子承包商 for sub in contractor.subs: walk_contractor(sub)
调用方式:直接传入根节点 walk_contractor(general_contractor)
2. 迭代式遍历(避免递归深度限制)
如果承包商层级极深,递归可能触发RecursionError,用迭代实现的深度/广度优先遍历更稳妥:
深度优先遍历(迭代版)
def dfs_walk(root): stack = [root] while stack: contractor = stack.pop() # 处理当前承包商 if contractor.status != 'Enrolled': print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})") # 子项倒序入栈,保证遍历顺序和递归一致 for sub in reversed(contractor.subs): stack.append(sub)
广度优先遍历(按层级顺序遍历)
def bfs_walk(root): queue = [root] while queue: contractor = queue.pop(0) # 处理当前承包商 if contractor.status != 'Enrolled': print(f"处理非Enrolled承包商:{contractor.contractorName}(状态:{contractor.status})") # 子项入队 queue.extend(contractor.subs)
架构优化建议
1. 优化ContractorData初始化逻辑
当前代码重复调用soup.find('a', id=True),缓存重复查找的元素可减少IO开销:
class ContractorData(): def __init__(self, soup:BeautifulSoup, parentId=None): # 缓存重复查找的元素 name_link = soup.find('a', id=True) self.contractorName = name_link.getText() self.id = int(name_link.get('href').split('&')[-1].split('=')[1]) status_options = ['Enrolled', 'Excluded', 'Pending'] self.status = 'Unknown' for i in range(3): cls = f'contractorStatusCol{i+1}' status_div = soup.find('div', class_=cls) if not status_div: continue status_img = status_div.find('img') if status_img and 'gray' not in status_img.get('src'): self.status = status_options[i] break date_col = soup.find('div', class_='contractorStatusCol4') self.date_enrolled = date_col.get_text().replace(u'\xa0', '') if date_col else '' self.has_subs = soup.find('a', class_="expand") is not None self.subs = [] self.parent = parentId
2. 给ContractorData内置遍历能力
把遍历逻辑封装到类中,符合面向对象设计,调用更直观:
class ContractorData(): # ... 保留原有初始化代码 ... def walk(self, handler): """遍历当前及所有子承包商,对每个实例执行handler回调""" handler(self) for sub in self.subs: sub.walk(handler) def iter_all(self): """生成器,返回当前及所有子承包商的迭代器""" yield self for sub in self.subs: yield from sub.iter_all()
使用示例:
# 用walk方法处理 def handle_contractor(contractor): if contractor.status != 'Enrolled': print(f"需处理:{contractor.contractorName}") general_contractor.walk(handle_contractor) # 用生成器遍历 for contractor in general_contractor.iter_all(): if contractor.status != 'Enrolled': # 执行你的业务操作 pass
3. 优化层级加载逻辑(可选延迟加载)
当前一次性加载所有层级,可改为延迟加载,减少初始请求量,仅在需要时加载子承包商:
class ContractorData(): def __init__(self, soup:BeautifulSoup, parentId=None): # ... 保留原有初始化代码 ... self._subs_loaded = False # 标记子项是否已加载 def load_subs(self, pid, level): if not self.has_subs or self._subs_loaded: return self.subs = load_subs(pid, self.id, level) self._subs_loaded = True
遍历时代按需加载:
def walk_with_lazy_load(contractor, pid, level): if contractor.status != 'Enrolled': print(f"处理:{contractor.contractorName}") if contractor.has_subs: contractor.load_subs(pid, level + 1) for sub in contractor.subs: walk_with_lazy_load(sub, pid, level + 1)
4. 提升状态判断的健壮性
当前依赖固定顺序的列判断状态,若页面布局变化会失效,可改为通过图片的关键词或alt属性判断:
# 示例:通过图片src关键词判断状态 status_img = soup.find('img', class_='status-icon') # 假设状态图标有统一类名 if status_img: src = status_img.get('src', '') if 'enrolled' in src: self.status = 'Enrolled' elif 'excluded' in src: self.status = 'Excluded' elif 'pending' in src: self.status = 'Pending' else: self.status = 'Unknown'
内容的提问来源于stack exchange,提问作者Mike Dergance
相关产品推荐
相关产品推荐

