55using System . Collections . Generic ;
66using System . Linq ;
77using System . Runtime . Serialization ;
8+ using System . Text ;
89using System . Text . RegularExpressions ;
910using System . Threading . Tasks ;
1011using System . Xml ;
@@ -37,6 +38,7 @@ public static WebApi Instance
3738 }
3839 }
3940
41+ internal bool CheckNeedLogin ( string html ) => html . Contains ( "id=\" login\" " ) ;
4042 internal void ParsePlayListSong ( SongModel song , HtmlNode tr )
4143 {
4244 var name = tr . SelectSingleNode ( "./td[@class='song_name']" ) ;
@@ -904,50 +906,69 @@ public IAsyncOperation<SearchResult> Search(string keyword)
904906 doc . LoadHtml ( content ) ;
905907 var root = doc . DocumentNode . SelectSingleNode ( "html/body/div[@id='page']" ) ;
906908 var results = root . SelectSingleNode ( ".//div[@class='search_result']" ) . Elements ( "div" ) . ToList ( ) ;
909+
907910 var match = results [ 0 ] ; //最佳匹配内容
908- if ( match . GetAttributeValue ( "class" , "" ) == "top_box" )
911+ if ( match . GetAttributeValue ( "class" , "" ) . Contains ( "top_box" ) )
909912 {
910913 results . Remove ( match ) ;
911914 // TODO: 处理最佳匹配
912915 }
916+
913917 var songs = results [ 0 ] ;
914- res . Songs = new PageItemsCollection < SongModel > ( songs . Descendant ( "tbody" ) . Elements ( "tr" ) . Select ( ( node ) => ParseSearchedSong ( node ) ) ,
915- async ( page , ptoken ) =>
916- {
917- var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/song/page/{ page } ?key={ keyword } ") ;
918- ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
919- var pcontent = await pgettask ;
920- var pdoc = new HtmlDocument ( ) ;
921- pdoc . LoadHtml ( pcontent ) ;
922- return pdoc . DocumentNode . SelectSingleNode ( ".//table[@class='track_list']" )
923- . Descendant ( "tbody" ) . Elements ( "tr" ) . Select ( ( node ) => ParseSearchedSong ( node ) ) ; // 会有多个tbody,TODO:处理tbody分组(取第一个?)
924- } ) ;
918+ var songres = ParseSearchedSongTable ( songs . Descendant ( "table" ) ) ;
919+ //System.Diagnostics.Debugger.Break();
920+ if ( songs . Element ( "span" ) != null )
921+ res . Songs = new PageItemsCollection < SongModel > ( songres ,
922+ async ( page , ptoken ) =>
923+ {
924+ var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/song/page/{ page } ?key={ keyword } ") ;
925+ ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
926+ var pcontent = await pgettask ;
927+ if ( CheckNeedLogin ( pcontent ) ) return null ; // 需要登录才能看5页以后的内容
928+ var pdoc = new HtmlDocument ( ) ;
929+ pdoc . LoadHtml ( pcontent ) ;
930+ return ParseSearchedSongTable ( pdoc . DocumentNode . SelectSingleNode ( ".//table[@class='track_list']" ) ) ;
931+ } ) ;
932+ else
933+ res . Songs = songres ;
934+
925935 var albums = results [ 1 ] ;
936+ var albumres = albums . Descendant ( "ul" ) . Elements ( "li" ) . Select ( ( node ) => ParseSearchedAlbum ( node ) ) ;
926937 //System.Diagnostics.Debugger.Break();
927- res . Albums = new PageItemsCollection < AlbumModel > ( 30 , albums . Descendant ( "ul" ) . Elements ( "li" ) . Select ( ( node ) => ParseSearchedAlbum ( node ) ) ,
928- async ( page , ptoken ) =>
929- {
930- var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/album/page/{ page } ?key={ keyword } ") ;
931- ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
932- var pcontent = await pgettask ;
933- var pdoc = new HtmlDocument ( ) ;
934- pdoc . LoadHtml ( pcontent ) ;
935- return pdoc . DocumentNode . SelectSingleNode ( ".//ul[@class='clearfix']" )
936- . Elements ( "li" ) . Select ( ( node ) => ParseSearchedAlbum ( node ) ) ;
937- } ) ;
938+ if ( albums . Element ( "span" ) != null )
939+ res . Albums = new PageItemsCollection < AlbumModel > ( 30 , albumres ,
940+ async ( page , ptoken ) =>
941+ {
942+ var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/album/page/{ page } ?key={ keyword } ") ;
943+ ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
944+ var pcontent = await pgettask ;
945+ if ( CheckNeedLogin ( pcontent ) ) return null ;
946+ var pdoc = new HtmlDocument ( ) ;
947+ pdoc . LoadHtml ( pcontent ) ;
948+ return pdoc . DocumentNode . SelectSingleNode ( ".//ul[@class='clearfix']" )
949+ . Elements ( "li" ) . Select ( ( node ) => ParseSearchedAlbum ( node ) ) ;
950+ } ) ;
951+ else
952+ res . Albums = albumres . ToList ( ) ;
953+
938954 var artists = results [ 2 ] ;
955+ var artistres = artists . Descendant ( "ul" ) . Elements ( "li" ) . Select ( ( node ) => ParseSearchedArtist ( node ) ) ;
939956 //System.Diagnostics.Debugger.Break();
940- res . Artists = new PageItemsCollection < ArtistModel > ( 30 , artists . Descendant ( "ul" ) . Elements ( "li" ) . Select ( ( node ) => ParseSearchedArtist ( node ) ) ,
941- async ( page , ptoken ) =>
942- {
943- var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/artist/page/{ page } ?key={ keyword } ") ;
944- ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
945- var pcontent = await pgettask ;
946- var pdoc = new HtmlDocument ( ) ;
947- pdoc . LoadHtml ( pcontent ) ;
948- return pdoc . DocumentNode . SelectSingleNode ( ".//div[@class='artistBlock_list']" ) . Element ( "ul" )
949- . Elements ( "li" ) . Select ( ( node ) => ParseSearchedArtist ( node ) ) ;
950- } ) ;
957+ if ( artists . Element ( "span" ) != null )
958+ res . Artists = new PageItemsCollection < ArtistModel > ( 30 , artistres ,
959+ async ( page , ptoken ) =>
960+ {
961+ var pgettask = HttpHelper . GetAsync ( $ "http://www.xiami.com/search/artist/page/{ page } ?key={ keyword } ") ;
962+ ptoken . Register ( ( ) => pgettask . Cancel ( ) ) ;
963+ var pcontent = await pgettask ;
964+ if ( CheckNeedLogin ( pcontent ) ) return null ;
965+ var pdoc = new HtmlDocument ( ) ;
966+ pdoc . LoadHtml ( pcontent ) ;
967+ return pdoc . DocumentNode . SelectSingleNode ( ".//div[@class='artistBlock_list ']" ) . Element ( "ul" )
968+ . Elements ( "li" ) . Select ( ( node ) => ParseSearchedArtist ( node ) ) ;
969+ } ) ;
970+ else
971+ res . Artists = artistres . ToList ( ) ;
951972 if ( results . Count > 3 )
952973 {
953974 var collects = results [ 3 ] ;
@@ -965,13 +986,43 @@ public IAsyncOperation<SearchResult> Search(string keyword)
965986 } ) ;
966987 }
967988
989+ internal List < SongModel > ParseSearchedSongTable ( HtmlNode table )
990+ {
991+ string temp = table . InnerHtml ;
992+ foreach ( Match m in Regex . Matches ( temp , @"(<tbody\s)[\s\S]+?(/tbody>)" ) )
993+ {
994+ StringBuilder sb = new StringBuilder ( ) ;
995+ sb . Append ( "</tbody>" ) ;
996+ sb . Append ( m . Value ) ;
997+ sb . Append ( "<tbody>" ) ;
998+ temp = temp . Replace ( m . Value , sb . ToString ( ) ) ;
999+ }
1000+ table . InnerHtml = temp ;
1001+ List < SongModel > res = new List < SongModel > ( ) ;
1002+ foreach ( var node in table . Elements ( "tbody" ) )
1003+ {
1004+ if ( node . GetAttributeValue ( "class" , "" ) . Contains ( "same_song_group" ) )
1005+ res . AddRange ( node . Elements ( "tr" ) . Select ( ( tr ) =>
1006+ {
1007+ var song = ParseSearchedSong ( tr ) ;
1008+ song . DuplicateOf = res [ res . Count - 1 ] ;
1009+ return song ;
1010+ } ) ) ; // 不全部返回的话会造成歌曲数目不够,PageItem加载时会产生Exception
1011+ else
1012+ res . AddRange ( node . Elements ( "tr" ) . Select ( ( tr ) => ParseSearchedSong ( tr ) ) ) ;
1013+ } ;
1014+ return res ;
1015+ }
9681016 internal SongModel ParseSearchedSong ( HtmlNode tr )
9691017 {
1018+ //System.Diagnostics.Debugger.Break();
9701019 var tds = tr . Elements ( "td" ) . ToList ( ) ;
9711020 var checkbox = tds [ 0 ] . Descendant ( "input" ) ;
9721021 SongModel song = SongModel . GetNew ( uint . Parse ( checkbox . GetAttributeValue ( "value" , "0" ) ) ) ;
9731022 song . Available = checkbox . GetAttributeValue ( "checked" , "" ) == "checked" ;
974- song . NameHtml = tds [ 1 ] . Element ( "a" ) . InnerHtml ;
1023+ var links = tds [ 1 ] . SelectNodes ( "./a[@target='_blank']" ) ;
1024+ song . NameHtml = links [ 0 ] . InnerHtml ;
1025+ if ( links . Count > 1 ) song . MV = MVModel . GetNew ( ParseXiamiIDString ( links [ 1 ] . GetAttributeValue ( "href" , "/0" ) ) ) ;
9751026 var anode = tds [ 3 ] . Element ( "a" ) ;
9761027 var album = AlbumModel . GetNew ( ParseXiamiID ( anode . GetAttributeValue ( "href" , "/0" ) ) ) ;
9771028 album . NameHtml = anode . InnerHtml . Replace ( "《" , "" ) . Replace ( "》" , "" ) ;
@@ -983,7 +1034,6 @@ internal SongModel ParseSearchedSong(HtmlNode tr)
9831034 }
9841035 internal AlbumModel ParseSearchedAlbum ( HtmlNode li )
9851036 {
986- LogService . DebugWrite ( li . OuterHtml ) ;
9871037 //System.Diagnostics.Debugger.Break();
9881038 var ips = li . SelectNodes ( "./div/p" ) ;
9891039 var albumlink = ips [ 1 ] . Element ( "a" ) ;
0 commit comments