wau's headphone dust

日記とか雑記とか自分用のまとめ 音楽理論もちょっと

Webスクレイピングというものでカニの写真を集めてみた話[Python]

wauです。Webスクレイピングというものに挑戦したので、メモを残しておきます。

自己紹介

C#をさわりはじめて半年
Pythonわからん
・Html、CSSもあまりわからん

こんな私ですが、Pythonを使ってのWebスクレイピングを行ってみました。

参考:
作って学ぶPython入門 #004 「齋藤飛鳥の画像をスクレイピング」Pythonのできることの例: 画像をダウンロード。Progateが終わってから行う演習の参考にしてください。 - YouTube

やったこと

Yahooの画像検索でカニの種類であるカラッパの画像を集め、まとめて保存しました。

f:id:wauchangwau:20210513230259p:plain
カニの画像を集めました

なぜカラッパかというと、好きだからですね。

準備

Visual Studio 2019でコードを書いていきます。
そのために、インストーラーでPython開発環境をインストールしておきます。

f:id:wauchangwau:20210513204404p:plain
Python開発環境のインストール

Pythonでは「ライブラリ」というものを適宜インストールしていくことで環境を拡張することができます。
今回使った外部ライブラリはこちら。

・requests … Webサイトの情報を取得
・BeautifulSoup4 … Htmlからデータを取り出す
Selenium … ブラウザを自動操作する

その他にも、Seleniumを動作させるために必要なWebDriverをダウンロードする必要などがありました。
Python全くわからなかったのでインストールも大変でした。

Pythonでの外部ライブラリインストール(Windows環境)

コマンドプロンプトを起動、
py -m pip install "インストールするライブラリ名"
を打ち込む。これだけ。

f:id:wauchangwau:20210513211805p:plain
Pythonでの外部ライブラリインストール

py -mを外して記述すると、pipを認識してくれない模様。Pathが通ってないらしい。よくわからんけど、py -mを入れることでインストールされているPythonを呼び出したうえでインストールを実行してくれるみたい?です。

実際のコード

色んなサイトや動画を見てつなぎ合わせたコードがこちら。

import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import chromedriver_binary 
import subprocess
import time
import re

# スクレイピングするURLを記述。
pageUrl = "https://search.yahoo.co.jp/image/search?p=%E3%82%AB%E3%83%A9%E3%83%83%E3%83%91&fr=top_ga1_sa&ei=UTF-8"

# WebDriverのオプションを設定。
options = webdriver.ChromeOptions()
# options.add_argument('--headless') # コメントアウトすることでブラウザを実際に立ち上げる。
print('connectiong to remote browser...')
driver = webdriver.Chrome(options=options)

# WebDriverにURLを渡す。
driver.get(pageUrl)

# 最下段まで進んで1秒待つ、を繰り返す(ページの自動リロード)。
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(1)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

# ページのソースをエンコードしてHtmlとし、BeautifulSoupで読み込む。
html = driver.page_source.encode('utf-8')
soup = BeautifulSoup(html, 'html.parser')

# imgタグのうち、alt属性に"カラッパ"を含むものを全て集めてリストにする。
img_tags = soup.find_all("img", alt=re.compile("カラッパ"))
img_urls = []

# リストのimgタグそれぞれから画像URLを取得し、img_urlsに追加。
for img_tag in img_tags:
    url=img_tag.get("src")
    img_urls.append(url)
    print(url)

# ダウンロード関数の定義。
def download_image(url, file_path):
    # requwstsでURLからページを取得
    r = requests.get(url, stream=True)      
    # status_code == 200 というのは、ページ取得成功のこと。
    if r.status_code == 200:                
        # f = open(~)と同じ。with-asで使うことでcloseの必要がなくなる。
        # "wb"はw:書き込み用で、b:バイナリモードで開く。
        with open(file_path, "wb") as f:    
            # URLのバイナリデータをfに書き込む。つまり画像などのデータ保存となる。
            f.write(r.content)              

# subprocessモジュールでコマンドプロンプトを起動。画像を保存するディレクトリを作成。
subprocess.Popen("dir",shell=True)
subprocess.Popen("mkdir images",shell = True)

# img_urlsをforループで回して、ダウンロード関数を動かす。
for index, url in enumerate(img_urls):
    file_name = f"{index:03}.jpg"
    print(file_name)
    img_path = "images/{}".format(file_name)
    print(img_path)

    download_image(url = url, file_path = img_path)


正直よくわかってない箇所があまりに多いのでアレなんですが、ひとまずこれで画像を沢山保存することはできました。

ここにつまづいた

Pythonのライブラリインストール

上にも書きましたが、最初はここからわかりませんでした。importってコードに書けば使えるものだと思ってました。C#でいうとこのusingですね。
fromってのをつけることで、モジュール名を省略して使えるようになるそうで。この辺もC#で似たようなのをやった気がします。

画面に表示されてる分しか画像が保存できない

Yahooの画像検索の仕様で、下にスクロールすることである程度画像を追加読み込みしてくれます。これを疑似的に動かすために、webdriverのメソッドで一番下までスクロールさせて、timeのメソッドで1秒待つ。を何度か行いました。もう少しスマートにいくと思うんだけどな。

HTMLタグと属性

imgタグが付いてるものは画像!というのは感覚でわかりました。
alt属性には画像の情報をテキストで書くものというのも聞いたことがあったので、そこに対象となる文字列(ここでは"カラッパ")を含むものをピックアップする…という書き方をするのにすこしつまづきました。
re.compileというメソッドを使うのですが、このreもimportしなきゃいけないんですね…わかりにくい……

forループの書き方

C#でいうとこのforeachに近いんだなという印象でした。また、indexを追記することでインデックスを同時に参照することもできるみたいです。これは便利。

コマンドの使い方

windowsではLinux,Unixのシェルコマンド?を使うことはできないので、Windowsで使われているコマンドを使う必要があります。それは当然なんだけど、これをどう使うか?最初はよくわかりませんでした。
結果、
subprocess.Popen("コマンド", shell=True)
と書く必要がありました。subprocessはimportしたうえで使います。
ちなみにPopen()のかわりにrun()でもよさそうです。違いは非同期処理か同期処理かだそうですが、なんのことやら…この用法だったらrun()の方でも良さそうな気もしますね。

ダウンロード関数の定義

コピペして動かしてから、なんで動いてんのかわからないな…?となったので調べました。
requests.status_codeってのはいわゆる404とか503とかのやつですね。200は成功を意味するそうです。
Pythonにおけるwithってのが最初はよくわからなかったですね。openで開いたファイルはcloseで閉じる必要があるらしく、withをつけてあげることでそのcloseを省略できるとのことでした。
そして画像保存の仕組みはバイナリデータの書き込みだそうです。なるほど…?

5/16追記 chromedriverのパス

chromedriverを実行する際にパスを指定する必要があります。
driver = webdriver.Chrome(~ここにパスを指定~)
ですが今回は記述してません。それなのに動いた理由は、
import chromedriver_binary
のおかげだそうです。理由は今一つわからないけど、ここを省略できるのは今後大きいなと思いました。

おわりに

Pythonわからん、Htmlもよくわからん。
でもググりにググって情報をかき集めることで、なんとかものになったので満足です。
今後はこれを応用できればなおよしですね。何ができるかを調べるのもよさそう。では。