はじめに
今回はGoogleNewsAPIを使ってニュースを取得してみる記事になります!
とある用事でUnity内でニュースを取得するスクリプトを書いてみました。
スクリプト
NewsReader.cs
using System; using System.Collections.Generic; using System.Collections; using UnityEngine; namespace Hanachiru.News { public static class NewsReader { private static readonly string GOOGLE_NEWS_API_URL = "https://news.google.com/news/rss/{0}?hl=ja&gl=JP&ned=jp"; private static readonly string SERCH_URL = "search/section/q/{0}/{0}/"; /// <summary> /// GoogleNewsAPIを使ってニュース(タイトル・概要・リンク)を読み込む /// </summary> /// <param name="onFinishReading">読み込み終了後のコールバック</param> /// <param name="keywords">検索キーワード</param> public static IEnumerator ReadNews(Action<NewsData> onFinishReading, IReadOnlyCollection<string> keywords = null) { string title = null; string description = null; string link = null; string news = null; bool isFinished = false; //GoogleNewsAPIでXmlを取得 NewsTools.GetDataFromNet(KeywordsToURL(keywords), (s, flag) => { news = s; isFinished = flag; }); yield return new WaitUntil(() => isFinished); //newsからタイトル・概要・newsDetailURLの抽出 var root = NewsTools.ParseXml(news); title = NewsTools.ExtractDataFromXML(root, "title", true); title = NewsTools.RemoveHtmlTags(title, true); description = NewsTools.ExtractDataFromXML(root, "description", true); description = NewsTools.RemoveHtmlTags(description, true); link = NewsTools.ExtractDataFromXML(root, "link", true); onFinishReading(new NewsData(title, description, link)); } /// <summary> /// keywordのコレクションからGoogleNewsAPI用URLに変換 /// </summary> private static string KeywordsToURL(IReadOnlyCollection<string> keywords) { if (keywords == null) return String.Format(GOOGLE_NEWS_API_URL, ""); string serchText = ""; foreach (var keyword in keywords) { serchText += NewsTools.EncodeUrl(keyword) + " "; } return String.Format(GOOGLE_NEWS_API_URL, String.Format(SERCH_URL, serchText)); } } public class NewsData { public string Title { get; } public string Description { get; } public string Link { get; } public NewsData(string title, string description, string link) { Title = title; Description = description; Link = link; } } }
NewsTools.cs
using System; using System.Text.RegularExpressions; using System.Linq; using System.Net; using System.IO; using UniRx; namespace Hanachiru.News { public static class NewsTools { private const string PATTERN_FOR_XML_OF_GOOGLE_API = "[( )(­)(&ensp)(&thinsp)(ニュースですべての記事を表示)(;)(|)]"; /// <summary> /// 指定したURLからJsonやXml(rss)などのデータを取得する /// </summary> /// <param name="url">対象のURL</param> /// <param name="onFinishReading">入手したデータ・終了を知らせるフラグを引数にもつコールバック</param> public static void GetDataFromNet(string url, Action<string, bool> onFinishReading) { //別スレッドで実行 Observable.Start(() => { try { var req = (HttpWebRequest)WebRequest.Create(url); var res = (HttpWebResponse)req.GetResponse(); using (var reader = new StreamReader(res.GetResponseStream())) { return reader.ReadToEnd(); } } catch { return ""; } }) .ObserveOnMainThread() //メインスレッドに切り替え .Subscribe(data => { onFinishReading(data, true); }); } /// <summary> /// テキストをUTF-8に変換する(URLエンコード) /// </summary> public static string EncodeUrl(string text) { if (string.IsNullOrEmpty(text)) return ""; return Uri.EscapeDataString(text); } /// <summary> /// テキストからHTMLタグを削除する /// </summary> public static string RemoveHtmlTags(string text, bool shouldDeleteSpecialCharacter = false) { if (string.IsNullOrEmpty(text)) return ""; string s = RemoveCharacter(text, "<[^>]*?>"); if (!shouldDeleteSpecialCharacter) return s; return RemoveCharacter(s, PATTERN_FOR_XML_OF_GOOGLE_API); } /// <summary> /// テキストから正規表現を用いて文字列を抽出する /// </summary> /// <param name="text">テキスト</param> /// <param name="pattern">正規表現</param> public static string ExtractCharacter(string text,string pattern) { if (string.IsNullOrEmpty(text)) return ""; return Regex.Match(text, pattern).Value; } /// <summary> /// テキストから正規表現を用いて文字列を削除 /// </summary> /// <param name="text">テキスト</param> /// <param name="pattern">正規表現</param> public static string RemoveCharacter(string text,string pattern) { if (string.IsNullOrEmpty(text)) return ""; return Regex.Replace(text, pattern, string.Empty); } public static System.Xml.Linq.XElement ParseXml(string data) { if (string.IsNullOrEmpty(data)) return null; System.Xml.Linq.XDocument xml = System.Xml.Linq.XDocument.Parse(data); System.Xml.Linq.XElement root = xml.Root; return root; } /// <summary> /// Xml形式データから指定のタグのデータを抽出する /// </summary> public static string ExtractDataFromXML(System.Xml.Linq.XElement root, string tag, bool shouldSkip = false) { if (root == null) return ""; var targetData = root.Descendants(tag); if (shouldSkip) { return targetData.Select(x => x.Value) .Skip(1) .FirstOrDefault(); } return targetData.Select(x => x.Value) .FirstOrDefault(); } } }
使い方
StartCoroutine(NewsReader.ReadNews(news => { Debug.Log("title: " + news.Title); Debug.Log("description: " + news.Description); Debug.Log("link: " + news.Link); }));
※この画像のリンクが間違っていたのですが、コードは修正をしておきました。
さいごに
もっと良いニュースであふれた世界になりますように…。
追記)KeywordsToURLメソッド内でキーワードを繋ぐためにスペースを利用していますが、ちゃんとこれもURLエンコードした方がいいです。あとで時間があれば直しておきますね。