mdbq 3.12.2__py3-none-any.whl → 3.12.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mdbq/__version__.py CHANGED
@@ -1 +1 @@
1
- VERSION = '3.12.2'
1
+ VERSION = '3.12.4'
@@ -16,7 +16,6 @@ from datetime import datetime
16
16
  import uuid
17
17
  from contextlib import contextmanager
18
18
 
19
-
20
19
  warnings.filterwarnings('ignore')
21
20
  logger = mylogger.MyLogger(
22
21
  name='deduplicator',
@@ -34,7 +33,22 @@ logger = mylogger.MyLogger(
34
33
 
35
34
  class MySQLDeduplicator:
36
35
  """
37
- MySQL数据去重
36
+ MySQL 数据去重。
37
+
38
+ 支持按天分区(如有 date_column)或全表去重,支持自定义去重分组字段(columns),可排除指定列(exclude_columns),并支持多线程并发。
39
+
40
+ 主要参数说明:
41
+ - columns: 指定去重分组字段,控制唯一性分组行为。若 columns 有值且不包含 date_column,则全表去重,否则按天分区。
42
+ - exclude_columns: 去重时排除的列名列表,自动合并 ['id', '更新时间'],在分组时排除。
43
+ - date_column: 指定日期分区字段,默认为 '日期'。如表存在该字段且 columns 未排除,则按天分区去重。
44
+ - duplicate_keep_mode: 'keep_one'(默认,重复组保留一条),'remove_all'(全部删除重复组)。
45
+ - dry_run: 是否为模拟运行,不实际删除数据。
46
+ - use_python_dedup: 是否用 Python 方式去重(否则用 SQL)。
47
+
48
+ 分天/全表去重行为:
49
+ - 若 columns 有值且不包含 date_column,则直接全表去重,分组字段为 columns。
50
+ - 否则,若表存在 date_column,则按天分区去重,分组字段为 columns(如有)或全表字段。
51
+ - exclude_columns 始终生效,分组时自动排除。
38
52
  """
39
53
 
40
54
  def __init__(
@@ -57,7 +71,8 @@ class MySQLDeduplicator:
57
71
  exclude_columns: Optional[List[str]] = None,
58
72
  exclude_databases: Optional[List[str]] = None,
59
73
  exclude_tables: Optional[Dict[str, List[str]]] = None,
60
- duplicate_keep_mode: str = 'keep_one'
74
+ duplicate_keep_mode: str = 'keep_one',
75
+ keep_order: str = 'min'
61
76
  ) -> None:
62
77
  """
63
78
  初始化去重处理器
@@ -163,6 +178,7 @@ class MySQLDeduplicator:
163
178
  self.exclude_tables = {k.lower(): set(t.lower() for t in v) for k, v in (exclude_tables or {}).items()}
164
179
 
165
180
  self.duplicate_keep_mode = duplicate_keep_mode if duplicate_keep_mode in ('keep_one', 'remove_all') else 'keep_one'
181
+ self.keep_order = keep_order if keep_order in ('min', 'max') else 'min'
166
182
 
167
183
  def _get_connection(self) -> pymysql.connections.Connection:
168
184
  """
@@ -370,15 +386,17 @@ class MySQLDeduplicator:
370
386
  lock_table: bool = True
371
387
  ) -> Tuple[int, int]:
372
388
  """
373
- 执行单表单天去重。只处理 date_val 这一天的数据(如果有 date_column),否则全表。
374
-
389
+ 执行单表单天或全表去重。根据 columns date_column 决定分天或全表:
390
+ - 若 columns 不包含 date_column,则全表去重(不分天)。
391
+ - 若 columns 包含 date_column 或未指定 columns,则按天分区去重(date_val 为当前处理日期)。
392
+
375
393
  Args:
376
394
  database (str): 数据库名。
377
395
  table (str): 表名。
378
- columns (Optional[List[str]]): 指定去重列。
396
+ columns (Optional[List[str]]): 指定去重列。若不含 date_column,则全表去重。
379
397
  dry_run (bool): 是否为模拟运行。
380
398
  use_python_dedup (bool): 是否用 Python 方式去重。
381
- date_val (Optional[str]): 指定处理的日期(如有 date_column)。
399
+ date_val (Optional[str]): 指定处理的日期(如有 date_column 且分天时)。
382
400
  lock_table (bool): 是否加表级锁。
383
401
  Returns:
384
402
  Tuple[int, int]: (重复组数, 实际删除行数)
@@ -392,13 +410,12 @@ class MySQLDeduplicator:
392
410
  exclude_columns_lower = [col.lower() for col in getattr(self, 'exclude_columns', [])]
393
411
  time_col = self.date_column
394
412
  time_col_lower = time_col.lower() if time_col else None
395
- if time_col_lower and time_col_lower in exclude_columns_lower:
396
- logger.warning('date_column在exclude_columns中,跳过该表', {"库": database, "表": table, "date_column": time_col, "exclude_columns": self.exclude_columns})
397
- return (0, 0)
398
- has_time_col = time_col_lower in all_columns_lower if time_col_lower else False
399
-
400
- # 只要有date_column,始终分天处理(本函数只处理一天)
401
- if has_time_col and date_val is not None:
413
+ # 如果传了columns且columns不包含date_column,则不分天,直接全表去重
414
+ if columns and (not time_col_lower or time_col_lower not in [c.lower() for c in columns]):
415
+ has_time_col = False # 全表去重
416
+ else:
417
+ has_time_col = time_col_lower in all_columns_lower if time_col_lower else False # 分天去重
418
+ if has_time_col:
402
419
  self._ensure_index(database, table, time_col)
403
420
  # 获取去重列
404
421
  use_columns = columns or all_columns
@@ -426,39 +443,58 @@ class MySQLDeduplicator:
426
443
 
427
444
  # 用Python查找重复
428
445
  if use_python_dedup:
446
+ # 判断分组字段是否有“更新时间”
447
+ has_update_time = any(col == '更新时间' for col in use_columns)
429
448
  select_cols = f'`{pk_real}`,' + ','.join([f'`{col}`' for col in use_columns])
430
- select_where = f"WHERE `{time_col}` = '{date_val}'"
449
+ if has_update_time:
450
+ select_cols += ',`更新时间`'
451
+ select_where = f"WHERE `{time_col}` = '{date_val}'" if date_val else ''
431
452
  grouped = defaultdict(list)
432
453
  for row in self._row_generator(database, table, select_cols, select_where, self.batch_size):
433
454
  key = tuple(row[col] for col in use_columns)
434
- grouped[key].append(row[pk_real])
455
+ grouped[key].append(row)
435
456
  dup_count = 0
436
457
  del_ids = []
437
458
  for ids in grouped.values():
438
459
  if len(ids) > 1:
439
460
  dup_count += 1
440
- del_ids.extend(ids[1:])
461
+ if has_update_time:
462
+ # 按更新时间最大保留
463
+ keep_row = max(ids, key=lambda x: x.get('更新时间') or '')
464
+ else:
465
+ # 按id保留
466
+ if self.keep_order == 'max':
467
+ keep_row = max(ids, key=lambda x: x[pk_real])
468
+ else:
469
+ keep_row = min(ids, key=lambda x: x[pk_real])
470
+ del_ids.extend([r[pk_real] for r in ids if r[pk_real] != keep_row[pk_real]])
441
471
  affected_rows = 0
442
472
  if not dry_run and del_ids:
443
473
  with self._conn_ctx() as conn:
444
474
  with conn.cursor() as cursor:
445
- for i in range(0, len(del_ids), self.batch_size):
446
- batch_ids = del_ids[i:i+self.batch_size]
447
- del_ids_str = ','.join([str(i) for i in batch_ids])
448
- delete_sql = f"DELETE FROM `{database}`.`{table}` WHERE `{pk_real}` IN ({del_ids_str})"
449
- cursor.execute(delete_sql)
450
- batch_deleted = cursor.rowcount
451
- affected_rows += batch_deleted
452
- conn.commit()
475
+ format_ids = ','.join([str(i) for i in del_ids])
476
+ del_sql = f"DELETE FROM `{database}`.`{table}` WHERE `{pk_real}` IN ({format_ids})"
477
+ cursor.execute(del_sql)
478
+ affected_rows = cursor.rowcount
479
+ conn.commit()
453
480
  logger.debug('去重完成', {"库": database, "表": table, "数据量": total_count, "重复组": dup_count, "实际删除": affected_rows, "去重方式": "Python", "数据处理": self.duplicate_keep_mode, "数据日期": date_val})
454
481
  return (dup_count, affected_rows)
455
482
  # SQL方式查找重复
456
483
  temp_table = self._make_temp_table_name(table)
457
484
  drop_temp_sql = f"DROP TABLE IF EXISTS `{database}`.`{temp_table}`"
458
485
  create_temp_where = f"WHERE `{time_col}` = '{date_val}'"
486
+ # 判断分组字段是否有“更新时间”
487
+ has_update_time = any(col == '更新时间' for col in use_columns)
488
+ if has_update_time:
489
+ keep_field = '更新时间'
490
+ keep_func = 'MAX'
491
+ else:
492
+ keep_field = pk_real
493
+ keep_func = 'MAX' if self.keep_order == 'max' else 'MIN'
494
+ keep_alias = 'keep_val'
459
495
  create_temp_sql = f"""
460
496
  CREATE TABLE `{database}`.`{temp_table}` AS
461
- SELECT MIN(`{pk_real}`) as `min_id`, {column_list}, COUNT(*) as `dup_count`
497
+ SELECT {keep_func}(`{keep_field}`) as `{keep_alias}`, {column_list}, COUNT(*) as `dup_count`
462
498
  FROM `{database}`.`{table}`
463
499
  {create_temp_where}
464
500
  GROUP BY {column_list}
@@ -481,7 +517,7 @@ class MySQLDeduplicator:
481
517
  while True:
482
518
  where_clauses = []
483
519
  if self.duplicate_keep_mode == 'keep_one':
484
- where_clauses.append(f"t.`{pk_real}` <> tmp.`min_id`")
520
+ where_clauses.append(f"t.`{keep_field}` <> tmp.`{keep_alias}`")
485
521
  if where_sql.strip():
486
522
  where_clauses.append(where_sql.strip())
487
523
  where_full = "WHERE " + " AND ".join(where_clauses) if where_clauses else ""
@@ -652,12 +688,20 @@ class MySQLDeduplicator:
652
688
  use_python_dedup: bool = True
653
689
  ) -> Tuple[int, int]:
