Files
surugaya/app/utils/parse_util.py
T
2026-06-08 16:09:13 +08:00

164 lines
5.4 KiB
Python

"""骏河屋页面解析工具函数"""
import re
from urllib.parse import parse_qs, urlparse
def extract_low_price(text: str) -> int | None:
"""从价格范围文本中提取最低价格
例如 "¥4,700 ~ ¥6,200" 会返回 4700
"""
prices = re.findall(r'(\d[0-9,.]*)', text)
if prices:
low_price_str = prices[0].replace(',', '')
return int(float(low_price_str))
return None
def format_price(price_text: str) -> int | None:
"""格式化商品价格文字为整数(日元)
支持的格式:
- 单价:¥4,700 → 4700
- 范围:¥4,700 ~ ¥6,200 → 4700(取最低价)
- 円后缀:4700円 → 4700
"""
if not price_text:
return None
# 价格范围,取最低价
if "" in price_text:
return extract_low_price(price_text)
match = re.search(r"[¥¥]\s*([0-9][0-9,]*)", price_text)
if not match:
match = re.search(r"([0-9][0-9,]*)\s*円", price_text)
if not match:
return None
try:
return int(match.group(1).replace(",", ""))
except ValueError:
return None
def normalize_goods_id(goods_id: str) -> str:
"""
高效处理商品ID:如果包含字母则转小写。
Python 的 lower() 会处理整个字符串,但在全是数字的情况下开销极小。
"""
# 如果 ID 已经是全数字,直接返回原对象,避免额外的处理
if goods_id.isdigit():
return goods_id
return goods_id.lower()
def surugaya_photo_url_to_cdn_m(photo_url: str | None = None, item_id: str | None = None) -> str:
"""将骏河屋商品照片 URL 转换为 CDN URL(webp 格式)
示例:
输入: https://www.suruga-ya.jp/database/photo.php?shinaban=220035970&size=m
输出: https://cdn.suruga-ya.jp/pics_webp/boxart_m/220035970m.jpg.webp
当 photo_url 无法解析时,回退使用 item_id 拼接 CDN 地址;
两者都为空时返回空字符串。
"""
# 如果无法从 photo_url 解析出商品编号,则用 item_id 直接拼接
if not photo_url:
if item_id:
return f"https://cdn.suruga-ya.jp/pics_webp/boxart_m/{normalize_goods_id(item_id)}m.jpg.webp"
return ""
parsed = urlparse(photo_url)
qs = parse_qs(parsed.query)
shinaban = (qs.get("shinaban") or [None])[0]
if not shinaban:
# photo_url 中没有 shinaban 参数,回退到 item_id
if item_id:
return f"https://cdn.suruga-ya.jp/pics_webp/boxart_m/{normalize_goods_id(item_id)}m.jpg.webp"
return photo_url
size = (qs.get("size") or ["m"])[0] or "m"
size = str(size).strip().lower()
folder = f"boxart_{size}"
filename = f"{normalize_goods_id(shinaban)}{size}"
return f"https://cdn.suruga-ya.jp/pics_webp/{folder}/{filename}.jpg.webp"
def surugaya_photo_url_to_cdn(photo_url: str | None = None, item_id: str | None = None) -> str:
"""将骏河屋商品照片 URL 转换为 CDN URL(webp 格式)
示例:
输入: https://www.suruga-ya.jp/database/photo.php?shinaban=220035970&size=m
输出: https://cdn.suruga-ya.jp/database/pics_webp/game/220035970.jpg.webp
优先使用 item_id 拼接 CDN 地址;
两者都为空时返回空字符串。
"""
if item_id:
return f"https://cdn.suruga-ya.jp/database/pics_webp/game/{normalize_goods_id(item_id)}.jpg.webp"
if photo_url:
parsed = urlparse(photo_url)
qs = parse_qs(parsed.query)
shinaban = (qs.get("shinaban") or [None])[0]
if not shinaban:
return photo_url
return f"https://cdn.suruga-ya.jp/database/pics_webp/game/{normalize_goods_id(shinaban)}.jpg.webp"
return ""
def extract_product_id(url: str | None = None) -> str:
"""从各种商品URL或路径中精准提取商品ID"""
if not url:
return ""
# 核心逻辑:匹配 /detail/ 后面紧跟的非斜杠、非问号、非点号的连续字符
match = re.search(r"/detail/([^/?.]+)", url)
if match:
return match.group(1)
return ""
def get_shipping_fee(amount: int) -> int:
"""
根据定义的运费规则计算运费
日本境内运费统一为固定价格(部分地区除外)。
• 订单金额低于1000日元:运费440日元
• 订单金额低于1500日元:运费385日元
• 订单金额1500日元及以上:免运费
*北海道、冲绳、离岛及其他部分地区可能需额外支付运费。
货到付款需另付550日元货到付款手续费。银行转账付款需由顾客承担转账手续费。信用卡付款无需支付手续费。
*通过电商平台购买的商品,运费因店铺而异。
"""
if amount > 1500:
return 0
elif amount > 1000:
return 440
else:
return 385
def get_handle_fee(amount: int) -> int:
"""
根据定义的规则计算手续费
https://www.suruga-ya.jp/man/qa/hanbai_qa/shiharai.html#shiharai_1_2
A. 运费根据购买金额而定。
■本州、四国、九州:
购买金额低于5,000日元…240日元;
购买金额5,000日元及以上…免运费。
■北海道、冲绳:
购买金额低于5,000日元…570日元;
购买金额5,000日元至低于10,000日元…285日元;
购买金额10,000日元及以上…免
运费。偏远岛屿及其他部分地区可能需额外收费。
这里按照本州计算手续费。
"""
if amount > 5000:
return 0
else:
return 240