
     j+                    (   d dl m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	Z	d dl
mZ d dlmZ d dlmZ d dlmZ d dlmZ d dlmZmZ d d	lmZmZ d d
lmZ dZdZdZdZdZ ej@                  dejB                        Z" ej@                  d      Z#dZ$ G d de%      Z& ed       G d d             Z'd(dZ(d)dZ)d*dZ*d+dZ+d,dZ,d-dZ-d.dZ.d/d Z/d0d!Z0d1d"Z1	 	 	 	 	 	 	 	 d2d#Z2d3d$Z3d4d%Z4e5d&k(  r	  e4         ejl                  d'       y)5    )annotationsN)	dataclass)datetime)MessagePath)Any)	HTTPErrorURLError)Requesturlopen)ZoneInfoi>zzAsia/Shanghaiz'https://www.bilibili.com/list/{user_id}z1https://space.bilibili.com/{user_id}/upload/videozuMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36z.window\.__INITIAL_STATE__=(.*?);\(function\(\)u   (\d+(?:\.\d+)?)(万|亿)?titlepublish_time	video_urlc                      e Zd ZdZy)BilibiliCrawlerErrorz9Raised when the crawler cannot extract the expected data.N)__name__
__module____qualname____doc__     bilibili_latest_video.pyr   r   &   s    Cr   r   T)slotsc                  b    e Zd ZU ded<   ded<   ded<   ded<   ded<   edd       Zdd	Zdd
Zy)	VideoInfointuser_idstrr   bvid
play_countpublish_timestampc                     d| j                    S )Nzhttps://www.bilibili.com/video/)r"   )selfs    r   r   zVideoInfo.video_url2   s    0<<r   c                x    t        |      }t        j                  | j                  |      j	                  d      S )N)tzz%Y-%m-%d %H:%M:%S %Z)r   r   fromtimestampr$   strftime)r&   timezone_namer(   s      r   format_publish_timezVideoInfo.format_publish_time6   s5    m$%%d&<&<DMM"
 	
r   c                    | j                   | j                  | j                  | j                  | j                  | j                  |      | j                  dS )N)r    r   r"   r#   r$   r   r   )r    r   r"   r#   r$   r,   r   )r&   r+   s     r   to_dictzVideoInfo.to_dict<   sF    ||ZZII//!%!7!7 44]C
 	
r   N)returnr!   )r+   r!   r/   r!   )r+   r!   r/   dict[str, Any])r   r   r   __annotations__propertyr   r,   r.   r   r   r   r   r   *   s8    LJ
IO= =
	
r   r   c                D    t         ddt        j                  |       dddS )Nz?text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8zzh-CN,zh;q=0.9,en;q=0.8r    zno-cache)z
User-AgentAcceptzAccept-LanguageRefererzCache-ControlPragma)
USER_AGENTSPACE_URL_TEMPLATEformatr4   s    r   build_headersr;   H   s,     S4%,,W,=# r   c                x   |j                  dd      j                         }|dk(  rt        j                  |       } n|dk(  r	 t	        j                  |       } |j                         xs d}| j                  |d      S # t        j
                  $ r( t	        j                  | t        j                         } Y aw xY w)NzContent-Encoding gzipdeflatezutf-8replace)errors)	getlowerr>   
