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"}
|
SUPPORTED_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif"}
|
||||||
|
IMAGE_HINT_HEADERS = (
|
||||||
|
"图片链接",
|
||||||
|
"商品图片",
|
||||||
|
"主图链接",
|
||||||
|
"图片url",
|
||||||
|
"image_url",
|
||||||
|
"image url",
|
||||||
|
"图片",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -109,11 +118,76 @@ def resolve_link_column(sheet, header_row: int, link_column: str) -> int:
|
|||||||
if str(value).strip() == column_spec:
|
if str(value).strip() == column_spec:
|
||||||
return col_idx
|
return col_idx
|
||||||
|
|
||||||
|
guessed = guess_link_column(sheet, header_row)
|
||||||
|
if guessed is not None:
|
||||||
|
return guessed
|
||||||
|
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Sheet '{sheet.title}' cannot find link column '{link_column}' in header row {header_row}."
|
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]):
|
def choose_sheets(workbook, names: Iterable[str]):
|
||||||
if not names:
|
if not names:
|
||||||
return list(workbook.worksheets)
|
return list(workbook.worksheets)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user