Ë
    ¸¬uj?,  ã                   óÚ   — d Z ddlZddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
mZ ddlmZ dddd	œZd
ZdZdZd„ Zd„ Zd„ Zdd„Zd„ Zd„ Zd„ Zd„ Zdd„Zd„ Zedk(  r e«        yy)u�   
ç¦„ä¸°å¸‚äººæ°‘æ”¿åºœ - ç”Ÿæ€�çŽ¯å¢ƒä¿�æŠ¤æ �ç›®çˆ¬è™«
URL: https://www.ynlf.gov.cn/zfxxgk/fdzdgknr/wjjzdnhms/hms/sthj.htm
CMS: Visual SiteBuilder 9 (VSB9)
é    N)Údatetime)ÚBeautifulSoupÚNavigableString)ÚurljoinzoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36z?text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8zzh-CN,zh;q=0.9)z
User-AgentÚAcceptzAccept-Languagezhttps://www.ynlf.gov.cnz>https://www.ynlf.gov.cn/zfxxgk/fdzdgknr/wjjzdnhms/hms/sthj.htmz/mnt/data/search.dbc                  ó4   — t        j                  t        «      S ©N)Úsqlite3ÚconnectÚDB_PATH© ó    ú$/root/gov_crawler/crawl_ynlf_sthj.pyÚget_connr      s   € Ü�?‰?œ7Ó#Ð#r   c                 ó0  — g }g }t        | j                  «      D ]è  }|j                  €Œ|j                  dk(  r|j                  t	        |«      «       Œ:|j                  d«      r3|j                  dd¬«      D ]  }|j                  t	        |«      «       Œ Œ~|j                  dk(  r'|j                  d¬«      }|sŒ¢|j                  |«       Œ´|j                  dv sŒÃ|j                  d¬«      }|sŒØ|j                  |«       Œê dj                  |«      |fS )z„Extract text with 

 paragraph separation from a VSB9 content container.
    Handles nested <p>, <div>, <table> structures properly.ÚtableT)Ú	recursiveÚp©Ústrip)ÚdivÚsectionÚh1Úh2Úh3Úh4Úh5Úh6ú

)	ÚlistÚchildrenÚnameÚappendÚstrÚfindÚfind_allÚget_textÚjoin)Ú	containerÚ
paragraphsÚtables_htmlÚchildÚts        r   Úextract_content_textr.   !   s  € ð €JØ€Kä�i×(Ñ(Ó)ò %ˆØ�:‰:ÐØØ�Z‰Z˜7Ò"Ø×Ñœs 5›zÔ*ØØ�Z‰Z˜Ô à—^‘^ G°t�^Ó<ò +�Ø×"Ñ"¤3 q£6Õ*ð+ð ð �:‰:˜ÒØ—‘ T�Ó*ˆAÚØ×!Ñ! !Õ$Ø�Z‰ZÐQÒQØ—‘ T�Ó*ˆAÚØ×!Ñ! !Õ$ð+%ð. �;‰;�zÓ" KÐ/Ð/r   c                 ó„  ‡‡— g }| j                  d«      D ]Q  }t        ||d   «      }|j                  dd«      j                  «       }|rd|› d�nd}|j	                  |› d|› �«       ŒS | j                  d	«      D ]G  }|j                  d
d«      Št        ˆfd„dD «       «      sŒ*|j	                  dt        |‰«      › �«       ŒI | j                  d«      D ]g  }|d   Št        |‰«      }	|j                  d¬«      xs ‰j                  d«      d   }
t        ˆfd„dD «       «      sŒQ|j	                  d|
› d|	› �«       Œi |S )zEExtract image/attachment links from the page when content is minimal.zimg[src]ÚsrcÚaltÚ u	   [å›¾ç‰‡: ú]u   [å›¾ç‰‡]ú zobject[data]Údatac              3   ó\   •K  — | ]#  }‰j                  «       j                  |«      –— Œ% y ­wr	   ©ÚlowerÚendswith)Ú.0Úextr5   s     €r   ú	<genexpr>z+extract_attachment_links.<locals>.<genexpr>K   s"   øè ø€ ÒO¨cˆt�z‰z‹|×$Ñ$ S×)ÑOùó   ƒ),)ú.pdfú.docú.docxu   [é™„ä»¶PDF] úa[href]ÚhrefTr   ú/éÿÿÿÿc              3   ó\   •K  — | ]#  }‰j                  «       j                  |«      –— Œ% y ­wr	   r7   )r:   r;   rB   s     €r   r<   z+extract_attachment_links.<locals>.<genexpr>Q   s"   øè ø€ Òx¨cˆt�z‰z‹|×$Ñ$ S×)Ñxùr=   )r>   r?   r@   z.xlsz.xlsxz.jpgz.pngz.gifu	   [é™„ä»¶: ú] )Úselectr   Úgetr   r#   Úanyr'   Úsplit)ÚsoupÚpage_urlÚlinksÚimgr0   r1   ÚlabelÚobjÚaÚfull_urlÚfnamer5   rB   s              @@r   Úextract_attachment_linksrT   A   sH  ù€ à€EØ�{‰{˜:Ó&ò 'ˆÜ�h  E¡
Ó+ˆØ�g‰g�e˜RÓ ×&Ñ&Ó(ˆÙ&)�)˜C˜5 Ñ"¨zˆØ�‰˜�w˜a ˜uÐ%Õ&ð	'ð
 �{‰{˜>Ó*ò CˆØ�w‰w�v˜rÓ"ˆÜÓOÐ5NÔOÕOØ�L‰L˜<¬°¸$Ó(?Ð'@ÐAÕBðCð �[‰[˜Ó#ò :ˆØ�‰yˆÜ˜8 TÓ*ˆØ—
‘
 �
Ó&Ò=¨$¯*©*°S«/¸"Ñ*=ˆÜÓxÐ5wÔxÕxØ�L‰L˜9 U G¨2¨h¨ZÐ8Õ9ð:ð €Lr   c                 óä  — t        | d«      }|j                  d«      D ]  }|j                  «        Œ d}dD ]  }|j                  |«      }|sŒ|} n |s|j                  r|j                  n|}|j                  d«      }|r|}t        |«      \  }}	t        |«      dk  rt        ||«      ng }
|}|	r|ddj                  |	«      z   z  }|
r|ddj                  |
«      z   z  }|j                  «       S )	zKClean HTML and extract structured content with proper paragraph separation.úhtml.parserz:script, style, iframe, .page_css, .label, hr, #div_vote_idN)zdiv#vsb_contentzdiv.contentúdiv.v_news_contentzdiv.vsb_contentzdiv.TRS_Editorzdiv.xxgk_contentzdiv.article-contentrW   iô  r   ú
)
r   rG   Ú	decomposeÚ
select_oneÚbodyr.   ÚlenrT   r(   r   )ÚhtmlrL   rK   ÚtagÚcontentÚselectorÚelÚvncÚtextr+   rM   Úresults               r   Ú
clean_htmlre   V   s
  € ä˜˜}Ó-€Dð �{‰{ÐWÓXò ˆØ�‰�ðð €GðRò ˆà�_‰_˜XÓ&ˆÚØˆGÙðñ Ø#Ÿyšy�$—)’)¨dˆð ×
Ñ
Ð1Ó
2€CÙ
Øˆô -¨WÓ5Ñ€Dˆ+ô 9<¸D»	ÀCºÔ$ T¨8Ô4ÈR€Eð €FÙØ�&˜6Ÿ;™; {Ó3Ñ3Ñ3ˆÙØ�&˜4Ÿ9™9 UÓ+Ñ+Ñ+ˆà�<‰<‹>Ðr   c           	      ó<  — 	 t        j                  dddddd| gdd¬	«      }|j                  d
k(  r+|j                  r|j                  j	                  dd¬«      }|S t        d|j                  › d| › �«       y# t        $ r}t        d| › d|› �«       Y d}~yd}~ww xY w)z&Fetch a page with curl via subprocess.Úcurlz-sSz-Lz
--max-timeÚ30z-kTé<   )Úcapture_outputÚtimeoutr   úutf-8Úreplace)Úerrorsz  [WARN] curl exit=z for Nz  [ERROR] fetch ú: )Ú
subprocessÚrunÚ
returncodeÚstdoutÚdecodeÚprintÚ	Exception)Úurlrd   rc   Úes       r   Ú
fetch_pagery      s®   € ðÜ—‘Ø�E˜4 ¨t°TØð!
ð ¨ô,ˆð ×Ñ Ò! f§m¢mØ—=‘=×'Ñ'¨¸	Ð'ÓBˆDØˆKÜÐ# F×$5Ñ$5Ð#6°e¸C¸5ÐAÔBØøÜò ÜÐ    R¨ sÐ+Ô,Üûðús   ‚AA7 ÁA7 Á7	BÂ BÂBc                 óö  — g }t        | d«      }|j                  d«      D ]Ö  }|j                  d«      }|sŒ|j                  dd«      }|j                  dd«      xs |j	                  d¬«      }|rt        |«      d	k  rŒ`|j                  d
«      }|r|j	                  d¬«      nd}	|j                  dd«      j                  dd«      j                  «       }t        ||«      }
|j                  ||
|	f«       ŒØ |S )z8Parse VSB9 list page, return list of (title, url, date).rV   zli[id^="line_u6_"]rA   rB   r2   ÚtitleTr   é   z	span.dateõ   Â r4   õ   Â·)
r   rG   rZ   rH   r'   r\   rm   r   r   r#   )r]   Úbase_urlÚitemsrK   ÚlirQ   rB   r{   Ú	date_spanÚdaterR   s              r   Ú
parse_listr„   �   sñ   € à€EÜ˜˜}Ó-€Dà�k‰kÐ.Ó/ò .ˆØ�M‰M˜)Ó$ˆÙØà�u‰u�V˜RÓ ˆØ—‘�g˜rÓ"Ò< a§j¡j°t jÓ&<ˆÙœ˜E›
 QšØà—M‘M +Ó.ˆ	Ù1:ˆy×!Ñ!¨Ð!Ô-Àˆà—‘˜f cÓ*×2Ñ2°8¸RÓ@×FÑFÓHˆÜ˜8 TÓ*ˆà�‰�e˜X tÐ,Õ-ð!.ð$ €Lr   c                 ó\  — d}| j                  d«      rd}nYt        j                  d| «      }|rt        |j	                  d«      «      dz   }n#|j                  d«      }|rt        | |d   «      S y|dz   }|dk(  r| j                  dd«      }|S t        j                  d	d