decompresszliberror	MAX_WBITSget_content_charsetdecode)bodyheaderscontent_encodingcharsets       r   decode_responserN   S   s    {{#5r:@@B6!t$	Y	&	:??4(D ))+6wG;;wy;11	 zz 	:??4$..9D	:s   A> >8B98B9c                   t        t        j                  |       t        |             }	 t	        ||      5 }|j                         }t        ||j                        cd d d        S # 1 sw Y   y xY w# t        $ r4}d|j                   }|j                  dk(  r|dz  }t        |      |d }~wt        $ r}t        d|j                         |d }~ww xY w)Nr4   )rK   )timeoutu   请求失败，HTTP i  u=   （B站风控触发，请稍后重试或更换网络环境）u   网络请求失败：)r   LIST_URL_TEMPLATEr:   r;   r   readrN   rK   r
   coder   r   reason)r    rP   requestresponserJ   excmessages          r   fetch_list_pagerY   a   s       1g&G
RWg.(==?D"4)9)9: /..  5(
388s?VVG"7+4 R"%:3::,#GHcQRs@   A5 &A)	A5 )A2.A5 2A5 5	C>/B--C9CCc                    t         j                  |       }|st        d      	 t        j                  |j                  d            S # t        j                  $ r}t        d      |d }~ww xY w)Nu.   未在页面中找到 __INITIAL_STATE__ 数据   u   页面状态 JSON 解析失败)INITIAL_STATE_PATTERNsearchr   jsonloadsgroupJSONDecodeError)htmlmatchrW   s      r   extract_initial_staterd   s   sd    !((.E"#STTNzz%++a.)) N"#CD#MNs   #A A,A''A,c                    t         j                  |       }|syt        |j                  d            }|j                  d      }d}|dk(  rd}n|dk(  rd}t	        ||z        S )Nr   r[      u   万i'  u   亿i )PLAY_COUNT_PATTERNr]   floatr`   r   )	play_textrc   valueunit
multipliers        r   parse_play_countrm   ~   sd    %%i0E%++a.!E;;q>DJu}
	 
uz!""r   c           
        g }| j                  d      xs i }|r|j                  |j                  dd      |j                  dd      |j                  d      xs |j                  d      xs d|j                  d      xs i j                  d	      xs( |j                  d      xs i j                  d
      xs dd       |j                  d      xs g D ]  }|j                  d      xs g D ]  }|j                  d      xs i }|j                  |j                  dd      |j                  d      xs |j                  dd      |j                  d      xs |j                  d      xs d|j                  d      xs i j                  d	      xs( |j                  d      xs i j                  d
      xs dd         |S )N	videoDatar"   r=   r   pubdatectimer   statviewvv)r"   r   rp   r#   sectionsepisodesarc)rB   append)staterecords
video_datasectionepisoderw   s         r   iter_episode_recordsr~      s   $&G;'-2J"vr2#4%>>)4T
w8OTST^^F+1r66v> "v.4"99$?		
 >>*-33{{:.4"4G++e$*CNN#KK3$[[1ISWWWb5I"wwy1JSWWW5EJ.B33F; GGFO1r66t<	 5 4  Nr   c                   | j                  d      xs g }|st        d      |d   }|j                  dd      }|j                  dd      }t        |j                  dd            }t        |       D ci c]'  }|j                  d      s|j                  dd      |) }}|j                  |      }	|	rqt	        |	j                  d      xs d      }
t	        |	j                  d	      xs |      }|
dkD  r0t        ||xs t        |	j                  d      xs d      |||

      S | j                  d      xs i }t	        |j                  d      xs |j                  d      xs d      }
t	        |j                  d      xs i j                  d      xs, |j                  d      xs i j                  d      xs |xs d      }|r5|
dkD  r0t        ||xs t        |j                  d      xs d      |||

      S t        d      c c}w )NresourceListu!   页面里没有找到视频列表r   r"   r=   r   viewsrp   r#   )r    r   r"   r#   r$   ro   rq   rr   rs   rt   u6   找到了视频列表，但没能提取出发布时间)rB   r   rm   r~   r   r   r!   )ry   r    resource_listlatest_itemlatest_bvidlatest_titlefallback_play_countitemepisode_mapr}   r$   r#   r{   s                r   extract_latest_videor      s   IIn-3M"#FGG"K//&"-K??7B/L*;??7B+GH 0DE/J/JtdhhW]N^d"/J   ook*GI 6 ;!<\2I6IJ
q "Ec'++g*>*D"&E %"3  ;'-2JJNN95U9PUTUV
..
 
&B	+	+F	3 	^^F#)r..t4		 	J (1,D#jnnW&=&C"D!/
 	
 W
