自分だけのクイズ作成 - Quipha公開中

【Python】スクレイピング(基本から実践) + docker(おまけ)

Python
スポンサーリンク

はじめに

今回はPythonでスクレイピングを行ってみたいと思います。

皆さんWebサイトのデータを収集したいときはどうしますか?

例えば
  • 株価などのデータ取得
  • オンラインショップの価格比較・変動時に通知
  • 特定のサイトの更新通知
  • 検索結果をCSVダウンロード
  • 定期的にデータを取得して機械学習やディープラーニングに使用

などなど、普段ブラウザでアクセスするページから、直接データを取得したい時ってありますよね!

それらのデータを、ブラウザを開いていちいちコピペで収集・・というのは現実的ではありません。
そこでPythonを使い、Webサイトのデータを自動的に取得してみましょう!

スクレイピングとは

ウェブスクレイピング(Web scraping)とは、ウェブサイトから情報を収集する技術のことです。

身近なところでは、家計簿アプリでも使われているでしょう。
(Zaimやマネーフォワードなど)

連携する金融機関が、以降に説明したAPIに対応していない場合は、スクレイピングでデータを取得していると思います。
金融機関と連携するには、事前にID/パスワードを登録しますよね?
要はユーザーの代わりに、金融機関のサイトにログインして、必要な情報を取得しているのです。

APIについて

ちなみに、サイトにAPIが用意されている場合はスクレイピングの必要はありません

APIとはApplication Programming Interfaceの略で、外部のプログラムからデータのやり取りに利用できるものです。

例えば、APIでお天気情報が取得できるサイトがあります。

APIはサイトが公式に用意している手段ですので、仕様が変わる可能性も低く合法的に取得できますので、この仕組みを使うのが一番良いです。

スクレイピングは、APIなどでデータが取得できない場合に、ウェブサイトのページの内容を解析してデータを取得します。

スクレイピングの注意点

スクレイピングは、ウェブページの内容(HTML)を解析する性質上、対象のウェブページの構成が変わった場合に、スクレイピングのプログラムを修正する必要があります。

また違法かどうかも注意が必要です。

  • 対象サイトが、スクレイピングを禁止している
  • 取得したデータの著作権の問題
  • サーバに過度に負荷をかける

ウェブサイトにスクレイピングする際は各自でご確認ください。

スクレイピングが直接の原因だったわけではありませんが、参考までに過去にも事件があります。

岡崎市立中央図書館事件 - Wikipedia

前置きが長くなりましたが、実際にプログラムを作ってみたいと思います。

ソースコードについて

今回使用するソースコードはgithubにて公開しています。

GitHub - chigusa-web/PythonScrapingSample: Pythonでスクレイピング
Pythonでスクレイピング. Contribute to chigusa-web/PythonScrapingSample development by creating an account on GitHub.

環境

以下の環境があれば、WindowsでもLinux系でも構いません。

  • Python3
  • beautifulsoup4
  • html5lib

それぞれのライブラリはpipでインストールできます。

pip3 install beautifulsoup4
pip3 install html5lib

dockerイメージを使う

以前の記事で、dockerでスクレイピング環境を作る記事を公開しました。

以降、このコンテナを使用した手順も合わせて説明しますが、Pythonが実行できる環境が既にある場合は、dockerは必要ありませんので、この説明は飛ばして下さい。

手元にdockerがあれば、それを使う方法が便利です。
dockerを利用すると、環境を用意するのが楽です。

イメージは以下に公開しております。

Docker Hub

dockerコマンドで、イメージをダウンロードします。

docker pull chigusaweb/python-scraping

以下のコマンドで、dockerコンテナを作成しました。

docker run -it -v D:\Docker\share:/root/share --name py-sc chigusaweb/python-scraping:latest /bin/bash

私はWindowsのdockerを使用し、「D:\Docker\share」を共有フォルダとしました。
そのフォルダの中に、Pythonファイルの作成や、HTMLファイルやTXTファイルの保存をしていきます。

コンテナの中に入った後、カレントディレクトリを移動します。
このディレクトリがWindowsと共有されています。

cd /root/share/
スポンサーリンク

HTMLファイルダウンロード

それではスクレイピングを始めましょう。
まずは、対象ページのHTMLファイルをダウンロードします。

一度に「サイトにアクセス→HTML解析」のような手順で解説しているサイトが多いですが、実際にHTMLの解析については、何度かトライアンドエラーを繰り返します。

その都度、サイトにアクセスすると迷惑がかかりますし、効率がよくありません

そのため、対象サイトのページのHTMLファイルを一度ダウンロードし、以降はそのHTMLファイルを読み込んで解析処理を作っていきます。
ダウンロードしたHTMLファイルを何度も解析する分には、サイトには迷惑かけませんからね!

2021/09/26追記:これ以降の説明は、リニューアル前の本サイトに対してスクレイピングを行いました。
サイトの構造が変わったため、ソースを試すことはできませんが、参考としてください。

