Ë
    \þÔi).  ã                  óÄ   — d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	Z	ddl
mZ ddlmZmZ d	d
lmZmZ  ej$                  e«      ZdZ G d„ de«      Z G d„ de«      Zy)u•  Scraper LinkedIn przez Apify.

Apify uruchamia aktora typu "linkedin search/company scraper" â€” zwraca listÄ™
kancelarii prawnych z LinkedIn. DomyÅ›lnie uÅ¼ywamy aktora
`curious_coder/linkedin-search-scraper`, ale moÅ¼na podmieniÄ‡ go zmiennÄ…
Å›rodowiskowÄ… `APIFY_LINKEDIN_ACTOR` lub konstruktorem.

KaÅ¼dy aktor LinkedIn ma wÅ‚asny schemat inputu â€” dostosuj `_build_input()`
jeÅ›li zmienisz aktora.
é    )ÚannotationsN)ÚIterator)Úurlparseé   )Úconfig)Úis_billing_errorÚnotify_billing_erroré   )ÚLeadÚScraperSourcezhttps://api.apify.com/v2c                  ó   — e Zd Zy)Ú
ApifyErrorN)Ú__name__Ú
__module__Ú__qualname__© ó    úA/var/www/html/scraper-prawny.klono.ai/scraper/sources/linkedin.pyr   r      s   „ Ør   r   c                  óÌ   ‡ — e Zd ZdZ	 	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 dˆ fd„Zdd„Zddd„Zedd„«       Zedd„«       Z	e
dd„«       Zddd„Zedd	„«       Zdd
„Zdd„Zdd„Zdd„Zˆ xZS )ÚLinkedinScraperÚlinkedinc                ó$  •— t         ‰| �  |¬«       |xs t        j                  | _        |xs t        j
                  | _        |xs t        j                  | _        |r	|g| _	        n	g d¢| _	        | j                  d   | _
        |xs dg| _        y )N)Úlimit)zlaw firm Polandzfamily law Polandzkancelaria prawnazdivorce lawyer Polandr   ÚPoland)ÚsuperÚ__init__r   ÚAPIFY_LINKEDIN_ACTORÚactorÚAPIFY_API_KEYÚapi_keyÚAPIFY_LINKEDIN_MAX_ITEMSÚ	max_itemsÚsearch_queriesÚsearch_queryÚ	locations)Úselfr   r   r    r"   r$   r%   Ú	__class__s          €r   r   zLinkedinScraper.__init__%   s‡   ø€ ô 	‰Ñ˜uÐÔ%ØÒ9œf×9Ñ9ˆŒ
ØÒ6¤&×"6Ñ"6ˆŒØ"ÒE¤f×&EÑ&EˆŒñ Ø#/ .ˆDÕò#ˆDÔð !×/Ñ/°Ñ2ˆÔØ"Ò0 x jˆ�r   c                ó:   — | j                   j                  dd«      S )Nú/ú~)r   Úreplace)r&   s    r   Ú_actor_pathzLinkedinScraper._actor_pathA   s   € à�z‰z×!Ñ! # sÓ+Ð+r   c                óB   — |xs | j                   dg| j                  ddœS )uÓ   Input dla aktora harvestapi/linkedin-company-search.

        Twardo wymuszamy locations=["Poland"] â€” Apify i tak czasem zwraca firmy
        spoza filtra, wiÄ™c dodatkowo post-filtrujemy w `fetch()`.
        r   Úfull)ÚsearchQueryr%   ÚmaxItemsÚscraperMode)r$   r"   )r&   Úquerys     r   Ú_build_inputzLinkedinScraper._build_inputE   s+   € ð !Ò5 D×$5Ñ$5Ø"˜ØŸ™Ø!ñ	
