From 6ed23050ed23a7b2097443f31bc6111294478ad3 Mon Sep 17 00:00:00 2001 From: chengma Date: Tue, 11 Aug 2026 17:35:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=AF=86=E5=88=AB=E5=B8=A6=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E7=9A=84=E7=BA=B5=E5=90=91=E5=A5=97=E9=A4=90=E7=BB=AD?= =?UTF-8?q?=E9=A1=B5=20(#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/pdd_collect_service.py | 64 +++++++++++++++++-------- client/test/test_pdd_collect_service.py | 17 ++++--- 2 files changed, 54 insertions(+), 27 deletions(-) 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)