HTMLファイルをダウンロードするPythonファイルを作成します。
前述のdockerイメージを使用している場合は、Windowsの共有フォルダの中にPythonファイルを作成します。

例:D:\Docker\share\donwload.py

ソースコードは以下です。UTF-8で保存してください。

import os
from urllib.request import *

print("ダウンロード開始")

# HTMLファイル 保存先のディレクトリ
save_dir = os.path.dirname(os.path.abspath(__file__)) + "/html/"

# 存在しなければディレクトリ作成
if not os.path.exists(save_dir): 
    os.mkdir(save_dir)

# htmlをダウンロードするURL
# ここでは千草ウェブのトップページ
download_url = "https://chigusa-web.com/"

# 保存先
save_file = save_dir + "/chigusa.html"

# ダウンロード
urlretrieve(download_url, save_file)

print("ダウンロード完了")

Pythonファイルを保存しましたら、実行します。

dockerのコンテナへ入っている状態で、ファイルがあるか確認します。

root@896a267e9c03:~/share# cd /root/share/
root@896a267e9c03:~/share# ls
download.py

作成したPythonを実行します。

root@896a267e9c03:~/share# python download.py
ダウンロード開始
ダウンロード完了

Pythonファイルと同じ階層に、htmlフォルダが作成され、中にhtmlファイルが作成されます。
dockerコンテナを使用して実行した場合は、Windowsの以下にファイルが作成されます。

例:D:\Docker\share\html\chigusa.html

上記のHTMLファイルをテキストエディタで確認してみてください。このサイトのトップページのHTMLになります。
引き続きこのHTMLファイルを解析していきます。

今回の例では単一のURLでしたが、例えば検索URLであれば、getパラメータを動的に変えて取得といったことも可能かと思います。

HTMLの解析

次に、先程ダウンロードしたHTMLファイルを読み込み、解析を行っていきます。
本サイトのトップページを例としています。

文字列をピンポイントで取得

以下のタイトルの文字列を取得してみます。

まずは該当の文字列の場所を特定します。
実際はHTMLを解析しますので、特定方法はCSSのセレクターを取得します。

ブラウザのFirefoxを使用します。
(Chromeや他のブラウザですと以降の手順で上手くできません)

Firefoxで該当のURLを開きます。(今回は本サイトのトップページ)

F12キーを押し、開発ツールを起動します
起動後、以下の赤枠の矢印マークをクリックします。

ブラウザの方に戻り、取得したい文字列を選択しクリックします。

開発ツールに戻り、選択された文字列を右クリックし、コピー→CSSセレクターをクリックします。

以下のような文字列がクリップボードに格納されました。
今回のページの中で、取得したい文字列の場所を特定するものです。

.entry-body > h2:nth-child(4)

上記の文字列が分かれば、Python側で文字列を取得してみたいと思います。

処理内容としては、先にダウンロードしたHTMLファイルの中身を読み込み、HTMLを解析することによって、ピンポイントで文字列を取得します。
HTMLの解析にはBeautifulSoupを使用し、該当の文字列を取得する際に先に取得したCSSセレクターを指定しています。

import os
from bs4 import BeautifulSoup

print("HTML解析開始")

# HTMLファイル 保存先のディレクトリ
save_dir = os.path.dirname(os.path.abspath(__file__)) + "/html/"
# HTMLファイルパス
html_file = save_dir + "/chigusa.html"

# ファイルの読み込み
with open(html_file, encoding='utf-8') as f:

    bsoup = BeautifulSoup(f, "html5lib")

    # HTMLから該当の文字を取得(CSSセレクターを指定)
    ele = bsoup.select_one(".entry-body > h2:nth-child(4)")

    if ele is None:
        print("見つかりませんでした")
    else:
        print("見つかりました:" + ele.string)

print("HTML解析終了")

それではPythonを実行してみましょう。
「Cliborとは」という文字列を取得できましたね!

root@896a267e9c03:~/share# python analysis1.py
HTML解析開始
見つかりました:Cliborとは
HTML解析終了

繰り返し要素を複数取得

次は、複数の要素を取得してみたいと思います。

例えばTableタグなど、複数のデータがある場合です。
(データ数が決まっていればピンポイントで取得できますが、データ数が可変の場合)

例として、このサイトのトップページの、ブログ記事一覧の各タイトルを取得したいと思います。

先程同様に、Firefoxの開発ツールでCSSセレクターを探しましょう。
CSSのclassで良さげなものがあればよいのですが、今回は少し難しそうです。

まずはブログの記事一覧のエリアのCSSセレクターを取得します。

CSSセレクターは以下です。

div.postList:nth-child(4)

HTMLを見てみると、このエリアの中のh1タグを全て取得し、さらにその中のaタグの値を取得すると、タイトルが取れそうです。
プログラムは以下のようにしました。

import os
from bs4 import BeautifulSoup

