diff --git a/client/src/pdd_collect_service.py b/client/src/pdd_collect_service.py
index 90f700b..2177221 100644
--- a/client/src/pdd_collect_service.py
+++ b/client/src/pdd_collect_service.py
@@ -1740,9 +1740,12 @@ class PddCollectService:
size_name = dimension.name
size_found = True
for value in dimension.values:
- if self._is_second_dimension_action(value.text):
+ option_text = self._second_dimension_option_text(value.text)
+ if option_text is None:
continue
- sizes[value.text] = sizes.get(value.text, False) or value.available
+ sizes[option_text] = (
+ sizes.get(option_text, False) or value.available
+ )
if size_found:
break
@@ -1827,9 +1830,10 @@ class PddCollectService:
)
size_name = size_dimension.name
for value in size_dimension.values:
- if self._is_second_dimension_action(value.text):
+ option_text = self._second_dimension_option_text(value.text)
+ if option_text is None:
continue
- sizes[value.text] = sizes.get(value.text, False) or value.available
+ sizes[option_text] = sizes.get(option_text, False) or value.available
signature = self._second_dimension_signature(xml_data, size_dimension)
stable_edge_reads = (
@@ -1928,14 +1932,24 @@ class PddCollectService:
"""返回已确认的第二规格签名,不要求当前屏仍显示规格标题。"""
root = _parse_xml(xml_data)
+ targets = {
+ normalized
+ for value in dimension.values
+ if (normalized := self._second_dimension_option_text(value.text))
+ is not None
+ }
result = []
- for value in dimension.values:
- node = self._find_option_node(root, value.text)
- if node is None:
+ for node in root.iter("node"):
+ if node.get("clickable") != "true":
+ continue
+ text = self._second_dimension_option_text(
+ _preferred_or_descendant_label(node).strip()
+ )
+ if text not in targets:
continue
bounds = _parse_bounds(node.get("bounds", ""))
if bounds is not None:
- result.append((value.text, bounds))
+ result.append((text, bounds))
return tuple(result)
def _build_second_dimension_context(
@@ -2009,13 +2023,11 @@ class PddCollectService:
continue
text = _preferred_or_descendant_label(node).strip()
bounds = _parse_bounds(node.get("bounds", ""))
+ option_text = self._second_dimension_option_text(text)
if (
- not text
- or text in seen
+ option_text is None
+ or option_text in seen
or bounds is None
- or self._is_second_dimension_action(text)
- or bounds[2] - bounds[0]
- >= (container_bounds[2] - container_bounds[0]) * 0.85
):
continue
if text.endswith(("…", "...")):
@@ -2023,8 +2035,8 @@ class PddCollectService:
"PDD_DATA_SKU_NAME_TRUNCATED",
f"规格名称被截断,无法安全采集:{text}",
)
- seen.add(text)
- values.append(DimensionValue(text, _is_available(node)))
+ seen.add(option_text)
+ values.append(DimensionValue(option_text, _is_available(node)))
if not values:
return None
return SpecDimension("size", context.name, tuple(values))
@@ -2078,13 +2090,11 @@ class PddCollectService:
)
@staticmethod
- def _is_second_dimension_action(text: str) -> bool:
- """排除规格面板中容易和选项结构相同的操作按钮。"""
+ def _second_dimension_option_text(text: str) -> Optional[str]:
+ """返回去掉尾部价格的规格文字;操作项或纯价格返回 ``None``。"""
compact = text.replace(" ", "")
- if "¥" in compact or "¥" in compact:
- return True
- return compact.startswith(("已选", "请选择")) or any(
+ if not compact or compact.startswith(("已选", "请选择")) or any(
marker in compact
for marker in (
"增加数量",
@@ -2097,7 +2107,19 @@ class PddCollectService:
"确认购买",
"关闭",
)
- )
+ ):
+ return None
+ if re.fullmatch(
+ r"(?:券后|到手价|拼单价|单买价|价格)?[¥¥]\d+(?:\.\d{1,2})?",
+ compact,
+ ):
+ return None
+ normalized = re.sub(
+ r"\s*[¥¥]\s*\d+(?:\.\d{1,2})?\s*$",
+ "",
+ text,
+ ).strip()
+ return normalized or None
def _second_dimension_region(
self,
diff --git a/client/test/test_pdd_collect_service.py b/client/test/test_pdd_collect_service.py
index 2960c29..27b477f 100644
--- a/client/test/test_pdd_collect_service.py
+++ b/client/test/test_pdd_collect_service.py
@@ -512,7 +512,7 @@ class HorizontalPackageDevice:
class VerticalLongPackageDevice:
"""模拟纵向长套餐:滑到第二屏后“套餐”标题不再出现在控件树。"""
- packages = tuple(f"套餐{i}" for i in range(1, 11))
+ packages = tuple(f"套餐{i}" for i in range(1, 17))
def __init__(self):
self.page = 0
@@ -528,14 +528,15 @@ class VerticalLongPackageDevice:
-
+
"""
options = []
for index, text in enumerate(visible):
top = 1080 + index * 210
+ right = 500 if self.page == 0 else 1015
options.append(
- f''
+ f''
)
return f"""
@@ -544,6 +545,8 @@ class VerticalLongPackageDevice:
{headings}
{''.join(options)}
+
y2:
- self.page = min(self.page + 1, 2)
+ self.page = min(self.page + 1, 4)
else:
self.page = max(self.page - 1, 0)
@@ -1323,13 +1326,15 @@ class PddCollectParserTest(unittest.TestCase):
result = service._collect_size_dimension(device)
self.assertIsNotNone(result)
- self.assertEqual(result.name, "套餐(10)")
+ self.assertEqual(result.name, "套餐(16)")
self.assertEqual(
[value.text for value in result.values],
list(device.packages),
)
self.assertNotIn("增加数量", [value.text for value in result.values])
self.assertNotIn("提交订单", [value.text for value in result.values])
+ self.assertNotIn("券后", [value.text for value in result.values])
+ self.assertTrue(all("¥" not in value.text for value in result.values))
self.assertEqual(device.clicks, [])
self.assertTrue(
all(abs(y2 - y1) > abs(x2 - x1) for x1, y1, x2, y2, _ in device.swipes)