|dz
  › d�| «      }|S )z'Find next page URL for VSB9 pagination.é   zsthj.htmz/sthj/(\d+)\.htmza[href*="sthj/"], span.p_next arB   Né   z
sthj/1.htmz/sthj/\d+\.htmz/sthj/z.htm)	r9   ÚreÚsearchÚintÚgrouprZ   r   rm   Úsub)Úcurrent_urlrK   Úcurrent_pageÚmÚnext_aÚ	next_pageÚnext_urls          r   Úget_next_page_urlr“   ª   s»   € à€LØ×Ñ˜JÔ'Ø‰ä�I‰IÐ)¨;Ó7ˆÙÜ˜qŸw™w q›z›?¨QÑ.‰Là—_‘_Ð%FÓGˆFÙÜ˜{¨F°6©NÓ;Ð;Øà˜qÑ €Ià�A‚~Ø×&Ñ& z°<Ó@ˆð €Oô —6‘6Ð+¨v°iÀ±k°]À$Ð-GÈÓUˆà€Or   c                 ó"  — 	 | j                  d|f«      j                  «       }|sy|\  }}}}}}}	|	xs	 |xs ddd }
d|› d|j                  t        d«      t        d«      dz  «      › d	|j                  t        d«      t        d«      dz  «      › d	|
j                  t        d«      t        d«      dz  «      › d
�	}t	        j
                  dt        g|j                  d«      dd¬«       y# t        $ r}t        d|› d|› �«       Y d}~yd}~ww xY w)zPSync a single row to FTS table via subprocess (stdin to preserve Chinese chars).zaSELECT id, title, content, publish_date, source_url, site_name, summary FROM gov_raw WHERE id = ?Nr2   éÈ   zKINSERT OR REPLACE INTO gov_search(rowid, title, site_name, summary) VALUES(z, 'é'   r‡   z', 'z');
