
    0i&                     P    d Z ddlmZmZ ddlmZmZmZ ddlm	Z	m
Z
  G d d      Zy)u  insurance_crawler.py — 보험사 공개 데이터 크롤러 프로토타입.

Scrapling Smart Matching으로 구조 변경에 강인한 크롤링.
crawl_utils.py의 ProxyRotator/html_to_markdown/clean_html 활용.

주의사항:
- 합법적 공개 데이터(보험사 공시 페이지 등)만을 대상으로 합니다.
- 실제 크롤링 전 반드시 대상 사이트의 robots.txt를 확인하고 준수해야 합니다.
- 본 모듈은 프로토타입이며 외부 네트워크 호출은 사용처에서 관리합니다.
    )AnyOptional)ProxyRotator
clean_htmlhtml_to_markdown)Selector	Selectorsc                   Z   e Zd ZdZ	 	 ddedeee      ddfdZddedede	fd	Z
	 	 dd
e	dededeeeef      deeeee   f      f
dZ	 	 dd
e	dedeeeef      dedeeeee   f      f
dZ	 	 	 	 dd
e	dededededeeeef      fdZdedefdZdedeeef   deeeee   f      fdZy)InsuranceCrawleru  보험사 공시 페이지 데이터를 추출하는 크롤러 프로토타입.

    Scrapling의 Smart Matching(auto_save/adaptive/find_similar)을 활용하여
    웹사이트 구조 변경에도 데이터 추출을 유지한다.

    robots.txt 준수: 실제 크롤링 시 반드시 대상 사이트의 robots.txt를 확인하고
    허용된 경로만 크롤링해야 합니다.
    Nadaptive
proxy_listreturnc                 F    || _         |rt        |      | _        yd| _        y)u   InsuranceCrawler 초기화.

        Args:
            adaptive: Smart Matching 활성화 여부 (True면 auto_save/adaptive 사용)
            proxy_list: 프록시 URL 리스트 (없으면 직접 연결)
        N)r   r   proxy_rotator)selfr   r   s      0/home/jay/workspace/scripts/insurance_crawler.py__init__zInsuranceCrawler.__init__   s"     !Q[\*5Mae    htmlurlc                 2    t        ||| j                        S )u	  HTML을 Smart Matching 지원 Selector로 파싱.

        Args:
            html: 파싱할 HTML 문자열
            url: 원본 URL (Smart Matching 식별자로 활용)

        Returns:
            Selector 인스턴스 (adaptive=self.adaptive 설정)
        )r   r   )r   r   )r   r   r   s      r   parsezInsuranceCrawler.parse*   s     #>>r   pagecss_selector
identifierfieldsc                    |r|n|}|j                  || j                  | j                  |      }|sg S |zg }|D ]q  }|j                  d      j                         }	|	;|j                  d      j                         }
dj	                  d |
D              xs d}	|j                  d|	i       s |S | j                  ||      S )u  CSS 셀렉터 기반 데이터 추출. Smart Matching으로 auto_save/adaptive 적용.

        Args:
            page: parse()로 생성한 Selector
            css_selector: 항목 컨테이너 CSS 셀렉터 (예: ".product")
            identifier: Smart Matching 저장 식별자 (예: "insurance_product")
                       빈 문자열이면 css_selector를 식별자로 사용
            fields: 필드명→서브셀렉터 매핑 (예: {"name": ".name", "price": ".price"})
                   None이면 컨테이너 텍스트만 추출

        Returns:
            [{"name": "보험A", "price": "10000"}, ...] 형태의 딕셔너리 리스트
        )	auto_saver   r   N::text c              3   ^   K   | ]%  }|j                         s|j                          ' y wNstrip.0ts     r   	<genexpr>z9InsuranceCrawler.extract_with_selector.<locals>.<genexpr>`   s     'NaAGGI	'N   --text)cssr   getgetalljoinappend_extract_fields)r   r   r   r   r   identelementsresultelemtext_valtextss              r   extract_with_selectorz&InsuranceCrawler.extract_with_selector6   s    * )
l #hhmm]]	 ' 
 I>57F  288H-113# HHX.557E"xx'N5'NNVRVHvx012 M##Hf55r   reference_selector	thresholdc                 \   |j                  |      j                  }|g S |j                  |      }|gt        |      z   }|Yg }|D ]P  }	|	j                  d      j	                         }