654
690
  """
655
- 对指定表进行去重。始终按天分区(如有 date_column),否则全表。
656
-
691
+ 对指定表进行去重。
692
+
693
+ 去重行为说明:
694
+ - 若 columns 参数传入且不包含 date_column,则全表直接按 columns 去重。
695
+ - 若 columns 包含 date_column 或未指定 columns,则按天分区去重(每一天独立去重)。
696
+ - exclude_columns 中的字段始终不会参与去重分组。
697
+ - date_column 默认为 '日期',可自定义。
698
+ - dry_run 模式下仅统计重复组和待删除行数,不实际删除。
699
+ - reorder_id=True 时,去重后自动重排主键 id。
700
+
657
701
  Args:
658
702
  database (str): 数据库名。
659
703
  table (str): 表名。
660
- columns (Optional[List[str]]): 指定去重列。
704
+ columns (Optional[List[str]]): 指定去重列。若不含 date_column,则全表去重。
661
705
  dry_run (bool): 是否为模拟运行。
662
706
  reorder_id (bool): 去重后是否自动重排 id 列。
663
707
  use_python_dedup (bool): 是否用 Python 方式去重。
@@ -686,7 +730,11 @@ class MySQLDeduplicator:
686
730
  all_columns_lower = [col.lower() for col in all_columns]
687
731
  time_col = self.date_column
688
732
  time_col_lower = time_col.lower() if time_col else None
689
- has_time_col = time_col_lower in all_columns_lower if time_col_lower else False
733
+ # 如果传了columns且columns不包含date_column,则不分天,直接全表去重
734
+ if columns and (not time_col_lower or time_col_lower not in [c.lower() for c in columns]):
735
+ has_time_col = False # 全表去重
736
+ else:
737
+ has_time_col = time_col_lower in all_columns_lower if time_col_lower else False # 分天去重
690
738
  if has_time_col:
691
739
  self._ensure_index(database, table, time_col)
692
740
  all_dates = self._get_all_dates(database, table, time_col)
@@ -727,7 +775,7 @@ class MySQLDeduplicator:
727
775
  logger.warning('分区处理失败', {"库": database, "表": table, "日期": date_val, "异常": err, "func": sys._getframe().f_code.co_name})
728
776
  total_dup += dup_count
729
777
  total_del += affected_rows
730
- logger.debug('单表完成', {"库": database, "表": table, "结果[重复, 删除]": (total_dup, total_del), '日期范围': f"{start_date} - {end_date}"})
778
+ logger.info('单表完成', {"库": database, "表": table, "结果[重复, 删除]": (total_dup, total_del), '日期范围': f"{start_date} - {end_date}", "唯一列": columns})
731
779
  # 自动重排id列(仅当有实际删除时且reorder_id为True)
732
780
  if reorder_id and total_del > 0:
733
781
  try:
@@ -736,11 +784,11 @@ class MySQLDeduplicator:
736
784
  except Exception as e:
737
785
  logger.error('自动重排id列异常', {"库": database, "表": table, "异常": str(e)})
738
786
  if affected_rows > 0:
739
- logger.info('单表完成(仅显示有删除的结果)', {"库": database, "表": table, "重复组": total_dup, "实际删除": total_del})
787
+ logger.info('单表完成(仅显示有删除的结果)', {"库": database, "表": table, "重复组": total_dup, "实际删除": total_del, "唯一列": columns})
740
788
  return (total_dup, total_del)
741
789
  # 没有date_column,直接全表去重
742
790
  result = self._deduplicate_table(database, table, columns, dry_run, use_python_dedup, date_val=None)
743
- logger.debug('单表完成', {"库": database, "表": table, "结果[重复, 删除]": result, '日期范围': '全表'})
791
+ logger.info('单表完成', {"库": database, "表": table, "结果[重复, 删除]": result, '日期范围': '全表', "唯一列": columns})
744
792
  dup_count, affected_rows = result
745
793
  if reorder_id and affected_rows > 0:
746
794
  try:
@@ -749,7 +797,7 @@ class MySQLDeduplicator:
749
797
  except Exception as e:
750
798
  logger.error('自动重排id列异常', {"库": database, "表": table, "异常": str(e)})
751
799
  if affected_rows > 0:
752
- logger.info('单表完成(仅显示有删除的结果)', {"库": database, "表": table, "重复组": dup_count, "实际删除": affected_rows})
800
+ logger.info('单表完成(仅显示有删除的结果)', {"库": database, "表": table, "重复组": dup_count, "实际删除": affected_rows, "唯一列": columns})
753
801
  return result
754
802
  except Exception as e:
755
803
  logger.error('发生全局错误', {"库": database, "表": table, 'func': sys._getframe().f_code.co_name, "发生全局错误": str(e)})
@@ -1289,20 +1337,26 @@ class MySQLDeduplicator:
1289
1337
 
1290
1338
 
1291
1339
  def main():
1340
+ from mdbq.config import config
1341
+ dir_path = os.path.expanduser("~")
1342
+ my_cont = config.read_config(file_path=os.path.join(dir_path, 'spd.txt'))
1343
+ username, password, host, port = my_cont['username'], my_cont['password'], my_cont['host'], int(my_cont['port'])
1344
+ # host = 'localhost'
1345
+
1292
1346
  deduplicator = MySQLDeduplicator(
1293
- username='root',
1294
- password='pwd',
1295
- host='localhost',
1296
- port=3306,
1297
- max_workers= 3,
1347
+ username=username,
1348
+ password=password,
1349
+ host=host,
1350
+ port=port,
1351
+ max_workers= 2,
1298
1352
  batch_size=1000,
1299
1353
  skip_system_dbs=True,
1300
1354
  max_retries=3,
1301
1355
  retry_waiting_time=5,
1302
1356
  pool_size=30,
1303
- recent_month=1,
1357
+ # recent_month=1,
1304
1358
  # date_range=['2025-06-09', '2025-06-10'],
1305
- date_column='日期',
1359
+ exclude_columns=['更新时间'],
1306
1360
  # exclude_databases=['测试库4'],
1307
1361
  # exclude_tables={
1308
1362
  # '推广数据2': [
@@ -1313,16 +1367,23 @@ def main():
1313
1367
  # "商品排行_2025",
1314
1368
  # ],
1315
1369
  # },
1370
+ keep_order='MAX', # 保留重复组中指定列的最大值
1316
1371
  )
1317
1372
 
1318
1373
  # 全库去重(单线程)
1319
- deduplicator.deduplicate_all(dry_run=False, parallel=True, reorder_id=True)
1374
+ # deduplicator.deduplicate_all(dry_run=False, parallel=True, reorder_id=True)
1320
1375
 
1321
1376
  # # 指定数据库去重(多线程)
1322
1377
  # deduplicator.deduplicate_database('数据引擎2', dry_run=False, parallel=True, reorder_id=True)
1323
1378
 
1324
1379
  # # 指定表去重(使用特定列)
1325
- # deduplicator.deduplicate_table('my_db', 'my_table', columns=['name', 'data'], dry_run=False, reorder_id=True)
1380
+ deduplicator.deduplicate_table(
1381
+ '京东数据3',
1382
+ '推广数据_京准通_2024',
1383
+ columns=['日期', '店铺名称', '产品线', '触发sku_id', '跟单sku_id', 'spu_id', '花费', '展现数', '点击数'],
1384
+ dry_run=False,
1385
+ reorder_id=True,
1386
+ )
1326
1387
 
1327
1388
  # # 重排id列
1328
1389
  # deduplicator.reorder_id_column('my_db', 'my_table', 'id', dry_run=False, auto_drop_backup=True)
@@ -1331,5 +1392,5 @@ def main():
1331
1392
  deduplicator.close()
1332
1393
 
1333
1394
  if __name__ == '__main__':
1334
- main()
1395
+ # main()
1335
1396
  pass
mdbq/mysql/uploader.py CHANGED
@@ -323,7 +323,7 @@ class MySQLUploader:
323
323
  logger.error('无效的标识符', {'标识符': identifier})
324
324
  raise ValueError(f"无效的标识符: `{identifier}`")
325
325
  # 始终做特殊字符清理
326
- cleaned = re.sub(r'[^-\uFFFF\w\u4e00-\u9fff$]', '_', identifier)
326
+ cleaned = re.sub(r'[^\w\u4e00-\u9fff$]', '_', identifier)
327
327
  cleaned = re.sub(r'_+', '_', cleaned).strip('_')
328
328
  if not cleaned:
329
329
  logger.error('无法清理异常标识符', {'原始标识符': identifier})
@@ -332,6 +332,8 @@ class MySQLUploader:
332
332
  'select', 'insert', 'update', 'delete', 'from', 'where', 'and', 'or',
333
333
  'not', 'like', 'in', 'is', 'null', 'true', 'false', 'between'
334
334
  }
335
+ if len(cleaned) > 64:
336
+ cleaned = cleaned[:64]
335
337
  if cleaned.lower() in mysql_keywords:
336
338
  logger.debug('存在MySQL保留字', {'标识符': cleaned})
337
339
  return f"`{cleaned}`"
@@ -423,11 +425,11 @@ class MySQLUploader:
423
425
  # UNIQUE KEY定义
424
426
  unique_defs = []
425
427
  if unique_keys:
426
- for idx, unique_cols in enumerate(unique_keys):
428
+ for unique_cols in unique_keys:
427
429
  if not unique_cols:
428
430
  continue
429
431
  safe_unique_cols = [self._normalize_col(col) for col in unique_cols]
430
- unique_name = f"uniq_{'_'.join(safe_unique_cols)}_{idx}"
432
+ unique_name = f"uniq_{'_'.join(safe_unique_cols)}"
431
433
  unique_defs.append(f"UNIQUE KEY `{unique_name}` (`{'`,`'.join(safe_unique_cols)}`)")
432
434
  index_defs = list(set(index_defs))
433
435
  all_defs = column_defs + [primary_key_sql] + index_defs + unique_defs
@@ -651,7 +653,7 @@ class MySQLUploader:
651
653
  添加UNIQUE KEY
652
654
  """