r
   rl   Té
   )Úinputrj   rk   z  [FTS ERROR] row_id=ro   )
ÚexecuteÚfetchonerm   Úchrrp   rq   r   Úencoderv   ru   )ÚconnÚrow_idÚrowÚridr{   r_   Úpub_dateÚsrc_urlÚ	site_nameÚsummaryÚsummary_textÚsqlrx   s                r   Úsync_ftsr§   Ã   sÔ  € ð5Ø�l‰lÐ~ð  BHð  AJó  K÷  Tñ  Tó  VˆÙØØEHÑBˆˆU�G˜X w°	¸7ØÒ5 5¢;¨B°°Ð"5ˆð \Ð\_Ð[`Ð`cÐdi×dqÑdqÔruÐvxÓryÔ{~ð  @Bó  |Cð  DEñ  |Eó  eFð  dGð  GKð  LU÷  L]ñ  L]ô  ^að  bdó  ^eô  gjð  kmó  gnð  opñ  gpó  Lqð  Krð  rvð  wC÷  wKñ  wKô  LOð  PRó  LSô  UXð  Y[ó  U\ð  ]^ñ  U^ó  w_ð  v`ð  `eð  fˆÜ�‰˜	¤7Ð+°3·:±:¸gÓ3FÐW[ÐegÖhøÜò 5ÜÐ% f X¨R°¨sÐ3×4Ñ4ûð5ús   ‚#C* ¦CC* Ã*	DÃ3D	Ä	Dc                 óÀ  — g }t        «       }t        d| › �«       t        }t        d| dz   «      D ]Ì  }t        d|› d|› �«       t	        |«      }|st        d|› �«        nžt        ||«      }|D �cg c]  }|d   |vsŒ|‘Œ }}|D ]  }|j                  |d   «       Œ t        dt        |«      › dt        |«      › d�«       |j                  |«       t        |d	«      }	t        ||	«      }
|
st        d
«        n|
}ŒÎ t        dt        |«      › d› d�«       t        «       }d}d}t        |d«      D �]¿  \  }\  }}}t        d|› dt        |«      › d|dd › d�d¬«       |j                  d|f«      j                  «       }|rt        d«       Œ]t	        |«      }|st        d«       Œvt        |d	«      }	|}|	j                  d«      }|r7|j                  d«      }|r$|j!                  d¬«      }|rt        |«      dkD  r|}|	j                  d«      }|r6|j#                  d «      r%|d    j%                  «       }|rt        |«      dkD  r|}|j'                  d!d«      j'                  d"d#«      j%                  «       }|}|r}|j!                  «       }t)        j*                  d$|«      }|rU|j-                  d«      › d%|j-                  d&«      j/                  d&«      › d%|j-                  d'«      j/                  d&«      › �}|	j                  d(«      }|r?|j#                  d «      r.|d    j%                  «       dd) }t)        j0                  d*|«      r|}t3        ||«      }|rt        |«      d)k  r|dz  }|rt        |«      nd}d+} 	 |j                  d,||xs d#||| |dd- d.d/f«       |j5                  «        |j                  d0«      j                  «       d   }!t7        ||!«       |dz  }t        d1|› d2�«       �ŒÂ |j=                  «        t        d4t        |«      › d5|› d6|› d7�«       yc c}w # t8        $ r)}"|j;                  «        t        d3|"› �«       Y d}"~"�Œ$d}"~"ww xY w)8zMain crawl function.u4   [START] ç¦„ä¸°å¸‚-ç”Ÿæ€�çŽ¯å¢ƒä¿�æŠ¤ çˆ¬è™« | pages=r†   z
[LIST] Page ro   z  [FAIL] Cannot fetch page z  Found z items, z newrV   z  No more pagesz	
[TOTAL] z items from z pagesr   z  [rC   rF   Né(   z...r4   )Úendz+SELECT id FROM gov_raw WHERE source_url = ?zSKIP (exists)zFAIL (fetch)z"form[name="_newscontent_fromname"]r   Tr   r|   zmeta[name="ArticleTitle"]r_   r}   r~   r2   u.   æ—¥æœŸ[ï¼š:](\d{4})å¹´(\d{1,2})æœˆ(\d{1,2})æ—¥ú-r‡   é   zmeta[name="PubDate"]r—   z\d{4}-\d{2}-\d{2}u   ç¦„ä¸°å¸‚äººæ°‘æ”¿åºœz�INSERT INTO gov_raw (title, content, publish_date, source_url, site_name, summary, group_name, industry) VALUES (?,?,?,?,?,?,?,?)r•   u   äº‘å�—çœ�æ¥šé›„å·žu   ç”Ÿæ€�çŽ¯å¢ƒzSELECT last_insert_rowid()zOK (z chars)z
DB ERROR: u   
[DONE] æ€»è®¡ u    æ�¡, æ–°å¢ž u    æ�¡, ç©ºæ­£æ–‡ u    æ�¡)Úsetru   ÚLIST_URLÚrangery   r„   Úaddr\   Úextendr   r“   r   Ú	enumerater™   rš   rZ   r'   rH   r   rm   rˆ   r‰   r‹   ÚzfillÚmatchre   Úcommitr§   rv   ÚrollbackÚclose)#ÚpagesÚ	all_itemsÚ	seen_urlsr�   Úpgr]   r€   ÚitÚ	new_itemsrK   r’   r�   ÚinsertedÚempty_contentÚir{   rw   rƒ   ÚexistingÚcontent_titleÚformr   r-   Ú
meta_titleÚmcÚcontent_dateÚ	date_textr�   Ú	meta_dateÚmdr[   Úcontent_lenr£   r    rx   s#                                      r   ÚcrawlrË   Ó   s}  € à€IÜ“€Iä	Ð@ÀÀÐ
HÔIä€KÜ�A�u˜q‘yÓ!ò ˆÜ�˜r˜d " [ MÐ2Ô3Ü˜+Ó&ˆÙÜÐ/°¨tÐ4Ô5Ùä˜4 Ó-ˆØ"'ÖB˜B¨2¨a©5¸	Ò+A’RÐBˆ	ÐBØò 	!ˆBØ�M‰M˜"˜Q™%Õ ð	!ô 	�œ˜U›˜ H¬S°«^Ð,<¸DÐAÔBØ×Ñ˜Ô#ä˜T =Ó1ˆÜ$ [°$Ó7ˆÙÜ�OÔ%ÙØ‰ð)ô, 
ˆJ”s˜9“~Ð& l°2°$°fÐ
=Ô>ä‹:€DØ€HØ€Mä!*¨9°aÓ!8ó C$ÑˆÑˆE�3˜Ü��A�3�aœ˜I›Ð' r¨%°°¨*¨°SÐ9¸sÕCà—<‘<Ð MÐPSÈvÓV×_Ñ_ÓaˆÙÜ�MÔ#Øä˜#‹ˆÙÜ�LÔ"Øä˜T =Ó1ˆð ˆØ�‰ÐCÓDˆÙØ—‘ Ó&ˆBÙØ—K‘K d�KÓ+�Ùœ˜Q› !šØ$%�Mà—_‘_Ð%@ÓAˆ
Ù˜*Ÿ.™.¨Ô3Ø˜IÑ&×,Ñ,Ó.ˆBÙ”c˜"“g ’kØ "�à%×-Ñ-¨f°cÓ:×BÑBÀ8ÈRÓP×VÑVÓXˆð ˆÙØŸ™›ˆIÜ—	‘	ÐKÈYÓWˆAÙØ"#§'¡'¨!£* ¨Q¨q¯w©w°q«z×/?Ñ/?ÀÓ/BÐ.CÀ1ÀQÇWÁWÈQÃZ×EUÑEUÐVWÓEXÐDYÐZ�à—O‘OÐ$:Ó;ˆ	Ù˜Ÿ™ yÔ1Ø˜9Ñ%×+Ñ+Ó-¨c¨rÐ2ˆBÜ�x‰xÐ,¨bÔ1Ø!�ä˜$ Ó$ˆá”s˜4“y 2’~Ø˜QÑˆMá#'”c˜$”i¨QˆØ+ˆ	ð	$Ø�L‰Lð TØ ¢
¨¨L¸#¸yÈ-ÐX\ÐY\ÐJ]Ð_sð  vDð  Eôð �K‰KŒMØ—,‘,Ð;Ó<×EÑEÓGÈÑJˆCÜ�T˜3ÔØ˜‰MˆHÜ�D˜˜ WÐ-Ö.ðAC$ðJ 	‡J�J„Lä	ÐœS ›^Ð,¨M¸(¸ÐCSÐTaÐSbÐbfÐ
gÕhùòw Cøôj ò 	$Ø�M‰MŒOÜ�J˜q˜cÐ"×#Ò#ûð	$ús%   Á1P&Á>P&ÎA3P+Ð+	QÐ4QÑQc                  ó¦   — dd l } | j                  «       }|j                  dt        d¬«       |j	                  «       }t        |j                  «       y )Nr   z--pagesé   )ÚtypeÚdefault)ÚargparseÚArgumentParserÚadd_argumentrŠ   Ú
parse_argsrË   r¸   )rÐ   ÚparserÚargss      r   ÚmainrÖ   A  sB   € ÛØ×$Ñ$Ó&€FØ
×Ñ˜	¬°QÐÔ7Ø×ÑÓ€DÜ	ˆ$�*‰*Õr   Ú__main__)r2   )rÍ   )Ú__doc__Úrequestsrˆ   ÚsysÚosr
   Újsonrp   r   Úbs4r   r   Úurllib.parser   ÚHEADERSÚBASE_URLr®   r   r   r.   rT   re   ry   r„   r“   r§   rË   rÖ   Ú__name__r   r   r   ú<module>râ      s™   ðñó
 Û 	Û 
Û 	Û Û Û Ý ß .Ý  ð DØOØ'ñ€ð
 %€ØK€à
€ò$ò0ò@ó*&òRò"ò4ò25ó kiò\ð ˆzÒÙ…Fð r   