XXEs   4H	H	c                H    t        | |      }t        |      }t        ||       S )N)rY   rd   r   )r    rP   rb   ry   s       r   get_latest_videor      s%    7G,D!$'Ew//r   c                     t        d|  d      S )Nbilibili_latest_videos_z.csvr   r4   s    r   default_csv_pathr      s    )'$788r   c                    | j                         sy| j                  ddd      5 }t        j                  |      }t	        fd|D              cd d d        S # 1 sw Y   y xY w)NFr	utf-8-sigr=   encodingnewlinec              3  F   K   | ]  }|j                  d       k(    yw)r   N)rB   ).0rowr   s     r   	<genexpr>z&latest_video_exists.<locals>.<genexpr>   s      G377;'94s   !)existsopencsv
DictReaderany)csv_pathr   filereaders    `  r   latest_video_existsr      sL    ??	s["	=%GGG 
>	=	=s   )AA#c                   |j                   j                  dd       t        || j                        ry|j	                          xs |j                         j                  dk(  }|j                  ddd      5 }t        j                  |t        	      }|r|j                          |j                  | j                  | j                  |      | j                  d
       d d d        y# 1 sw Y   yxY w)NT)parentsexist_okFr   ar   r=   r   )
fieldnamesr   )parentmkdirr   r   r   rr   st_sizer   r   
DictWriterCSV_FIELDNAMESwriteheaderwriterowr   r,   )videor   r+   should_write_headerr   writers         r   save_latest_video_to_csvr      s    
 OO$68U__5&oo//O8==?3J3Ja3O	s["	=@  % 9 9- H"__	
	 
>  
> s   9A&C((C1c                 D   t        j                  d      } | j                  dt        t        dt                | j                  dt
        dt
                | j                  d	t        d
d       | j                  ddd       | j                  dt        d d       | S )NuH   查询 B 站用户最新发布的视频标题、发布时间和播放量)descriptionz	--user-idu   目标 B 站用户 ID，默认 )typedefaulthelpz
--timezoneu"   发布时间输出时区，默认 )r   r   z	--timeoutg      .@u-   网络请求超时时间（秒），默认 15z--json
store_trueu   以 JSON 格式输出结果)actionr   z--csvuN   保存最新视频信息的 CSV 文件路径，默认按用户 ID 自动命名)argparseArgumentParseradd_argumentr   DEFAULT_USER_IDDEFAULT_TIMEZONErh   r   )parsers    r   build_parserr     s    $$^F ..?@	    12B1CD  
 <	   *  
 ]	   Mr   c                    t               } | j                         }	 t        |j                  |j                        }|j                  xs t        |j                        }	 t        |||j                        }|j                  r;t        t        j                   |j#                  |j                        dd	             y
t        d|j                          t        d|j$                          t        d|j'                  |j                                t        d|j(                          t        d|j*                          t        d|        t        d|rdnd        y
# t
        $ r(}t        d| t        j                         Y d }~yd }~wt        $ r(}t        d| t        j                         Y d }~yd }~ww xY w# t        $ r(}t        d| t        j                         Y d }~yd }~ww xY w)N)r    rP   u	   错误：)r   r[   u   未预期错误：u   CSV 写入失败：Frf   )ensure_asciiindentr   u
   用户ID: u   最新视频标题: u   发布时间: u   播放量: u   视频链接: u   CSV文件: u   CSV状态: u   已写入新视频u!   视频已存在，未重复写入)r   
parse_argsr   r    rP   r   printsysstderr	Exceptionr   r   r   timezoneOSErrorr^   dumpsr.   r   r,   r#   r   )r   argsr   rW   r   saveds         r   mainr   .  s   ^FD t||L xx9+DLL9H($--H
 yyJJdmm,"	
 	Ju}}o
&'	 
./	N544T]]CD
EF	K(()
*+	N5??+
,-	Kz
"#	K,;^_
`a?   	#cjj1 "3%(szz:  #C5)

;s;   !E' !G
 '	G0FGGG
	G;G66G;__main__i,  )r    r   r/   zdict[str, str])rJ   bytesrK   r   r/   r!   )r    r   rP   rh   r/   r!   )rb   r!   r/   r0   )ri   r!   r/   r   )ry   r0   r/   zlist[dict[str, Any]])ry   r0   r    r   r/   r   )r    r   rP   rh   r/   r   )r    r   r/   r   )r   r   r   r!   r/   bool)r   r   r   r   r+   r!   r/   r   )r/   zargparse.ArgumentParser)r/   r   )7
__future__r   r   r   r>   r^   rer   timerE   dataclassesr   r   email.messager   pathlibr   typingr	   urllib.errorr
   r   urllib.requestr   r   zoneinfor   r   r   rQ   r9   r8   compileDOTALLr\   rg   r   RuntimeErrorr   r   r;   rN   rY   rd   rm   r~   r   r   r   r   r   r   r   r   sleepr   r   r   <module>r      sS   "  
   	 
   !  !   , +  " = H % 
 #

5ryy   RZZ <= 7D< D 
 
 
:2R$N#!H,Y^09H  
	0 F%P z


6  r   