fix: auto-detect image link column when header varies

This commit is contained in:
Mayorz 2026-03-04 16:01:45 +08:00
parent b96e333df8
commit 0ce33a601f

View File

@ -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)