653
655
  safe_cols = [self._normalize_col(col) for col in unique_cols]
654
- unique_name = f"uniq_{'_'.join(safe_cols)}_{int(time.time()*1000)%100000}"
656
+ unique_name = f"uniq_{'_'.join(safe_cols)}"
655
657
  sql = f'ALTER TABLE `{db_name}`.`{table_name}` ADD UNIQUE KEY `{unique_name}` ({','.join(f'`{col}`' for col in safe_cols)})'
656
658
  try:
657
659
  with self._get_connection() as conn:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mdbq
3
- Version: 3.12.2
3
+ Version: 3.12.4
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1,5 +1,5 @@
1
1
  mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
- mdbq/__version__.py,sha256=7f_XGixBIsGAP5w1W3y_kTPfLVlurDoRsv3FOOJLDIA,18
2
+ mdbq/__version__.py,sha256=V3m3JOLEsFbctTVRE9dNc1iuNQahT_FbWxcHtAoWWqc,18
3
3
  mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
4
4
  mdbq/aggregation/query_data.py,sha256=nxL8hSy8yI1QLlqnkTNHHQSxRfo-6WKL5OA-N4xLB7c,179832
5
5
  mdbq/config/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
@@ -8,10 +8,10 @@ mdbq/log/__init__.py,sha256=Mpbrav0s0ifLL7lVDAuePEi1hJKiSHhxcv1byBKDl5E,15
8
8
  mdbq/log/mylogger.py,sha256=Crw6LwVo3I3IUbzIETu8f46Quza3CTCh-qYf4edbBPo,24139