dj                  d |
D              xs d}|j                  d|i       R |S | j                  ||      S )u  find_similar()로 반복 구조 데이터 자동 추출.

        기준 요소 하나를 찾고, 구조적으로 유사한 요소를 모두 찾아 데이터 추출.

        Args:
            page: Selector 인스턴스
            reference_selector: 기준 요소의 CSS 셀렉터 (첫 번째 매칭 요소 기준)
            fields: 필드명→서브셀렉터 매핑
            threshold: 유사도 임계값 (0.0~1.0)

        Returns:
            기준 요소 + 유사 요소에서 추출한 딕셔너리 리스트
        N)similarity_thresholdr   r    c              3   ^   K   | ]%  }|j                         s|j                          ' y wr"   r#   r%   s     r   r(   z3InsuranceCrawler.extract_similar.<locals>.<genexpr>   s     2Yqwwy17792Yr)   r*   )r+   firstfind_similarlistr-   r.   r/   r0   )r   r   r8   r   r9   ref_elementsimilar_elementsall_elementsr3   r4   r6   r5   s               r   extract_similarz InsuranceCrawler.extract_similarf   s    * +/((3E*F*L*LI '2&>&>T]&>&^ )4}t<L7M'M>57F$ 2*113*-((2Ye2Y*Y*a]avx012 M##L&99r   table_selectorheader_selectorrow_selectorcell_selectorc                    |j                  |      j                  }|g S |j                  |      }|sg S g }d}	|D ]I  }
|
j                  |      }|s|D cg c]%  }|j                  d      j                         xs d' }}|
}	 n g }|D ]  }
|	t        |
      t        |	      k(  r|
j                  |      }|s1|s%t	        t        |            D cg c]  }d| 	 }}n|}i }t        |      D ]C  \  }}|t        |      k  r||   nd| }|j                  d      j                         xs d||<   E |s|j                  |        |S c c}w c c}w )u  HTML 테이블 데이터를 딕셔너리 리스트로 추출.

        첫 행(th)을 헤더로, 나머지 행(td)을 데이터로 변환.

        Args:
            page: Selector 인스턴스
            table_selector: 테이블 CSS 셀렉터 (기본값: "table")
            header_selector: 헤더 셀 CSS 셀렉터 (기본값: "th")
            row_selector: 행 CSS 셀렉터 (기본값: "tr")
            cell_selector: 데이터 셀 CSS 셀렉터 (기본값: "td")

        Returns:
            [{"보험명": "화재보험", "보험료": "50000"}, ...] 형태의 딕셔너리 리스트
            헤더 없으면 col_0, col_1 등 자동 명명
        Nr    col_)r+   r=   r,   strrangelen	enumerater/   )r   r   rD   rE   rF   rG   tablerowsheaders
header_rowrowth_cellscellr3   td_cellsicol_headersrow_datakeys                      r   extract_tablezInsuranceCrawler.extract_table   s   . %)HH^$<$B$B=I))L1I  )-
 	Cww/HFNOd488H-1139r9OO 
	 (* 	(C%#c(c*o*Eww}-H 38X3GHaaSzHH%')H$X. ?4()C,<(<k!nD* $ 2 6 6 8 >B? h'+	(. ; P" Is   *EEc                 X    |syt        |      }t        |d      }|j                         S )u  HTML을 LLM 입력용 마크다운으로 변환.

        clean_html() → html_to_markdown() 파이프라인.

        Args:
            html: 변환할 원본 HTML 문자열

        Returns:
            LLM 입력에 적합한 마크다운 문자열
        rI   F)remove_noise)r   r   r$   )r   r   cleanedmarkdowns       r   to_llm_inputzInsuranceCrawler.to_llm_input   s1      T" )uE~~r   r2   c                     g }|D ]V  }i }|j                         D ].  \  }}|dz   }|j                  |      j                         }	|	||<   0 |j                  |       X |S )uX  내부 헬퍼: elements에서 fields 매핑에 따라 데이터 추출.

        TextHandler 체이닝: css(selector + '::text').get()

        Args:
            elements: Selectors 컬렉션 또는 Selector 리스트
            fields: 필드명→서브셀렉터 매핑

        Returns:
            추출된 딕셔너리 리스트
        r   )itemsr+   r,   r/   )
r   r2   r   r3   r4   item
field_namesub_selectortext_selectorvalues
             r   r0   z InsuranceCrawler._extract_fields   sw      24 	 D-/D,2LLN )(
L ,x 7'+xx'>'B'B'D#(Z 	)
 MM$	  r   )TN)rI   )rI   N)Ng?)rO   thtrtd)__name__
__module____qualname____doc__boolr   r?   rK   r   r   r   dictr7   floatrC   r[   r`   r   r0    r   r   r   r      s    *.ff T#Y'f 
	f
?# 
?C 
? 
?  +/.6.6 .6 	.6
 c3h(.6 
d3%&	'.6h ,0):):  ): c3h(	):
 ): 
d3%&	'):\ &# !EE E 	E
 E E 
d38n	EN     . S#X 
d3%&	'	r   r   N)rn   typingr   r   crawl_utilsr   r   r   scrapling.parserr   r	   r   rr   r   r   <module>rv      s$   	 ! B B 0x xr   