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 +1 -1
- mdbq/mysql/deduplicator.py +106 -45
- mdbq/mysql/uploader.py +6 -4
- {mdbq-3.12.2.dist-info → mdbq-3.12.4.dist-info}/METADATA +1 -1
- {mdbq-3.12.2.dist-info → mdbq-3.12.4.dist-info}/RECORD +7 -7
- {mdbq-3.12.2.dist-info → mdbq-3.12.4.dist-info}/WHEEL +0 -0
- {mdbq-3.12.2.dist-info → mdbq-3.12.4.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = '3.12.
|
1
|
+
VERSION = '3.12.4'
|
mdbq/mysql/deduplicator.py
CHANGED
@@ -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
|
-
|
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
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
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
|
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.`{
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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=
|
1294
|
-
password=
|
1295
|
-
host=
|
1296
|
-
port=
|
1297
|
-
max_workers=
|
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
|
-
|
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
|
-
|
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'[
|
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
|
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)}
|
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)}
|
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,5 +1,5 @@
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
2
|
-
mdbq/__version__.py,sha256=
|
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=
|
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=
|
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.
|
28
|
-
mdbq-3.12.
|
29
|
-
mdbq-3.12.
|
30
|
-
mdbq-3.12.
|
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
|
File without changes
|