面白そうな動画があったのでC#でできるかやってみました。
2019/10/26の動画です。1年以上前の二番煎じです。
Yahoo画像検索を使いますが、ページが例のごとくJavascriptによって描画されるようになっています。そのためHTMLを単純に解析するだけではダメです。ブラウザ自動操作ライブラリ「Selenium」を使います。
まずimgタグを取得します。そして一番最後のimgタグの座標をしらべてスクロールさせます。またスクロールを繰り返すと「もっと見る」というボタンが表示されます。そこで
一番最後のimgタグの座標をしらべてスクロール
「もっと見る」というボタンがあればクリック
を繰り返します。ボタンをクリックする処理は例外が発生する場合があるのでtry-catch文を使います。引数のmaxは最大取得数です(実際にやってみると600件ちょっとで「これ以上表示できません」と表示される。最大取得数を設定していないからといって無限に取得できるわけではない)。
NuGetでSelenium.WebDriverとSelenium.Support、そして操作したいブラウザのDriverをインストールします。今回はChromeを操作するのでSelenium.Chrome.WebDriverをインストールします。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | using OpenQA.Selenium; using OpenQA.Selenium.Chrome; public partial class Form1 : Form {     private void button1_Click(object sender, EventArgs e)     {         driver = new ChromeDriver();         driver.Url = "https://search.yahoo.co.jp/image/search?p=%E9%BD%8B%E8%97%A4%E9%A3%9B%E9%B3%A5";         System.Threading.Thread.Sleep(3000);         // 100件取得する         List<string> urls = GetImageUrls(100);     }     List<string> GetImageUrls(int max)     {         List<string> urls = new List<string>();         List<string> vs = new List<string>();         while (true)         {             string source = driver.PageSource;             List<string> urls2 = new List<string>();             var elms = driver.FindElementsByTagName("img");             StringBuilder sb = new StringBuilder();             foreach (var elm in elms)             {                 string id = elm.ToString();                 if (vs.Any(x => x == id))                     continue;                 vs.Add(id);                 urls2.Add(elm.GetAttribute("src"));             }             urls.AddRange(urls2);             // 新しく取得できたものがない場合は終了             if (urls2.Count == 0)                 return urls;             // 最大取得数に達しているなら終了             if (urls.Count > max)                 return urls.Take(max).ToList();             if (urls.Count == max)                 return urls;             // 一番下までスクロールさせる             int y = elms.Last().Location.Y;             string script = string.Format("window.scrollBy(0, {0})", y);             driver.ExecuteScript(script);             // ページが読み込まれるまで待つ             System.Threading.Thread.Sleep(3000);             //「もっと見る」というボタンをクリック。例外対策が必要             try             {                 var showMore = driver.FindElementByClassName("sw-MoreButton__button");                 if (showMore != null)                 {                     showMore.Click();                 }             }             catch             {             }         }     } } | 
上記コードでurlsのなかに画像のurlが格納されるので、ここから画像をダウンロードしてファイルとして保存します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | public partial class Form1 : Form {     private void button2_Click(object sender, EventArgs e)     {         driver = new ChromeDriver();         driver.Url = "https://search.yahoo.co.jp/image/search?p=%E9%BD%8B%E8%97%A4%E9%A3%9B%E9%B3%A5";         System.Threading.Thread.Sleep(3000);         List<string> urls = GetImageUrls(100);         urls = urls.Distinct().ToList(); // 同一urlを排除         driver.Quit();         driver.Dispose();         // 保存するフォルダを選択する         FolderBrowserDialog dialog = new FolderBrowserDialog();         dialog.ShowDialog();         string path = dialog.SelectedPath;         dialog.Dispose();         int i = 0;         foreach (string url in urls)         {             i++;             string filePath = String.Format("{0}\\{1}.jpg", path, i.ToString());             GetJpgFile(filePath, url);         }     }     // 指定したファイルパスに画像ファイルを保存する     public static void GetJpgFile(string filePath, string url)     {         var ms = new System.IO.MemoryStream();         try         {             var req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);             var res = (System.Net.HttpWebResponse)req.GetResponse();             var st = res.GetResponseStream();             Byte[] buf = new Byte[1000];             while (true)             {                 int read = st.Read(buf, 0, buf.Length);                 if (read > 0)                     ms.Write(buf, 0, read);                 else                     break;             }             System.IO.File.WriteAllBytes(filePath, ms.ToArray());         }         catch         {         }     } } | 
