|
|
@@ -262,7 +262,8 @@ class YaoShiBangSnapshot:
|
|
|
co.set_browser_path(chrome_path)
|
|
|
|
|
|
# ---------- 2. 统一使用 set_argument 设置(避免 set_headless 报错) ----------
|
|
|
- co.set_argument('--headless=new') # 启用新版无头模式(Windows/Linux 均支持)
|
|
|
+ co.set_argument('--headless') # 旧版无头(更兼容)
|
|
|
+ co.set_argument('--window-size=1920,1080')
|
|
|
co.set_argument('--no-sandbox') # Linux 必需,Windows 下被忽略,无害
|
|
|
co.set_argument('--disable-dev-shm-usage') # Linux 解决 /dev/shm 问题,Windows 无害
|
|
|
co.set_argument('--disable-gpu') # 无头模式下可减少 GPU 问题,兼容所有系统
|
|
|
@@ -277,23 +278,22 @@ class YaoShiBangSnapshot:
|
|
|
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
|
co.set_user_data_path(str(profile_dir))
|
|
|
|
|
|
- # ---------- 4. 可选:本地调试端口(跨平台) ----------
|
|
|
- # 如果想手动指定端口,可取消注释并自行实现 _get_free_port()
|
|
|
- # debug_port = self._get_free_port()
|
|
|
- # co.set_local_port(debug_port)
|
|
|
- # co.set_argument(f"--remote-debugging-port={debug_port}")
|
|
|
- # co.set_argument("--remote-debugging-address=127.0.0.1")
|
|
|
+ co.set_argument("--remote-debugging-port=0")
|
|
|
+ co.set_argument("--remote-debugging-address=127.0.0.1")
|
|
|
|
|
|
self.driver = ChromiumPage(co)
|
|
|
|
|
|
- def _solve_slider_captcha(self):
|
|
|
+ def _solve_slider_captcha(self, yidun=None):
|
|
|
"""检测并处理易盾滑块验证码,成功返回 True。"""
|
|
|
self.driver.wait.doc_loaded()
|
|
|
time.sleep(2)
|
|
|
|
|
|
- yidun = self.driver.ele("xpath://div[@class='yidun_modal']", timeout=3)
|
|
|
- if not yidun:
|
|
|
+ captcha_text = self.driver.ele("xpath://*[contains(text(),'安全验证') or contains(text(),'验证码')]", timeout=1)
|
|
|
+ if not captcha_text:
|
|
|
+ captcha_text = self.driver.ele("xpath://div[@class='yidun_modal']", timeout=2)
|
|
|
+ if not captcha_text:
|
|
|
return True
|
|
|
+ logger.warning("检测到验证码: %s", captcha_text.inner_text()[:50])
|
|
|
|
|
|
logger.info("检测到滑块验证码,开始处理")
|
|
|
jpg_bytes = yidun.get_screenshot(as_bytes="jpg")
|
|
|
@@ -439,6 +439,9 @@ class YaoShiBangSnapshot:
|
|
|
|
|
|
def _goto_detail_page(self, item_id, detail_url):
|
|
|
"""get 后 refresh 一次,让 SPA 按当前 URL 重新渲染详情。"""
|
|
|
+ if not self._solve_slider_captcha():
|
|
|
+ logger.error("验证码处理失败,跳过当前商品")
|
|
|
+ return False
|
|
|
for attempt in range(1, DETAIL_NAV_RETRIES + 1):
|
|
|
try:
|
|
|
self.driver.get(detail_url, timeout=5)
|
|
|
@@ -520,7 +523,7 @@ class YaoShiBangSnapshot:
|
|
|
time.sleep(1)
|
|
|
self._dismiss_popup_before_screenshot()
|
|
|
try:
|
|
|
- jpg_bytes = self.driver.get_screenshot(as_bytes="jpg")
|
|
|
+ jpg_bytes = self.driver.get_screenshot(as_bytes="jpg", full_page=True)
|
|
|
if not jpg_bytes:
|
|
|
logger.warning("截图为空 upload_key=%s", upload_key)
|
|
|
return ""
|
|
|
@@ -594,6 +597,17 @@ class YaoShiBangSnapshot:
|
|
|
|
|
|
def _dismiss_popup_before_screenshot(self):
|
|
|
"""截图前关闭或隐藏营销弹窗,避免遮挡。"""
|
|
|
+ self.driver.run_js("""
|
|
|
+ // 暴力清除所有高 z-index 遮罩和弹窗
|
|
|
+ document.querySelectorAll('*').forEach(el => {
|
|
|
+ const s = getComputedStyle(el);
|
|
|
+ const z = parseInt(s.zIndex) || 0;
|
|
|
+ if (z > 100 && (s.position === 'fixed' || s.position === 'absolute') &&
|
|
|
+ (el.clientWidth > 100 || el.clientHeight > 100)) {
|
|
|
+ el.remove();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ """)
|
|
|
close_locs = [
|
|
|
"xpath=//div[contains(@class,'dialog')]//i[contains(@class,'close')]",
|
|
|
"xpath=//div[contains(@class,'popup')]//i[contains(@class,'close')]",
|
|
|
@@ -601,6 +615,8 @@ class YaoShiBangSnapshot:
|
|
|
"xpath=//button[contains(@class,'close')]",
|
|
|
"xpath=//span[text()='×']",
|
|
|
"xpath=//*[contains(text(),'智能采购')]/ancestor::div[1]//*[contains(@class,'close')]",
|
|
|
+ "xpath=//*[contains(text(),'智能采购')]/ancestor::div[3]//*[contains(@class,'close')]",
|
|
|
+ "xpath=//*[contains(text(),'智能采购')]/ancestor::div[3]//button[last()]",
|
|
|
]
|
|
|
for loc in close_locs:
|
|
|
try:
|
|
|
@@ -764,6 +780,10 @@ class YaoShiBangSnapshot:
|
|
|
page_start = self.start_page
|
|
|
self.page = page_start # 兜底:避免0页时回告 total_pages=0
|
|
|
for page in range(page_start, 100):
|
|
|
+ if not self._solve_slider_captcha():
|
|
|
+ logger.error("验证码处理失败,停止任务")
|
|
|
+ self._report_exception(3, "验证码处理失败,停止当前任务")
|
|
|
+ return False
|
|
|
first_search = page == 1
|
|
|
logger.info("药师帮爬取第%s页 firstSearch=%s stateValue=%s", page, first_search,
|
|
|
self._state_value or "(空)")
|
|
|
@@ -815,6 +835,8 @@ class YaoShiBangSnapshot:
|
|
|
continue
|
|
|
|
|
|
state_val = self._extract_state_value(json_data, data_block)
|
|
|
+ logger.info("第%s页 解密后 keys: %s, stateValue=%s, has_wholesales=%s", page,
|
|
|
+ list(json_data.keys())[:20], state_val, bool(json_data.get("wholesales")))
|
|
|
if state_val:
|
|
|
self._state_value = state_val
|
|
|
|