9
9
  mdbq/log/spider_logging.py,sha256=-ozWWEGm3HVv604ozs_OOvVwumjokmUPwbaodesUrPY,1664
10
10
  mdbq/mysql/__init__.py,sha256=A_DPJyAoEvTSFojiI2e94zP0FKtCkkwKP1kYUCSyQzo,11
11
- mdbq/mysql/deduplicator.py,sha256=eILGFxFtMCSR6dvCgEgsOjwlK_hEWBe2dFSgJTPHfj8,68623
11
+ mdbq/mysql/deduplicator.py,sha256=MYcNe0rwWOFS2Bqac8yGwwothlz8H--cOi6yuZf3qIs,72602
12
12
  mdbq/mysql/mysql.py,sha256=Kjpi-LL00WQUmTTOfhEBsNrmo4-4kFFJzrHbVKfqiBE,56770
13
13
  mdbq/mysql/s_query.py,sha256=dlnrVJ3-Vp1Suv9CNbPxyYSRqRJUHjOpF39tb2F-wBc,10190
14
- mdbq/mysql/uploader.py,sha256=szX6t4SObBF6fbHT2s5ixfh1-c288cigsJ66pFE02Qg,70266
14
+ mdbq/mysql/uploader.py,sha256=ekpPaJypnuwxi2v42e-khqwT_eZ5LRl1ylQP492xbkk,70271
15
15
  mdbq/other/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