ð 	
r   c                óè  ‡— g Šˆfd„}dD ][  }| j                  |«      }t        |t        «      r	 ||«       Œ-t        |t        «      sŒ>dD ]  } ||j                  |«      «       Œ Œ] | j                  d«      xs g D ]q  }t        |t        «      sŒdD ]  } ||j                  |«      «       Œ |j                  d«      }t        |t        «      sŒTdD ]  } ||j                  |«      «       Œ Œs ‰S )zDZbiera wszystkie pola country/location/headquarters z rekordu Apify.c                óˆ   •— t        | t        «      r1| j                  «       r ‰j                  | j                  «       «       y y y ©N)Ú
isinstanceÚstrÚstripÚappend)ÚvÚouts    €r   Úpushz.LinkedinScraper._country_strings.<locals>.pushX   s/   ø€ Ü˜!œSÔ! a§g¡g¤iØ—
‘
˜1Ÿ7™7›9Õ%ð '0Ð!r   )ÚcountryÚcountryCodeÚheadquartersÚlocationÚaddressCountry)r>   r?   ÚnameÚcityr%   )r>   r?   rD   Úline1Úline2Úparsed)r>   r?   rD   )Úgetr7   r8   Údict)Úitemr=   Úkeyr;   ÚkÚlocrG   r<   s          @r   Ú_country_stringsz LinkedinScraper._country_stringsS   sð   ø€ ð ˆô	&ð \ò 	#ˆCØ—‘˜“ˆAÜ˜!œSÔ!Ù�Q•Ü˜AœtÕ$ØCò #�AÙ˜Ÿ™˜q›•Nñ#ð	#ð —8‘8˜KÓ(Ò.¨Bò 	(ˆCÜ˜c¤4Ô(ØØIò !�Ù�S—W‘W˜Q“ZÕ ð!à—W‘W˜XÓ&ˆFÜ˜&¤$Õ'Ø;ò (�AÙ˜Ÿ™ A›Õ'ñ(ð	(ð ˆ
r   c                óH   — t        t        j                  d| xs d«      «      S )Nz[\u0400-\u04FF]Ú )ÚboolÚreÚsearch)Útexts    r   Ú_has_cyrilliczLinkedinScraper._has_cyrillicp   s   € ä”B—I‘IÐ0°$²*¸"Ó=Ó>Ð>r   c                ó  — | j                  |«      }|rdj                  |«      nd}| j                  |«      rd|dz   fS |j                  «       }d|v sd|v rd|fS |D ])  }|j	                  «       j                  «       dk(  sŒ%d|fc S  d|fS )	uÐ   Zwraca (czy_w_PL, opis_lokalizacji_do_logu).

        Twarde odrzucenie jeÅ›li ktÃ³rekolwiek pole lokalizacji zawiera cyrylicÄ™
        â€” to jednoznaczny sygnaÅ‚ firmy spoza Polski (RU/UA/BG/itd.).
        z | z(brak danych lokalizacji)Fz [cyrylica]ÚpolandÚpolskaTÚPL)rN   ÚjoinrU   Úlowerr9   Úupper)ÚclsrJ   ÚstringsÚjoinedr[   Úss         r   Ú_is_in_polandzLinkedinScraper._is_in_polandt   s£   € ð ×&Ñ& tÓ,ˆÙ(/�—‘˜GÔ$Ð5PˆØ×Ñ˜VÔ$Ø˜& =Ñ0Ð0Ð0Ø—‘“ˆØ�uÑ ¨EÑ 1Ø˜�<ÐØò 	$ˆAØ�w‰w‹y�‰Ó  DÓ(Ø˜V�|Ò#ð	$ð �fˆ}Ðr   c                óÌ  — t         › d| j                  «       › d�}| j                  t        j                  dœ}| j                  |«      }t        j                  d| j                  |d   | j                  «       	 t        j                  |||t        j                  dz   ¬«      }|j                  d
k\  rYd|j                  › d|j                  d	d › �}t!        |j                  |j                  «      rt#        dd|› �«       t        |«      ‚	 |j%                  «       S # t        j                  $ r}t        d|› �«      |‚d	}~ww xY w# t&        $ r}t        d|› �«      |‚d	}~ww xY w)z;Synchronicznie uruchamia aktora i zwraca elementy datasetu.z/acts/z/run-sync-get-dataset-items)ÚtokenÚtimeoutz8[linkedin] Uruchamiam aktora %s (query=%r, max_items=%d)r/   é   )ÚparamsÚjsonrd   zNetwork error: Ni�  zHTTP z: iô  r   zApify zInvalid JSON from Apify: )Ú
APIFY_BASEr,   r    r   ÚAPIFY_RUN_TIMEOUTr3   ÚlogÚinfor   r"   ÚrequestsÚpostÚRequestExceptionr   Ústatus_coderT   r   r	   rg   Ú
ValueError)r&   r2   Úurlrf   ÚpayloadÚrÚeÚerrs           r   Ú
_run_actorzLinkedinScraper._run_actor‡   sL  € ä�˜F 4×#3Ñ#3Ó#5Ð"6Ð6QÐRˆØŸ<™<´F×4LÑ4LÑMˆØ×#Ñ# EÓ*ˆÜ�‰ØFØ�J‰JØ�MÑ"Ø�N‰Nô		
ð	;Ü—‘˜c¨&°wÌ×H`ÑH`ÐceÑHeÔfˆAð �=‰=˜CÒØ˜!Ÿ-™-˜¨¨1¯6©6°$°3¨<¨.Ð9ˆCÜ §¡¨¯©Ô6Ü$ Z°6¸#¸°Ô@Ü˜S“/Ð!ð	EØ—6‘6“8ˆOøô ×(Ñ(ò 	;Ü˜¨q¨cÐ2Ó3¸Ð:ûð	;ûô ò 	EÜÐ8¸¸Ð<Ó=À1ÐDûð	Eús0   Á9*D ÄE ÄEÄ/D>Ä>EÅ	E#ÅEÅE#c                óÔ   — | sy t        | t        «      r(| j                  d«      xs | j                  d«      xs d} t        | t        «      sy t	        j
                  dd| «      }|xs d S )NÚnumberÚphonerP   z[\s\-()])r7   rI   rH   r8   rR   Úsub)ÚvalueÚcleaneds     r   Ú_norm_phonezLinkedinScraper._norm_phone¡   s\   € áØä�eœTÔ"Ø—I‘I˜hÓ'ÒC¨5¯9©9°WÓ+=ÒCÀˆEÜ˜%¤Ô%ØÜ—&‘&˜ b¨%Ó0ˆØŠ˜$Ðr   c                ó–  — |j                  d«      xs$ |j                  d«      xs |j                  d«      }|sy |j                  d«      xs |j                  d«      }|j                  d«      xs |j                  d«      xs d}| j                  |j                  d	«      «      }|s| j                  |«      }|j                  d
