/// <summary> /// 從YtCfg字串解析inner context資料供取得留言使用 /// </summary> /// <param name="strCfg"></param> /// <returns></returns> private InnerTubeContextData ParseInnerContextData(string strCfg) { InnerTubeContextData ret = new InnerTubeContextData(); dynamic jsonData = JsonConvert.DeserializeObject <Dictionary <String, dynamic> >(strCfg); ret.INNERTUBE_API_KEY = Convert.ToString(JsonHelper.TryGetValue(jsonData, "INNERTUBE_API_KEY", "")); ret.context.clickTracking.clickTrackingParams = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.clickTracking.clickTrackingParams", "")); ret.context.request.useSsl = Convert.ToBoolean(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.request.useSsl", false)); ret.context.client.browserName = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.browserName", "")); ret.context.client.browserVersion = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.browserVersion", "")); ret.context.client.clientFormFactor = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.clientFormFactor", "")); ret.context.client.clientName = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.clientName", "")); ret.context.client.clientVersion = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.clientVersion", "")); ret.context.client.deviceMake = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.deviceMake", "")); ret.context.client.deviceModel = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.deviceModel", "")); ret.context.client.gl = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.gl", "")); ret.context.client.hl = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.hl", "")); ret.context.client.originalUrl = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.originalUrl", "")); ret.context.client.osName = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.osName", "")); ret.context.client.osVersion = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.osVersion", "")); ret.context.client.platform = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.platform", "")); ret.context.client.remoteHost = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.remoteHost", "")); ret.context.client.userAgent = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.userAgent", "")); ret.context.client.visitorData = Convert.ToString(JsonHelper.TryGetValueByXPath(jsonData, "INNERTUBE_CONTEXT.client.visitorData", "")); return(ret); }
/// <summary> /// 取得第一次訪問的cookie資料,並在html中取出ytcfg資料與window["ytInitialData"] 後方的json code,並解析出continuation /// </summary> /// <param name="liveChatUrl">聊天室位址</param> /// <returns>回傳continuation參數值</returns> private List <CommentData> ParseLiveChatHtml(String liveChatUrl, ref String continuation) { String htmlContent = ""; List <CommentData> initComments = new List <CommentData>(); RaiseStatusChanged(CommentLoaderStatus.GetLiveChatHtml); CookieContainer cc = new CookieContainer(); var handler = new HttpClientHandler { UseCookies = true, CookieContainer = cc, }; //取得HTML內容 using (HttpClient client = new HttpClient(handler)) { try { client.DefaultRequestHeaders.Add("User-Agent", @"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"); htmlContent = client.GetStringAsync(liveChatUrl).Result; _cookieContainer = handler.CookieContainer; } catch (Exception e) { Debug.WriteLine(String.Format("[ParseLiveChatHtml] 無法取得聊天室HTML內容. Excetion:{0}", e.Message)); RaiseError(CommentLoaderErrorCode.CanNotGetLiveChatHtml, e.Message); return(null); } } //解析YtCfg RaiseStatusChanged(CommentLoaderStatus.ParseYtCfgData); string strCfg = ParseYtCfg(htmlContent); if (strCfg == null) { Debug.WriteLine(String.Format("[ParseLiveChatHtml] 無法解析YtCfg. HTML content:{0}", htmlContent)); RaiseError(CommentLoaderErrorCode.CanNotParseYtCfg, htmlContent); return(null); } //解析inner context data _innerContextData = ParseInnerContextData(strCfg); //解析HTML RaiseStatusChanged(CommentLoaderStatus.ParseLiveChatHtml); Match match = Regex.Match(htmlContent, "window\\[\"ytInitialData\"\\] = ({.+});\\s*</script>", RegexOptions.Singleline); if (!match.Success) { Debug.WriteLine(String.Format("[ParseLiveChatHtml] 無法解析HTML. HTML content:{0}", htmlContent)); RaiseError(CommentLoaderErrorCode.CanNotParseLiveChatHtml, htmlContent); return(null); } //解析json data String ytInitialData = match.Groups[1].Value; dynamic jsonData; try { jsonData = JsonConvert.DeserializeObject <Dictionary <String, dynamic> >(ytInitialData); var data = jsonData["contents"]["liveChatRenderer"]["continuations"][0]["invalidationContinuationData"]; if (data == null) { data = jsonData["contents"]["liveChatRenderer"]["continuations"][0]["timedContinuationData"]; } continuation = Convert.ToString(JsonHelper.TryGetValue(data, "continuation", "")); _innerContextData.continuation = continuation; var actions = jsonData["contents"]["liveChatRenderer"]["actions"]; initComments = ParseComment(actions); } catch (Exception e) { Debug.WriteLine(String.Format("[ParseLiveChatHtml] 無法解析json data:{0}", e.Message)); RaiseError(CommentLoaderErrorCode.CanNotParseLiveChatHtml, ytInitialData); return(null); } return(initComments); }