fix: auto-detect image link column when header varies
This commit is contained in:
parent
b96e333df8
commit
0ce33a601f
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user