«      xs |j                  d«      }|s| j                  |«      }d x}x}}	|j                  d«      xs g }
t	        d„ |
D «       d «      }|s|
rt        |
d   t        «      r|
d   }|r‚|j                  d«      xs$ |j                  d«      xs i j                  d«      }|j                  d«      }|j                  d«      r|› d|d   › �j                  «       }|j                  d«      }	|j                  d«      xs g }d }|r't        |d   t        «      r|d   j                  d«      }|rd|› �nd}t        || j                  ||||	|||d|j                  d«      xs |j                  d«      i¬«
      S )NrC   ÚcompanyNameÚtitleÚwebsiteÚ
websiteUrlÚdescriptionÚtaglinerP   ry   ÚemailÚcompanyEmailr%   c              3  óf   K  — | ])  }t        |t        «      sŒ|j                  d «      sŒ&|–— Œ+ y­w)ÚheadquarterN)r7   rI   rH   )Ú.0Úls     r   ú	<genexpr>z+LinkedinScraper._to_lead.<locals>.<genexpr>Â   s$   è ø€ ÒX˜¬:°a¼Õ+>À1Ç5Á5ÈÕCW”1ÑXùs   ‚1˜1ª1r   rD   rG   rE   rF   ú Ú
postalCodeÚ
industrieszLinkedIn / ÚLinkedInÚlinkedin_urlÚlinkedinUrlrq   )
rC   ÚsourceÚnameMorerD   Ústreetr�   r…   ry   r�   Úextra)
rH   r}   Úextract_phoneÚextract_emailÚnextr7   rI   r9   r   rC   )r&   rJ   rC   r�   rƒ   ry   r…   rD   r”   Úpostalr%   ÚhqrŽ   ÚindustryÚmores                  r   Ú_to_leadzLinkedinScraper._to_lead­   s)  € Ø�x‰x˜ÓÒO 4§8¡8¨MÓ#:ÒO¸d¿h¹hÀwÓ>OˆÙØà—(‘(˜9Ó%Ò?¨¯©°,Ó)?ˆØ—h‘h˜}Ó-ÒJ°·±¸)Ó1DÒJÈˆð × Ñ  §¡¨'Ó!2Ó3ˆÙØ×&Ñ& {Ó3ˆEð —‘˜Ó!Ò= T§X¡X¨nÓ%=ˆÙØ×&Ñ& {Ó3ˆEð "&Ð%ˆÐ%ˆv˜Ø—H‘H˜[Ó)Ò/¨Rˆ	ÜÑX˜iÔXÐZ^Ó_ˆÙ‘i¤J¨y¸©|¼TÔ$BØ˜1‘ˆBÙØ—6‘6˜&“>ÒI b§f¡f¨XÓ&6Ò&<¸"×%AÑ%AÀ&Ó%IˆDØ—V‘V˜G“_ˆFØ�v‰v�gŒØ"˜8 1 R¨¡[ MÐ2×8Ñ8Ó:�Ø—V‘V˜LÓ)ˆFð —X‘X˜lÓ+Ò1¨rˆ
ØˆÙœ* Z°¡]´DÔ9Ø! !‘}×(Ñ(¨Ó0ˆHÙ+3�˜X˜JÑ'¸ˆäØØ—9‘9ØØØØØØØØ! 4§8¡8¨MÓ#:Ò#M¸d¿h¹hÀu»oÐNô
ð 	
r   c                ó`   — | j                  |«      }|sy | j                  |j                  «      S r6   )rH   r—   rT   )r&   rq   rs   s      r   Ú_extract_email_from_urlz'LinkedinScraper._extract_email_from_urlá   s*   € Ø�H‰H�S‹MˆÙØØ×!Ñ! !§&¡&Ó)Ð)r   c                ó
  — |sy|j                  d«      sd|z   }	 t        |«      }|j                  sy|j                  › d|j                  › �}||› d�|› d�fD ]  }| j                  |«      }|sŒ|c S  y# t        $ r Y yw xY w)uE   Pobiera email ze strony www firmy. Sprawdza teÅ¼ /kontakt i /contact.N)zhttp://úhttps://r¡   z://z/kontaktz/contact)Ú
startswithr   Ú	ExceptionÚnetlocÚschemerŸ   )r&   r�   rG   Úbaserq   r…   s         r   Ú_enrich_email_from_websitez*LinkedinScraper._enrich_email_from_websiteç   s©   € áØà×!Ñ!Ð"9Ô:Ø  7Ñ*ˆGð	Ü˜gÓ&ˆFð �}Š}ØØ—-‘-�  F§M¡M ?Ð3ˆà˜T˜F (Ð+°¨v°XÐ->Ð?ò 	ˆCØ×0Ñ0°Ó5ˆEÚØ’ð	ð øô ò 	Ùð	ús   ›A6 Á6	BÂBc              #  ó¢  ‡ K  — ‰ j                   st        j                  d«       y g }d }‰ j                  D ]}  }	 ‰ j	                  |«      }t        j                  d|t        |«      «       t        ˆ fd„|D «       «      }|rt        j                  d||«       |}|} nt        j                  d|«       Œ |s!t        j                  d‰ j                  «       y |xs ‰ j                  ‰ _
        t        j                  dt        |«      |«       d	}t        «       }|D �]€  }	‰ j                  r|‰ j                  k\  r y ‰ j                  |	«      \  }
}|
sS|	j                  d
«      xs( |	j                  d«      xs |	j                  d«      xs d}t        j                  d||«       ŒŠ‰ j                  |	«      }|sŒž|j                   sW|j"                  rKt        j                  d|j$                  |j"                  «       ‰ j'                  |j"                  «      |_        |j                   s"t        j                  d|j$                  «       �Œ/|j)                  «       s�ŒA|j                   xs d|j*                  xs df}||v r�Œg|j-                  |«       |dz  }|–— �Œƒ y # t
        $ r"}t        j                  d||«       Y d }~�Œ‘d }~ww xY w­w)Nu4   [linkedin] Brak APIFY_API_KEY â€” pomijam ÅºrÃ³dÅ‚o.z*[linkedin] Apify run failed (query=%r): %su$   [linkedin] query=%r â†’ %d rekordÃ³wc              3  óL   •K  — | ]  }‰j                  |«      d    sŒd–— Œ y­w)r   r
   N)ra   )r‰   Úitr&   s     €r   r‹   z(LinkedinScraper.fetch.<locals>.<genexpr>  s#   øè ø€ ÒJ °×0BÑ0BÀ2Ó0FÀqÓ0Iœ1ÑJùs   ƒ$�$uI   [linkedin] query=%r zwrÃ³ciÅ‚ %d firm z Polski â€” uÅ¼ywam tego zapytaniau<   [linkedin] query=%r â€” 0 firm z Polski, prÃ³bujÄ™ nastÄ™pneuF   [linkedin] SKIP â€” Å¼adne z zapytaÅ„ %s nie zwrÃ³ciÅ‚o firm z Polski.u2   [linkedin] Apify zwrÃ³ciÅ‚ %d rekordÃ³w (query=%r)r   rC   r   r€   z(bez nazwy)u/   [linkedin] SKIP â€” firma poza PolskÄ…: %s | %su0   [linkedin] Brak emaila dla '%s' â€” szukam na %sz$[linkedin] SKIPPED (brak emaila): %srP   r
   )r    rj   Úwarningr#   rv   r   Úerrorrk   ÚlenÚsumr$   Úsetr   ra   rH   r�   r…   r�   rC   r§   Úis_validry   Úadd)r&   ÚitemsÚ
used_queryr2   Úbatchrt   Úpl_countÚyieldedÚseenrJ   Úin_plÚloc_strÚnmÚleadrK   s   `              r   ÚfetchzLinkedinScraper.fetchý   sx  øè ø€ Ø�|Š|Ü�K‰KÐNÔOØð ˆØ!%ˆ
Ø×(Ñ(ò 	\ˆEðØŸ™¨Ó.�ô �H‰HÐ;¸UÄCÈÃJÔOÜÓJ uÔJÓJˆHÙÜ—‘Ø_ØØôð
 �Ø"�
ÙÜ�H‰HÐSÐUZÕ[ð#	\ñ& Ü�H‰HØXØ×#Ñ#ôð à&Ò;¨$×*;Ñ*;ˆÔÜ�‰ÐEÄsÈ5ÃzÐS]Ô^àˆÜ%(£UˆØó 	ˆDØ�zŠz˜g¨¯©Ò3ÙØ!×/Ñ/°Ó5‰NˆE�7Ùà—H‘H˜VÓ$ò %Ø—x‘x Ó.ò%à—x‘x Ó(ò%ð %ð	 ô —‘ÐJÈBÐPWÔXØØ—=‘= Ó&ˆDÙØØ—:’: $§,¢,Ü—‘ÐKÈTÏYÉYÐX\×XdÑXdÔeØ!×<Ñ<¸T¿\¹\ÓJ�”
Ø—:’:Ü—‘Ð?ÀÇÁÔKÙØ—=‘=”?ÙØ—:‘:Ò#  T§Z¡ZÒ%5°2Ð6ˆCØ�d‰{ÙØ�H‰H�SŒMØ�q‰LˆGØŒJñ;	øô9 ò Ü—	‘	ÐFÈÈqÔQÝûðüs/   ƒ6KºJ!ÁIKÊ!	KÊ*KËKËKËK)NNNNNN)r   ú
int | Noner   ú
str | Noner    r¾   r"   r½   r$   r¾   r%   zlist[str] | None)Úreturnr8   r6   )r2   r¾   r¿   rI   )rJ   rI   r¿   z	list[str])rT   r8   r¿   rQ   )rJ   rI   r¿   ztuple[bool, str])r2   r¾   r¿   z
list[dict])r¿   r¾   )rJ   rI   r¿   zLead | None)rq   r8   r¿   r¾   )r�   r8   r¿   r¾   )r¿   zIterator[Lead])r   r   r   rC   r   r,   r3   ÚstaticmethodrN   rU   Úclassmethodra   rv   r}   r�   rŸ   r§   r¼   Ú__classcell__)r'   s   @r   r   r   "   sÑ   ø„ Ø€Dð !Ø Ø"Ø $Ø#'Ø&*ð1àð1ð ð1ð ð	1ð
 ð1ð !ð1ð $õ1ó8,ô
ð òó ðð8 ò?ó ð?ð òó ðô$Eð4 ò	ó ð	ó1
óh*ó÷,Er   r   )Ú__doc__Ú
__future__r   ÚloggingrR   Útypingr   Úurllib.parser   rl   rP   r   Únotifierr   r	   r¦   r   r   Ú	getLoggerr   rj   rh   r£   r   r   r   r   r   ú<module>rÊ      sX   ðñ	õ #ã Û 	Ý Ý !ã å ß =ß %à€g×Ñ˜Ó!€à'€
ô	�ô 	ô`�mõ `r   