@@ -222,6 +222,17 @@ async def crawl(
222222 page = self .page
223223
224224 try :
225+
226+ try :
227+ if hasattr (page , 'frames' ) and len (page .frames ) > 1 :
228+ _ , merged_id_map = await self .crawl_all_frames (page = page , enable_highlight = highlight )
229+ return CrawlResultModel (
230+ flat_element_map = ElementMap (data = merged_id_map or {}),
231+ element_tree = {}
232+ )
233+ except Exception :
234+ pass
235+
225236 # Build JavaScript payload for element detection
226237 payload = (
227238 f"(() => {{"
@@ -394,6 +405,99 @@ def _dedupe_consecutive(seq):
394405 # Return as compact JSON array
395406 return json .dumps (items , ensure_ascii = False , separators = ("," , ":" ))
396407
408+ async def crawl_all_frames (self , page = None , enable_highlight = False ):
409+ """
410+ 爬取主页面及所有 iframe 的元素(支持跨域与嵌套),并返回统一的 id 列表与映射。
411+
412+ 返回值:
413+ (ids, id_map)
414+ - ids: List[str] 高亮编号(全局唯一)
415+ - id_map: Dict[str, Dict] 元素信息,包含 tagName/className/innerText/center_x/center_y
416+ 其中 center_x/center_y 为“主页面视口坐标”,可直接用于 page.mouse.click
417+ """
418+ if not page :
419+ page = self .page
420+
421+ import logging
422+
423+ ids = []
424+ merged_id_map : Dict [str , Dict [str , Any ]] = {}
425+
426+ # helper: frame scroll
427+ async def _get_frame_scroll (f ):
428+ try :
429+ return await f .evaluate ("() => ({x: window.scrollX, y: window.scrollY})" )
430+ except Exception :
431+ return {"x" : 0 , "y" : 0 }
432+
433+ # helper: accumulate iframe offsets to top-page viewport
434+ async def _accumulate_iframe_offsets (f ):
435+ total_left , total_top = 0 , 0
436+ cur = f
437+ while True :
438+ parent = cur .parent_frame
439+ if not parent :
440+ break
441+ try :
442+ el = await cur .frame_element ()
443+ rect = await el .evaluate ("(el) => el.getBoundingClientRect()" )
444+ total_left += rect .get ('left' , 0 ) or 0
445+ total_top += rect .get ('top' , 0 ) or 0
446+ except Exception :
447+ pass
448+ cur = parent
449+ return total_left , total_top
450+
451+ # 1) main frame first (base = 0)
452+ try :
453+ payload_main = f"window.__highlightBase__ = 0; window._highlight = { str (enable_highlight ).lower ()} ;\n { self .read_js (self .DETECTOR_JS )} "
454+ await page .evaluate (payload_main )
455+ main_tree , main_id_map = await page .evaluate ("buildElementTree()" )
456+ # 保持与单 frame 逻辑一致:直接使用 detector 返回的文档坐标(含主页面滚动)
457+ for k , v in (main_id_map or {}).items ():
458+ key = str (k )
459+ ids .append (key )
460+ merged_id_map [key ] = {kk : v .get (kk ) for kk in ('tagName' ,'className' ,'innerText' ,'center_x' ,'center_y' ) if v .get (kk ) is not None }
461+ except Exception as e :
462+ logging .warning (f"Main frame crawl failed: { e } " )
463+
464+ # 2) sub frames
465+ frames = page .frames
466+ for idx , frame in enumerate (frames ):
467+ if frame == page .main_frame :
468+ continue
469+ try :
470+ highlight_base = (idx + 1 ) * 1000
471+ payload = f"window.__highlightBase__ = { highlight_base } ; window._highlight = { str (enable_highlight ).lower ()} ;\n { self .read_js (self .DETECTOR_JS )} "
472+ await frame .evaluate (payload )
473+ iframe_tree , iframe_id_map = await frame .evaluate ("buildElementTree()" )
474+ frame_scroll = await _get_frame_scroll (frame )
475+ total_left , total_top = await _accumulate_iframe_offsets (frame )
476+ top_scroll = await _get_frame_scroll (page )
477+
478+ for k , v in (iframe_id_map or {}).items ():
479+ try :
480+ # frame document -> frame viewport
481+ vx = (v .get ('center_x' ) or 0 ) - (frame_scroll .get ('x' ) or 0 )
482+ vy = (v .get ('center_y' ) or 0 ) - (frame_scroll .get ('y' ) or 0 )
483+ # frame viewport -> top-page viewport
484+ gvx = total_left + vx
485+ gvy = total_top + vy
486+ # top-page viewport -> top-page document
487+ gx = gvx + (top_scroll .get ('x' ) or 0 )
488+ gy = gvy + (top_scroll .get ('y' ) or 0 )
489+ v ['center_x' ] = gx
490+ v ['center_y' ] = gy
491+ except Exception :
492+ pass
493+ key = str (k )
494+ ids .append (key )
495+ merged_id_map [key ] = {kk : v .get (kk ) for kk in ('tagName' ,'className' ,'innerText' ,'center_x' ,'center_y' ) if v .get (kk ) is not None }
496+ except Exception as e :
497+ logging .warning (f"Sub frame crawl failed: { e } " )
498+
499+ return ids , merged_id_map
500+
397501 # ------------------------------------------------------------------------
398502 # DOM CACHE MANAGEMENT
399503 # ------------------------------------------------------------------------
0 commit comments