「いらすとや」の画像をスクレイピングで自動収集してみた

かの有名な「いらすとや」さんでスクレイピングによる画像収集を試した記録.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/296645/ef8307e3-95eb-2c8c-4ff0-39ae4f2c5a10.png)
いらすとや
https://www.irasutoya.com/
#やったこと
Pythonを用いたWebスクレイピングによって「いらすとや」の職業カテゴリの画像を自動でダウンロードし,ファイルに保存した.
![スクリーンショット (82).png](https://qiita-image-store.s3.amazonaws.com/0/296645/0bbcd258-48f9-76c9-d833-7e05020c2467.png)
↑ファイルに格納したイメージ
#スクレイピングの基礎知識
リンク参照

スクレイピングとは：https://www.fascinatedwithtofu.com/2017/01/08/scraping1/
実装における注意事項：https://qiita.com/nezuq/items/c5e827e1827e7cb29011
#開発環境
言語: Python 3.6.5
フレームワーク: Jupyter Notebook
OS: Windows 10
ブラウザ: Google Chrome 
#サンプルコード
```py

import re
import urllib.request as urlreq
from bs4 import BeautifulSoup

pages = range(1, 6)
linkData = []

for p in pages:
    if p == 1:
        r = urlreq.Request('https://www.irasutoya.com/search/label/%E8%81%B7%E6%A5%AD')
        with urlreq.urlopen(r) as r:
            r = r.read()
        soup = BeautifulSoup(r, 'lxml')
        links = soup.select("a[href]")
        for a in links:
            href = a.attrs['href']
            if re.search('irasutoya.*blog-post.*html$',href):
                if not href in linkData:
                    linkData.append(href)
                    print(href)
            if re.search('max-results=20&start=20&by-date=false$',href):
                r = href
                break
        for link in linkData:
            res = requests.get(link)
            soup = BeautifulSoup(res.text, "lxml")
            links = soup.select(".separator > a")
            for a in links:
                imageLink = a.get('href')
                filename = re.search(".*\/(.*png|.*jpg)$",imageLink)
                try: 
                    urlreq.urlretrieve(imageLink,"C:/Users/***/Pictures/illust_ya/"+filename.group(1))
                    print(imageLink)
                except ValueError:
                    print("ValueError!")
    else:
        r = urlreq.Request(r)
        with urlreq.urlopen(r) as r:
            r = r.read()
        soup = BeautifulSoup(r, 'lxml')
        links = soup.select("a[href]")
        for a in links:
            href = a.attrs['href']
            if re.search('irasutoya.*blog-post.*html$',href):
                if not href in linkData:
                    linkData.append(href)
                    print(href)
            if re.search('max-results=20&start='+str(p*20)+'&by-date=false$',href):
                r = href
                break
        for link in linkData:
            res = requests.get(link)
            soup = BeautifulSoup(res.text, "lxml")
            links = soup.select(".separator > a")
            for a in links:
                    imageLink = a.get('href')
                    filename = re.search(".*\/(.*png|.*jpg)$",imageLink)
                    try: 
                        urlreq.urlretrieve(imageLink,"C:/Users/***/Pictures/illust_ya/"+filename.group(1))
                        print(imageLink)
                    except ValueError:
                        print("ValueError!")
```
以下サンプルコードを詳しく説明
#必要なライブラリのインポート
・re・・・正規表現を扱うためのライブラリ.「いらすとや」のWebサイトのソースコードから,画像をスクレイピングするのに必要なHTMLタグの部分を抽出するために必要.
・urllib.request・・・「いらすとや」の画像などのURLを開いたり、URLから画像をダウンロードするために必要なライブラリ.
・BeautifulSoup・・・HTMLやXMLファイルからデータを習得するライブラリ.HTMLタグからURLを抜き出すために必要.
pipでインストール
```$ pip install beautifulsoup4```

(補足)PythonではPyQueryやScrapyなどのライブラリでもスクレイピング可能.

```py
import re
import urllib.request as urlreq
from bs4 import BeautifulSoup
```
#コードのだいたいの流れ
1.職業カテゴリの画像一覧ページ(1ページ目)から各画像のリンクを取得しリストに追加
![スクリーンショット (83).png](https://qiita-image-store.s3.amazonaws.com/0/296645/2c40b253-b5bf-f6c0-1df5-286369122a7e.png)
2.取得した画像リンクのリストから各画像のページを開き、画像をダウンロード
![スクリーンショット (84).png](https://qiita-image-store.s3.amazonaws.com/0/296645/74973f10-0488-d4b3-adca-a0a11f056d63.png)
3.画像一覧ページ(1ページ目)の画像をすべてダウンロードしたら、画像一覧ページの2ページ目に移動し同様に画像をダウンロードしていく
4.画像一覧ページの5ページまで繰り返す
#1.職業カテゴリの1ページ目から各画像のリンクを取得
```py

pages = range(1, 6)
# 5ページ目まで画像一覧ページを開くためにリストを定義
linkData = []
# 画像一覧ページから各画像のリンクを取得して格納するリストを定義
for p in pages:
    if p == 1:
    # 1ページ目
        r = urlreq.Request('https://www.irasutoya.com/search/label/%E8%81%B7%E6%A5%AD')
        # 「いらすとや」の職業カテゴリのリンクを取得
        with urlreq.urlopen(r) as r:
            r = r.read()
        # リンクを開き読み込む
        soup = BeautifulSoup(r, 'lxml')
        # Webページのソースコードをパース
        links = soup.select("a[href]")
        # href=タグのついたリンクをすべて取得
        for a in links:
            href = a.attrs['href']
            #<a href='x'></a>のxを取得
            if re.search('irasutoya.*blog-post.*html$',href):
            # 取得したリンクが画像リンクかどうか
                if not href in linkData:
                # 取得したリンクがlinkData[]にないか確認
                    linkData.append(href)
                    # 取得したリンクをリストに追加
                    print(href)
                    # デバッグ用にコンソールに表示
            if re.search('max-results=20&start=20&by-date=false$',href):
            # 画像一覧ページ(2ページ目)のリンクを取得
                r = href
                # 画像一覧ページのリンクを1ページ目から2ページ目に更新
                break
                # 2ページ目のリンクより下にも画像ページのリンクを取得しない
```
画像一覧2ページ目のリンクを取得した後,breakでfor文を終了させている理由は2ページ目のリンクより下には職業カテゴリでない画像のリンクが置いてあったので,必要でない画像リンクを取得することを防ぐため.
(これはWebページのHTMLの構造による.「いらすとや」以外のページに当てはまるとは限らない)
#2.各画像のページを開き、画像をダウンロード
```py

for link in linkData:
            res = requests.get(link)
            # 先ほど作成した画像リンクリストから各画像リンクを取得
            soup = BeautifulSoup(res.text, "lxml")
            # Webページのソースコードをパース
            links = soup.select(".separator > a")
            #<a></a>を取得
            for a in links:
                imageLink = a.get('href')
                # <a href='x'></a>のxを取得
                filename = re.search(".*\/(.*png|.*jpg)$",imageLink)
                # 画像ファイル名をfilenameに格納
                try: 
                    urlreq.urlretrieve(imageLink,"C:/Users/***/Pictures/illust_ya/"+filename.group(1))
                    # 画像リンクをダウンロードしフォルダに格納
                    print(imageLink)
                    # デバッグ用に画像リンクを表示
                except ValueError:
                    print("ValueError!")
                    # ValueErrorにより画像がダウンロードできなかった場合に表示
```
例外処理を実装している理由はちょいちょい画像によってはunknown url type:という謎のエラーが出てValueErrorとなりプログラムが止まってしまったのでそれを防ぐため.
#3.画像一覧ページの5ページまで繰り返す
if文で1ページ目とそれ以外の処理を分け、for文を回す
#雑感
・自動化のためにスクレイピングした別のページはページ遷移がp=2とか非常に単純だったが,「いらすとや」はupdate-max=が挿入されているため,シンプルなやり方ではうまくいかずえらい苦戦した.
いらすとやの2ページ目のリンク↓
https://www.irasutoya.com/search/label/%E8%81%B7%E6%A5%AD?updated-max=2018-07-26T14:00:00%2B09:00&max-results=20&start=20&by-date=false

・参考文献にある「いらすとや」のスクレイピングでは上のようなリンクに対応していなかったため今回の記事で対応できたと思っているが,無駄が多いプログラムのため実行速度が遅い.改善の余地あり

・初心者すぎて例外処理の概念を知らず,エラーを解決するのに10時間くらい格闘した.例外処理を実践的に勉強するいい機会になったが死にかけた.

・スクレイピングはWebページのHTMLソースコードの整頓具合によって難易度が結構変わることを実感.
#参考文献
・「いらすとや」さんのwebサイトのスクレイピング
https://kumanabe.com/%E3%80%8C%E3%81%84%E3%82%89%E3%81%99%E3%81%A8%E3%82%84%E3%80%8D%E3%81%95%E3%82%93%E3%81%AEweb%E3%82%B5%E3%82%A4%E3%83%88%E3%81%AE%E3%82%B9%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%94%E3%83%B3%E3%82%B0/
・PythonとBeautiful Soupでスクレイピング
https://qiita.com/itkr/items/513318a9b5b92bd56185#html%E3%83%91%E3%83%BC%E3%82%B5%E3%83%BC%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
・Python基礎講座(11 例外)
https://qiita.com/Usek/items/53527feba2adcb386aa8
・urllib パッケージを使ってインターネット上のリソースを取得するには
https://docs.python.jp/3/howto/urllib2.html
・Beautiful Soup
http://kondou.com/BS4/