mengtaoguo commited on
Commit
5da291c
·
verified ·
1 Parent(s): d34acc7

Update bs4_quality_scanner.py

Browse files
Files changed (1) hide show
  1. bs4_quality_scanner.py +29 -28
bs4_quality_scanner.py CHANGED
@@ -8,8 +8,7 @@ url = os.environ.get("SUPABASE_URL")
8
  key = os.environ.get("SUPABASE_SERVICE_ROLE_KEY")
9
  supabase = create_client(url, key)
10
 
11
- # --- 定义每个字段的“正常范围”和“异常规则 ---
12
- # min_len: 低于此长度疑似抓取不全; max_len: 高于此长度疑似抓错位置或吞了后面内容
13
  FIELD_RULES = {
14
  'latin_name': {'min': 5, 'max': 80},
15
  'common_name': {'min': 2, 'max': 100},
@@ -24,11 +23,17 @@ FIELD_RULES = {
24
  'medicinal_uses': {'min': 10, 'max': 5000},
25
  'other_uses': {'min': 10, 'max': 5000},
26
  'cultivation_details': {'min': 20, 'max': 8000},
27
- 'propagation': {'min': 20, 'max': 4000}
 
 
 
 
 
 
28
  }
29
 
30
  def analyze_value_pro(field_name, val):
31
- """增强版检测:判定空值、乱码及离群异常"""
32
  raw_s = str(val or "")
33
  s = raw_s.strip()
34
 
@@ -39,31 +44,30 @@ def analyze_value_pro(field_name, val):
39
  # 1. 乱码检测
40
  has_garbage = bool(re.search(r'[ÂÃÅÐÑÒÓÔÕÖר]', s))
41
 
42
- # 2. 离群值/异常检测 (Outlier)
43
  is_outlier = False
44
  length = len(s)
45
 
46
  if field_name in FIELD_RULES:
47
  rule = FIELD_RULES[field_name]
48
- # 长度异常判定
49
  if length < rule['min'] or length > rule['max']:
50
  is_outlier = True
51
- # 结构异常判定:如果包含超过 5 个连续空格,或者包含 HTML 标签
52
- if " " in raw_s or "<div>" in raw_s.lower() or "<span>" in raw_s.lower():
53
  is_outlier = True
54
 
55
  return False, (has_garbage or is_outlier), length
56
 
57
  def run_bs4_audit():
58
  target_columns = list(FIELD_RULES.keys())
59
- # 增加 outlier 统计项
60
  stats = {col: {'filled': 0, 'empty': 0, 'outlier': 0, 'len_sum': 0, 'score_sum': 0} for col in target_columns}
61
 
62
- log(f"\n[{time.strftime('%H:%M:%S')}] 🛡️ 启动离群值扫描 (深度体检模式)...")
63
 
64
  offset = 0
65
  limit = 200
66
  while True:
 
67
  res = supabase.table("bs4_plants").select(",".join(target_columns)).range(offset, offset + limit - 1).execute()
68
  if not res.data: break
69
 
@@ -79,21 +83,18 @@ def run_bs4_audit():
79
  if is_bad:
80
  stats[col]['outlier'] += 1
81
 
82
- # 评分逻辑优化:离群异常和乱码按 50 分计(警告级),空值 0 分
83
- if is_emp: score = 0
84
- elif is_bad: score = 50
85
- else: score = 100
86
  stats[col]['score_sum'] += score
87
 
88
  offset += len(res.data)
89
 
90
- # --- 控制台可视化报告 ---
91
- log("\n" + "="*95)
92
  log(f"{'字段名称':<25} | {'填充':<6} | {'异常/离群':<10} | {'空缺':<6} | {'均长':<6} | {'健康分'}")
93
- log("-" * 95)
94
 
95
  report_batch = []
96
- # 按健康分从低到高排序
97
  sorted_cols = sorted(stats.items(), key=lambda x: (x[1]['score_sum']/offset if offset>0 else 0))
98
 
99
  for col, data in sorted_cols:
@@ -102,26 +103,26 @@ def run_bs4_audit():
102
 
103
  log(f"{col:<25} | {data['filled']:<6} | {data['outlier']:<10} | {data['empty']:<6} | {avg_len:<6} | {total_score}")
104
 
105
- # 为了兼容你之前的数据库表,我们将 outlier 存入原本的 dirty_count 字段,或者你可以扩充字段
106
  report_batch.append({
107
  "field_name": col,
108
  "total_records": offset,
109
  "filled_count": data['filled'],
110
  "empty_count": data['empty'],
111
- "dirty_count": data['outlier'], # 现在这个字段代表广义的“坏数据”
112
  "avg_char_count": avg_len,
113
  "quality_score": total_score,
114
  "updated_at": "now()"
115
  })
116
 
 
117
  supabase.table("bs4_field_quality_report").upsert(report_batch).execute()
118
- log("="*95)
119
- log(f"✅ 深度巡检完成。异常记录已标记,下次巡检 10 分钟后...")
120
 
121
  if __name__ == "__main__":
122
- while True:
123
- try:
124
- run_bs4_audit()
125
- except Exception as e:
126
- log(f"❌ 运行报错: {e}")
127
- time.sleep(600)
 
8
  key = os.environ.get("SUPABASE_SERVICE_ROLE_KEY")
9
  supabase = create_client(url, key)
10
 
11
+ # --- 扩展后的字段规则字典 (同步 JSON 补齐字段) ---
 
12
  FIELD_RULES = {
13
  'latin_name': {'min': 5, 'max': 80},
14
  'common_name': {'min': 2, 'max': 100},
 
23
  'medicinal_uses': {'min': 10, 'max': 5000},
24
  'other_uses': {'min': 10, 'max': 5000},
25
  'cultivation_details': {'min': 20, 'max': 8000},
26
+ 'propagation': {'min': 20, 'max': 4000},
27
+ # --- 新增补齐字段 ---
28
+ 'weed_potential': {'min': 2, 'max': 50},
29
+ 'found_in': {'min': 5, 'max': 5000},
30
+ 'conservation_status': {'min': 5, 'max': 500},
31
+ 'special_uses': {'min': 5, 'max': 1000},
32
+ 'author': {'min': 1, 'max': 50}
33
  }
34
 
35
  def analyze_value_pro(field_name, val):
36
+ """深度检测逻辑"""
37
  raw_s = str(val or "")
38
  s = raw_s.strip()
39
 
 
44
  # 1. 乱码检测
45
  has_garbage = bool(re.search(r'[ÂÃÅÐÑÒÓÔÕÖר]', s))
46
 
47
+ # 2. 离群值检测
48
  is_outlier = False
49
  length = len(s)
50
 
51
  if field_name in FIELD_RULES:
52
  rule = FIELD_RULES[field_name]
 
53
  if length < rule['min'] or length > rule['max']:
54
  is_outlier = True
55
+ # 结构异常判定:HTML残留或异常空白
56
+ if " " in raw_s or "<div>" in raw_s.lower():
57
  is_outlier = True
58
 
59
  return False, (has_garbage or is_outlier), length
60
 
61
  def run_bs4_audit():
62
  target_columns = list(FIELD_RULES.keys())
 
63
  stats = {col: {'filled': 0, 'empty': 0, 'outlier': 0, 'len_sum': 0, 'score_sum': 0} for col in target_columns}
64
 
65
+ log(f"\n[{time.strftime('%H:%M:%S')}] 🛡️ 启动全字段深度扫描 (含新增字段)...")
66
 
67
  offset = 0
68
  limit = 200
69
  while True:
70
+ # 动态获取字段
71
  res = supabase.table("bs4_plants").select(",".join(target_columns)).range(offset, offset + limit - 1).execute()
72
  if not res.data: break
73
 
 
83
  if is_bad:
84
  stats[col]['outlier'] += 1
85
 
86
+ # 质量评分
87
+ score = 0 if is_emp else (50 if is_bad else 100)
 
 
88
  stats[col]['score_sum'] += score
89
 
90
  offset += len(res.data)
91
 
92
+ # --- 输出可视化表格 ---
93
+ log("\n" + "="*100)
94
  log(f"{'字段名称':<25} | {'填充':<6} | {'异常/离群':<10} | {'空缺':<6} | {'均长':<6} | {'健康分'}")
95
+ log("-" * 100)
96
 
97
  report_batch = []
 
98
  sorted_cols = sorted(stats.items(), key=lambda x: (x[1]['score_sum']/offset if offset>0 else 0))
99
 
100
  for col, data in sorted_cols:
 
103
 
104
  log(f"{col:<25} | {data['filled']:<6} | {data['outlier']:<10} | {data['empty']:<6} | {avg_len:<6} | {total_score}")
105
 
 
106
  report_batch.append({
107
  "field_name": col,
108
  "total_records": offset,
109
  "filled_count": data['filled'],
110
  "empty_count": data['empty'],
111
+ "dirty_count": data['outlier'],
112
  "avg_char_count": avg_len,
113
  "quality_score": total_score,
114
  "updated_at": "now()"
115
  })
116
 
117
+ # 回写到统计表
118
  supabase.table("bs4_field_quality_report").upsert(report_batch).execute()
119
+ log("="*100)
120
+ log(f"✅ 巡检完成。共处理 {offset} 条记录。")
121
 
122
  if __name__ == "__main__":
123
+ # 保持之前商定的逻辑:启动时运行一遍。
124
+ # 如果想手动触发,重启 Space 即可。
125
+ try:
126
+ run_bs4_audit()
127
+ except Exception as e:
128
+ log(f"❌ 运行报错: {e}")