From 0ce33a601ff7b1f1dd247687dffff3cccd0ad4b0 Mon Sep 17 00:00:00 2001 From: Mayorz Date: Wed, 4 Mar 2026 16:01:45 +0800 Subject: [PATCH] fix: auto-detect image link column when header varies --- .../scripts/convert_excel_image_links.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py b/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py index 9194ded..2cf9b6e 100755 --- a/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py +++ b/remote_sync/skills/dingtalk-excel-image-return/scripts/convert_excel_image_links.py @@ -27,6 +27,15 @@ CONTENT_TYPE_TO_EXT = { } SUPPORTED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif"} +IMAGE_HINT_HEADERS = ( + "图片链接", + "商品图片", + "主图链接", + "图片url", + "image_url", + "image url", + "图片", +) @dataclass @@ -109,11 +118,76 @@ def resolve_link_column(sheet, header_row: int, link_column: str) -> int: if str(value).strip() == column_spec: return col_idx + guessed = guess_link_column(sheet, header_row) + if guessed is not None: + return guessed + raise ValueError( f"Sheet '{sheet.title}' cannot find link column '{link_column}' in header row {header_row}." ) +def looks_like_http_url(value: str) -> bool: + return str(value or "").lower().startswith(("http://", "https://")) + + +def looks_like_image_url(url: str) -> bool: + raw = str(url or "").strip().lower() + if not looks_like_http_url(raw): + return False + parsed = urllib.parse.urlparse(raw) + path = parsed.path or "" + host = parsed.hostname or "" + if any(path.endswith(ext) for ext in SUPPORTED_EXTENSIONS): + return True + if host.startswith("img.") or "image" in host: + return True + if any(token in path for token in ("/img", "/image", "/upload")): + return True + return False + + +def guess_link_column(sheet, header_row: int) -> int | None: + max_scan_rows = min(sheet.max_row, header_row + 30) + best_col = None + best_score = -1 + + for col_idx in range(1, sheet.max_column + 1): + header_val = str(sheet.cell(row=header_row, column=col_idx).value or "").strip().lower() + score = 0 + if header_val: + if header_val in IMAGE_HINT_HEADERS: + score += 40 + if "图片" in header_val: + score += 20 + if "链接" in header_val or "url" in header_val: + score += 15 + + http_count = 0 + image_like_count = 0 + for row in range(header_row + 1, max_scan_rows + 1): + cell_value = sheet.cell(row=row, column=col_idx).value + if cell_value is None: + continue + text = str(cell_value).strip() + if not text: + continue + if looks_like_http_url(text): + http_count += 1 + if looks_like_image_url(text): + image_like_count += 1 + + score += http_count * 3 + image_like_count * 6 + if image_like_count == 0 and http_count == 0 and score < 20: + continue + + if score > best_score: + best_score = score + best_col = col_idx + + return best_col + + def choose_sheets(workbook, names: Iterable[str]): if not names: return list(workbook.worksheets)