perf: 优化颜色采集等待时间 (#44)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import math
|
||||
import re
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
@@ -638,6 +639,10 @@ class PddCollectService:
|
||||
max_goods_page_swipes: int = 12,
|
||||
max_spec_swipes: int = 12,
|
||||
max_sku_count: int = 200,
|
||||
color_poll_interval: float = 0.1,
|
||||
color_selection_timeout: float = 0.6,
|
||||
color_price_timeout: float = 1.2,
|
||||
horizontal_swipe_settle_interval: float = 0.1,
|
||||
artifact_directory: Optional[Path] = None,
|
||||
) -> None:
|
||||
self._device_service = device_service
|
||||
@@ -654,6 +659,21 @@ class PddCollectService:
|
||||
self._max_goods_page_swipes = max_goods_page_swipes
|
||||
self._max_spec_swipes = max_spec_swipes
|
||||
self._max_sku_count = max_sku_count
|
||||
timing_values = {
|
||||
"颜色轮询间隔": color_poll_interval,
|
||||
"颜色选中超时": color_selection_timeout,
|
||||
"颜色价格超时": color_price_timeout,
|
||||
"水平滑动稳定间隔": horizontal_swipe_settle_interval,
|
||||
}
|
||||
for name, value in timing_values.items():
|
||||
if value <= 0:
|
||||
raise ValueError(f"{name}必须大于 0 秒")
|
||||
self._color_poll_interval = color_poll_interval
|
||||
self._color_selection_timeout = color_selection_timeout
|
||||
self._color_price_timeout = color_price_timeout
|
||||
self._horizontal_swipe_settle_interval = (
|
||||
horizontal_swipe_settle_interval
|
||||
)
|
||||
self._artifact_directory = artifact_directory
|
||||
self._last_goods_xml: Optional[str] = None
|
||||
self._goods_screens_checked = 0
|
||||
@@ -953,13 +973,15 @@ class PddCollectService:
|
||||
current_row_colors: list[DimensionValue] = []
|
||||
stable_edge_reads = 0
|
||||
previous_signature: Optional[tuple[tuple[str, Bounds], ...]] = None
|
||||
pending_xml: Optional[str] = None
|
||||
|
||||
for swipe_count in range(self._max_spec_swipes + 1):
|
||||
self._check_cancelled()
|
||||
# 每次只处理一个节点;点击可能让列表自动移动,下一项必须
|
||||
# 从最新 XML 重新计算,不能继续使用点击前的旧坐标。
|
||||
while True:
|
||||
xml_data = self._dump_hierarchy(device)
|
||||
xml_data = pending_xml or self._dump_hierarchy(device)
|
||||
pending_xml = None
|
||||
rows = self._visible_color_rows(xml_data)
|
||||
if row_index >= len(rows):
|
||||
raise PddCollectError(
|
||||
@@ -998,7 +1020,7 @@ class PddCollectService:
|
||||
if stable_edge_reads >= 2 or swipe_count >= self._max_spec_swipes:
|
||||
break
|
||||
|
||||
root = _parse_xml(self._dump_hierarchy(device))
|
||||
root = _parse_xml(xml_data)
|
||||
region = self._horizontal_region(root)
|
||||
if region is None:
|
||||
break
|
||||
@@ -1008,7 +1030,9 @@ class PddCollectService:
|
||||
horizontal=True,
|
||||
reverse=not move_right,
|
||||
)
|
||||
self._sleep(0.35)
|
||||
pending_xml = self._wait_for_horizontal_change(
|
||||
device, signature
|
||||
)
|
||||
|
||||
# 操作采用蛇形以减少无效滑动;输出仍恢复成页面自然的
|
||||
# “每行从左到右”顺序,方便 Admin 下拉框稳定展示。
|
||||
@@ -1030,13 +1054,10 @@ class PddCollectService:
|
||||
|
||||
previous_signature: Optional[tuple[tuple[str, Bounds], ...]] = None
|
||||
stable_edge_reads = 0
|
||||
xml_data = self._dump_hierarchy(device)
|
||||
for _ in range(self._max_spec_swipes):
|
||||
self._check_cancelled()
|
||||
xml_data = self._dump_hierarchy(device)
|
||||
rows = self._visible_color_rows(xml_data)
|
||||
signature = tuple(
|
||||
(item.text, item.bounds) for row in rows for item in row
|
||||
)
|
||||
signature = self._color_view_signature(xml_data)
|
||||
if signature == previous_signature:
|
||||
stable_edge_reads += 1
|
||||
else:
|
||||
@@ -1052,7 +1073,7 @@ class PddCollectService:
|
||||
self._swipe_region(
|
||||
device, region, horizontal=True, reverse=True
|
||||
)
|
||||
self._sleep(0.35)
|
||||
xml_data = self._wait_for_horizontal_change(device, signature)
|
||||
|
||||
def _click_and_sample_color(
|
||||
self, device: Any, target: str
|
||||
@@ -1080,11 +1101,20 @@ class PddCollectService:
|
||||
(bounds[1] + bounds[3]) // 2,
|
||||
)
|
||||
|
||||
selection_deadline = self._monotonic() + self._color_selection_timeout
|
||||
selection_sleep_limit = math.ceil(
|
||||
self._color_selection_timeout / self._color_poll_interval
|
||||
)
|
||||
selection_sleeps = 0
|
||||
price_deadline: Optional[float] = None
|
||||
price_sleep_limit = math.ceil(
|
||||
self._color_price_timeout / self._color_poll_interval
|
||||
)
|
||||
price_sleeps = 0
|
||||
previous_price: Optional[tuple[int, Optional[str], Optional[int]]] = None
|
||||
stable_price_reads = 0
|
||||
for _ in range(8):
|
||||
while True:
|
||||
self._check_cancelled()
|
||||
self._sleep(0.2)
|
||||
latest_xml = self._dump_hierarchy(device)
|
||||
latest_root = _parse_xml(latest_xml)
|
||||
snapshot = parse_spec_panel(latest_xml)
|
||||
@@ -1097,28 +1127,74 @@ class PddCollectService:
|
||||
# 只能以完整可见可点击节点未报错的点击作为降级证据。
|
||||
selection_confirmed = selected or not selection_exposed
|
||||
|
||||
if snapshot.price_cent is None or not selection_confirmed:
|
||||
now = self._monotonic()
|
||||
if not selection_confirmed:
|
||||
stable_price_reads = 0
|
||||
previous_price = None
|
||||
continue
|
||||
current_price = (
|
||||
snapshot.price_cent,
|
||||
snapshot.raw_price,
|
||||
snapshot.list_price_cent,
|
||||
)
|
||||
if current_price == previous_price:
|
||||
stable_price_reads += 1
|
||||
else:
|
||||
stable_price_reads = 1
|
||||
previous_price = current_price
|
||||
if stable_price_reads >= 2:
|
||||
return ColorPriceSample(
|
||||
snapshot.price_cent,
|
||||
snapshot.raw_price,
|
||||
snapshot.list_price_cent,
|
||||
selection_timed_out = (
|
||||
now >= selection_deadline
|
||||
or selection_sleeps >= selection_sleep_limit
|
||||
)
|
||||
if selection_timed_out:
|
||||
return ColorPriceSample(None, None, None)
|
||||
else:
|
||||
if price_deadline is None:
|
||||
price_deadline = now + self._color_price_timeout
|
||||
if snapshot.price_cent is None:
|
||||
stable_price_reads = 0
|
||||
previous_price = None
|
||||
else:
|
||||
current_price = (
|
||||
snapshot.price_cent,
|
||||
snapshot.raw_price,
|
||||
snapshot.list_price_cent,
|
||||
)
|
||||
if current_price == previous_price:
|
||||
stable_price_reads += 1
|
||||
else:
|
||||
stable_price_reads = 1
|
||||
previous_price = current_price
|
||||
if stable_price_reads >= 2:
|
||||
return ColorPriceSample(
|
||||
snapshot.price_cent,
|
||||
snapshot.raw_price,
|
||||
snapshot.list_price_cent,
|
||||
)
|
||||
if now >= price_deadline or price_sleeps >= price_sleep_limit:
|
||||
return ColorPriceSample(None, None, None)
|
||||
|
||||
return ColorPriceSample(None, None, None)
|
||||
self._sleep(self._color_poll_interval)
|
||||
if selection_confirmed:
|
||||
price_sleeps += 1
|
||||
else:
|
||||
selection_sleeps += 1
|
||||
|
||||
def _color_view_signature(
|
||||
self, xml_data: str | bytes
|
||||
) -> tuple[tuple[str, Bounds], ...]:
|
||||
"""返回颜色视口签名,用于判断水平滑动是否已经更新页面。"""
|
||||
|
||||
return tuple(
|
||||
(item.text, item.bounds)
|
||||
for row in self._visible_color_rows(xml_data)
|
||||
for item in row
|
||||
)
|
||||
|
||||
def _wait_for_horizontal_change(
|
||||
self,
|
||||
device: Any,
|
||||
previous_signature: tuple[tuple[str, Bounds], ...],
|
||||
) -> str:
|
||||
"""水平滑动后短暂等待;首次未变化时只补等一次。"""
|
||||
|
||||
latest_xml = ""
|
||||
for _ in range(2):
|
||||
self._check_cancelled()
|
||||
self._sleep(self._horizontal_swipe_settle_interval)
|
||||
latest_xml = self._dump_hierarchy(device)
|
||||
if self._color_view_signature(latest_xml) != previous_signature:
|
||||
break
|
||||
return latest_xml
|
||||
|
||||
def _collect_size_dimension(self, device: Any) -> Optional[SpecDimension]:
|
||||
"""颜色采价完成后只滚动并收集尺码文字,不点击尺码。"""
|
||||
|
||||
Reference in New Issue
Block a user