16
16
  mdbq/other/download_sku_picture.py,sha256=YU8DxKMXbdeE1OOKEA848WVp62jYHw5O4tXTjUdq9H0,44832
17
17
  mdbq/other/otk.py,sha256=iclBIFbQbhlqzUbcMMoePXBpcP1eZ06ZtjnhcA_EbmE,7241
@@ -24,7 +24,7 @@ mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
24
24
  mdbq/redis/getredis.py,sha256=YHgCKO8mEsslwet33K5tGss-nrDDwPnOSlhA9iBu0jY,24078
25
25
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
26
26
  mdbq/spider/aikucun.py,sha256=cqK-JRd_DHbToC7hyo83m8o97NZkJFqmB2xBtr6aAVU,20961
27
- mdbq-3.12.2.dist-info/METADATA,sha256=1V071qDvuX5apscDGFLY_ICX6mMVBUhjzMGh_a_wd7w,364
28
- mdbq-3.12.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
29
- mdbq-3.12.2.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
30
- mdbq-3.12.2.dist-info/RECORD,,
27
+ mdbq-3.12.4.dist-info/METADATA,sha256=bptLkLuByUNJJnZ_ruWRjxeG2LOFvaqK26M3MtQkf78,364
28
+ mdbq-3.12.4.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
29
+ mdbq-3.12.4.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
30
+ mdbq-3.12.4.dist-info/RECORD,,
File without changes