print("HTML解析開始")

# HTMLファイル 保存先のディレクトリ
save_dir = os.path.dirname(os.path.abspath(__file__)) + "/html/"
# HTMLファイルパス
html_file = save_dir + "/chigusa.html"

# ファイル読み込み
with open(html_file, encoding='utf-8') as f:

    bsoup = BeautifulSoup(f, "html5lib")

    # HTMLから該当の範囲を取得
    ele = bsoup.select_one("div.postList:nth-child(4)")

    if ele is None:
        print("見つかりませんでした")
    else:
        # h1タグのリストを取得
        allTitles = ele.find_all("h1")
        for h1 in allTitles:
            # h1の中のaタグの表示文字列を取得
            print(h1.select_one("a").string)

print("HTML解析終了")

実行してみると以下のようになり、上手く取得ができました!

root@896a267e9c03:~/share# python analysis2.py
HTML解析開始
ラズパイで温度を取得(デジタル温度センサADT7310)
Alexaのスキルを手軽に作ってみた(Alexa Skill Blueprints)
ラズパイとNode-REDを使って簡単にLチカ!
ラズパイにNode-REDをインストール
ラズパイZeroでLチカ!(Python編)
HTML解析終了

find_allを使用すると、Tableタグのtrタグを複数取得するのも可能です。

このようにHTMLの中身を見ながら、色々考え試してみることが大事です。

スクレイピング結果をテキストファイルに保存

取得した情報を、テキストファイルに保存しましょう。
先程のタイトル一覧を保存するプログラムを作成しました。

import os
from bs4 import BeautifulSoup

print("HTML解析開始")

# HTMLファイル 保存先のディレクトリ
save_dir = os.path.dirname(os.path.abspath(__file__)) + "/html/"
# HTMLファイルパス
html_file = save_dir + "/chigusa.html"

# 保存先テキストファイル
save_file = os.path.dirname(os.path.abspath(__file__)) + "/save.txt"
if os.path.exists(save_file):
    os.remove(save_file)

# ファイルの読み込み・書き込み
with open(save_file, 'a', encoding='utf-8') as fw, open(html_file, encoding='utf-8') as f:

    bsoup = BeautifulSoup(f, "html5lib")

    # HTMLから該当の範囲を取得
    ele = bsoup.select_one("div.postList:nth-child(4)")

    if ele is None:
        print("見つかりませんでした")
    else:
        # h1タグのリストを取得
        allTitles = ele.find_all("h1")
        for h1 in allTitles:
            title = h1.select_one("a").string
            # h1の中のaタグの表示文字列を取得
            print(title)
            # テキストファイルに保存
            fw.write(title + "\n")

print("HTML解析終了")

実行したPythonファイルと同階層にTXTファイルを出力しました。
dockerコンテナからPythonを実行すると、以下の共有フォルダにTXTファイルが出力されていると思います。

例:D:\Docker\share\save.txt

テキストファイルの中身にタイトルの一覧が保存されます。

ラズパイで温度を取得(デジタル温度センサADT7310)
Alexaのスキルを手軽に作ってみた(Alexa Skill Blueprints)
ラズパイとNode-REDを使って簡単にLチカ!
ラズパイにNode-REDをインストール
ラズパイZeroでLチカ!(Python編)

スクレイピングした結果を、例えばCSVファイルに保存すると便利ですね!

dockerで一発実行!

スクレイピングするPythonファイルが完成したら、定期的にデータを取得する想定で考えてみましょう。

dockerコンテナに毎回入りPythonを実行せずとも、コマンド一つでPytonを実行しファイルへ保存できると便利ですよね!

まず、ダウンロードと解析を行うPythonを実行するシェルスクリプトを、共有フォルダの直下に作りましょう。
(改行コードはLFにします)

例:D:\Docker\share\start.sh

#!/bin/bash
python download.py
python analysis3.py

以下のようにdockerコマンドを実行します。

docker run -it --rm -v D:\Docker\share:/root/share -w /root/share --name py-sc-temp chigusaweb/python-scraping:latest ./start.sh

–rmを指定することにより、コンテナ実行後、自動でコンテナが削除されます。
-wを指定することにより、コンテナ内のカレントディレクトリを移動しています。

作成したstart.shを実行し、Pythonを実行し、HTMLダウンロード・解析・テキストファイルを保存し終了します。

その後、Windows側の共有フォルダに、save.txtが作成されることを確認してください。

これにより、バッチファイルを作成しタスクで定期的に保存できますね!
(Linux系だったらcron)

最後に

今回はPythonを使用して、htmlファイルをダウンロードし、解析を行いました。
意外にやってみると簡単にできますね!

ただし、Webページによっては、Javascriptで動的にページを表示する場合もあるかと思います。
その場合には今回のやり方はではできず、また別なやり方で行うことができます。
そのやり方も別途まとめて行きたいと思います。

コメント

タイトルとURLをコピーしました