省電力なラズパイは簡単なデータ収集とデータ分析させるのにも適しています。WEBサイトから情報を持ってくるクローリング&スクレイピングをjupyter notebookを使って試してみました。
既に先人がたくさんの情報をWEBにアップしてくれているので、特別な書籍などは無くてもそれなりにできます。もちろん基本は知っていた方が良いので、興味が沸いたら有名な書籍を購入した方が良いと思います。
ラズパイとLibreELECで再生できますよ!
詳しくは「Raspberry Pi とLibreELEC9.0で作るAmazonプライム・ビデオプレイヤー」へ
今回の環境
今回もRaspberry Pi ノーマル3Bを使います。プラス(+)が付かない旧型です。多少のWEBスクレイピングならスペック的にもそれなりですし、取得するデータもテキストベースですからスペースも必要ありません。更にLinux(Raspbian)環境なら必要なライブラリ、アプリケーションは手に入ります。
今回の環境
- 2019-04-08-raspbian-stretch
- pandas
- selenium
- jupyter notebook
- chromium-chromedriver
Raspbian StretchはliteでなければPython3やpip3は入っています。一部アップグレードして今回のプロジェクトで必要な他のライブラリをインストールしていきます。
事前にノーマル3BにRaspbianのセットアップは済ませてください。
インストールと設定
apt-getでインストールするのはpandasです。
sudo apt-get install python3-pandas
pipからインストールするものは以下です。pipはアップグレードしておきます。
sudo pip3 install --upgrade pip
sudo pip3 install jupyter
sudo pip3 install selenium
スクレイピングするjupyter notebookはLinuxもChromeも対応しています。しかし、RaspbianではChromiumドライバであるchromium-chromedriverをダウンロードしてインストールします。
古い情報にバージョン65ではムリとありましたが、記事執筆時点では問題ありませんでした。
http://launchpadlibrarian.net/361669488/chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb
wgetでダウンロードします。
wget http://launchpadlibrarian.net/361669488/chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb
debパッケージの展開とインストール
sudo dpkg -i chromium-chromedriver_65.0.3325.181-0ubuntu0.14.04.1_armhf.deb
以下のcommandでchromedriverがきちんとインストールできたか確認する
ls -l /usr/lib/chromium-browser/chromedriver
スクレイピング用ディレクトリの新規作成
mkdir ~/notebooks
権限を与えます。こうしないと恐らくデータが書き込めない??
cd ~/notebooks sudo chmod -R 777 ./
jupyter notebookの設定
jupyter notebook --generate-config
これで、home/pi/.jupyter/jupyter_notebook_config.pyに設定ファイルが出来上がります。
設定ファイルは3箇所を修正する
c.NotebookApp.ip = '0.0.0.0' c.NotebookApp.notebook_dir = '/home/pi/notebooks' c.NotebookApp.port = 8888
パスワードの設定
jupyter notebook password
このパスワードはChromiumブラウザからログインする際に使用します。
実行手順
ここからが本番です。
jupyter notebookを立ち上げます。
jupyter notebook

先程設定したパスワードを入れます。
以下のような画面から新しくnotebookを作ります。
New→Python3

実際の画面は以下です。

この枠内にあるボックスへコードを記述していきます。そしてRunボタンで実行させます。
終了するときは、左上のメニュー File → close and halt から行います。(ブラウザを閉じない)
WEBスクレイピングしたコード
たくさんのサイトで紹介されています。いつものごとく「オラに力を分けてくれ!」の先人達のコードを片っ端から読んで寄せ集め・・・いや、なんとか理解しつつまとめてみました。(無駄なコードは適時直してください)
今回、hatenaで任意の文字列(今回はRaspberry Pi )で検索し、その中で100以上ブックマークされている記事で絞り込んでいます。
検索された記事のタイトル名、ブックマーク数、投稿日付、リンクURLをcsvとして書き出しています。
-- coding:utf-8 -- from selenium import webdriver import pandas as pd from bs4 import BeautifulSoup import time #hatenaへアクセス url = "http://b.hatena.ne.jp/search/text?safe=on&q=Raspberry+Pi+python&users=100" URL_HEAD=url.split('/search')[0] p_url = 'https://b.hatena.ne.jp' browser = webdriver.Chrome(executable_path="/usr/lib/chromium-browser/chromedriver") browser.get(url) data = browser.page_source.encode('utf-8') soup = BeautifulSoup(data,'lxml') outputData = pd.DataFrame(index=[],columns=['title', 'bookmarks', 'created', 'link']) page = 1 while True: if not soup.find(class_='centerarticle-pager-next') is None: #Pageのclass centerarticle-entry-data内 posts = soup.find_all('ul', class_='centerarticle-entry-data') print('Starting to get posts… page: {}'.format(page)) for post in posts:
title = post.find(class_='centerarticle-users').find('a').get('title')
print(title)
bookmarks = post.find(class_='centerarticle-users').find('a').text print(bookmarks) created = post.find(class_='entry-contents-date').text print(created) link_s = post.find(class_='centerarticle-users').find('a').get('href') link = p_url + link_s print(link) series = pd.Series([title, bookmarks, created, link], index=outputData.columns) #データ蓄積 outputData = outputData.append(series, ignore_index=True) print(outputData) print('title: {}'.format(title)) clickNextPageBtn = URL_HEAD + soup.find(class_='centerarticle-pager-next').a ['href'] browser.get(clickNextPageBtn) page += 1 browser.implicitly_wait(5) data = browser.page_source.encode('utf-8') soup = BeautifulSoup(data, 'lxml') print("--- Moving to next page ---") print('page={}'.format(page)) time.sleep(5) else: print("no pager exist anymore") break #ファイル出力 outputData.to_csv('/home/pi/notebooks/hatena.csv') print(outputData) print("DONE") #終了処理 browser.quit()
結果はこの通り。ソートはしていません。
ハマったところ
詰まったところは沢山あって全ては表現しきれません。非エンジニアとして基本の基本はある程度理解したかなーと思っても、少しの応用で躓く・・・。
中でも一番ハマったのがこれ。
AttributeError: 'NoneType' object has no attribute 'text'
AttributeError: 'list' object has no attribute 'text'
これは本当に参りました。上のNoneTypeは絞り込めずに値を取れなかった場合が多いです。そもそもそんな値が取れないヘンテコな場所だったりした当てが外れたパターンもありました。
もう一つのlistのエラーは一意(ユニーク)にならず複数の値があったりした場合です。
やはり文法は勉強しないとダメですね・・・。書き方のミスも多かったです。それに似たようなメソッドも違いを覚えないとなりません。find_all、find、selectなど。
今回は時間をかけてハマった?!ので、だいぶ身につきました。HTMLの要素を理解するのは大前提なものの、HTML、CSS、Python、Beautiful Soupでかなり実用的なことができるなーという印象です。
※最後に、WEBスクレイピングは逮捕者も出ています(不起訴だった)。扱いによっては、サーバーに負荷を掛けすぎるという危険なこともあり責任重大です。cornなどで自動にする際には気をつけないとなりません。本格的に運用するなら、そういうセキュリティ面も考慮してください。
サイト側でブロックしていればエラーで取得できません。ラズパイダでも悪質なアクセスはブロックしています。
コメント ご感想をお願いします。