Python入門講座

PythonでWebスクレイピングをしよう ~BeautifulSoup4って便利~

みなさん、お久しぶりです。
初めての方、どうもこんにちわ。

独学でPythonを勉強しているノンストップ飯田です。
ここ最近はDjangoでサイトをWebサービスを作っていました。
それについては、またいつか書きますが、今日はWebスクレイピングについて書きます。

Webスクレイピングって何?

Webは言葉の通りです。
スクレイピングとは、日本語で“削る”ことを意味します。

Web上にある情報から、自分が欲しいものを”削り取って”くるイメージですね。

<イメージ図 その1>

HTMLタグを頼りに、欲しい情報を取り出す

上の図だとちょっとざっくりし過ぎてますでしょうか(笑)

例えば、Googleでこのページを開いている方、右クリックを押して「検証」を選んでみてください。
titleだのh1だの、div class=”xxx”や、a href=”xxxxxxx”だのが出てきますよね。
これはHTMLという言語で、Webページは共通してこの言語で書かれています。
<・・・>はタグといい、それぞれ役割があります。
(他にもデザインをコントロールするCSSや、サイト上で処理をさせるためのJavaScript等があります)

Webページは、共通してこれらの言語で記載されているため、このタグを頼りに情報を削り取って自分の欲しい情報だけを取り出すことができるのです!!
ちなみに、aタグからはURLが、titleタグからはサイトのタイトルが、h1タグはヘッダーと言って見出しが取り出しできます。

<イメージ図 その2>

何となくイメージできたと思います。
ただ、HTMLやCSSを知らないと、Webスクレイピングができないかというと、そんなことは都度ググってしまえばいいので、HTMLをまずは勉強してからやろう・・・なんてことは不要です。
やりながら覚えればいいのです。何事も。

ニュ-スサイトから、スクレイピングでタイトルとURLを取り出してみよう

何はともあれ、手を動かしてコードを書き、いろいろ自分で試してみるのが覚えるには一番です。
では、ライフハッカーというサイトから記事のタイトルとURLを取り出してみましょう。

取り出す際の手順

取り出すにあたっての手順は以下の通りです。

➀ 対象のURLの情報を全て取り出す(request)
➁ 取り出した情報から、必要な情報をBeautifulSoup4を使ってスクレイピングする

そう、なんとこれだけなのです。

requestって何?

長くなるので、はしょりますが基本PCを介してインターネットでWEBサイトの情報を表示したり、投稿したりするような一連の仕組みをクライアントサーバーモデルと言います。

クライアントは、あなたがこのブログを読むために利用しているスマホやPC端末のことをいい、サーバーはスマホやPC端末からの要求に応えて、HTML言語等で記載されたページ情報を返しています。
これにより、インターネット情報が閲覧ができているわけです。

<イメージ図 その3>

クエストで、WEB情報を取得する

それでは、Pythonでリクエスト(request)をしてWEB情報を取得します。
reqeustsというモジュールで実施することが可能です。
まずは、コマンドプロンプト等でpip installを使ってrequestsモジュールをインストールしましょう。

※ピップって何!?という方は↓こちらへ

無事インストールはできましたでしょうか?

もう一つ外部モジュールをスクレイピングのために使う必要があります。
BeautifulSoup4というモジュールです。※
これもpipでさくっとインストールしちゃいましょう。

できましたね!

では、コードを書いていきましょう。
IDLEでもテキストエディターでもいいので、開いてください。(僕はIDLEで書きます)

まずは、先ほどinstallしたreqeustsモジュールとBeautifulSoup4をインポートしてください。
BeautifulSoupをインポートする際に、fromを使っているのはBeautifulSoup4を使う時に記述を簡略にするためです。

#インポートする
import requests
from bs4 import BeautifulSoup

まずは、モジュールをインポートしました。
次に、サイトのアドレスを指定してサーバー側にリクエストをして、requestを出しましょう。

#該当ページのリクエストを行う(キーワード一覧でpythonを検索した状態のURL)
#「URL」という変数に、URLを文字列で渡す。
url = ‘https://www.lifehacker.jp/’

#requestsモジュールのgetメソッドを使って、変数rにWebページからのレスポンスを格納する
r = reqeusts.get(url)

これで「r」という変数にウェブサイト側からのresponseが格納されました。
なお、レスポンスで取得したオブジェクトは、そのままでは文字列等として取得できません。
なので、サイトの文字コードでエンコーディングをしたうえで、テキスト要素に変換します。

「なになに!?エンコーディングって!?難しいこと分かんないし」
って思った方、、、大丈夫です。
ざっくりいうと、所謂データを文字として読めるように符号化しているだけです。

コンピュータのデータは基本0と1の2進数や、16進数(アルファベットと数字の組み合わせ)等なので、人間が読めるような文字を扱う「文字コード」というものが存在します。
JISコードやASCIIコード、万能なUTF-8等様々な文字コードがあります。

以下の方法を使うと、そのサイト上で使用されている文字コードを使ってエンコーディングすることができます。

#レスポンスオブジェクトをエンコードしたうえでテキストに変換する
r.encoding = r.apparent_encoding
res = r.text

  

最後の.textで、文字列に変換をしています。
ではまず、この状態で出力をしてみてください。

print(res)で出力しようとすると、こんなん出てきません?

簡単に言うと、「1456行もあるから圧縮しとくね!」ってことだと思います。(違ったらごめんなさい)
ここを意地を張って、ダブルクリックなんてしようものなら・・・
暫くIDLEは固まります(笑)

前述したHTMLやCSSで記載されたサイトをユーザーに見せるクライアント側のコードが、ドバっと1456行出てくるわけです。・・・しんどいですね。
では、タイトルとURLが欲しい場合、どうするか?

それは、タイトルとURLをゲットするには、どのタグを当てにすればいいかを調べる事です。
では、初めの方で見たのと同じように、ライフハッカーの記事のところで’右クリック’→’検証’を押してみましょう。

薄青色のところを見てください。このaタグで削れば良さそう・・・ですが、aタグはありとあらゆるところにあります。
ちょっとやってみましょう。

嗚呼・・・567に減っただけやん・・・
そして、しょうやんさんのいきなりステーキの方に注目してしまったあなたは残念ながら失格です。
嘘です。でも、しょうやんさんは要チェックです。

では、先ほどの検証で出てきたHTMLコード達を再度みていると・・・

こちらのh3タグは使えそうですね。

ちなみにBeautifulSoupでは、簡単にタグとクラス(class)や属性、URLを削りとるための方法が用意されています。
さっき、サラッと流してしまいましたが、まずはBeautifulSoupオブジェクトを作ります。
先ほどの書き方だと、「soup」という変数に、BeautifulSoupのインスタンスを(一旦引っかからずに無視してください)、さっき取得した「res」という変数に格納された、Webページから取得した情報と、’html.parser’という文字列を引数にして格納します。

インスタンスが謎の方は以下の記事をご覧ください。

簡単にいうと、「スクレイピングをするためのBeautifulSoupという用意された設計図から、実際にスクレイピングマシーンを呼び出した」みたいな感じです。
html.parserが何かというと、複雑な構造の文書をプログラムで扱いやすいように変換するよーくらいのもので、他に”lxml”等でパースすることも可能です。
後は、このスクレイピングマシーンに備え付けられているfind_allという関数を利用して、削り取りにかかろうというわけです。
(find_allは引数で渡した検索条件に合致するものを、リスト形式にして返してくれます)

では書いてみましょう。

soup = BeautifulSoup(res, ‘html.parser’)
target = soup.find_all(‘h3’, class_=”lh-summary-title”)

なお、classの後にアンダーバー(_)を付けているのは、classは予約語といってPythonの文法上既に使い方が決まっているため、こう書かないとエラーになります。
(僕はここで1週間詰まりましたwww)

では、実行してみると・・・

これで大分絞れましたね!!
ダブルクリックしても、さっと出てきます。

では、最後にtitleと、urlを抜き出しましょう。
まずはタイトルを。リスト形式なので、forでループ処理を行います。
出力時は、.stringをつけてあげることで文字列として出力が可能です。

for title in targets:
  print(title.string)

できましたね!!

では次に、URLを取り出しましょう。
同様にループ処理で、出力時は[‘href’]でaタグのhref属性(URL)が出力できます。
まずはaタグで絞りなおしてから、やってみましょう。

for urls in target:
  url = urls.find(‘a’)
  print(‘https://www.lifehacker.jp/’ + url[‘href’])

そうすると・・・

できましたね!!

出力する際に、アドレスを足している理由について解説すると、サイト内は「相対パス」と言って、URLを全て記載せずともページ遷移できるため、削り取ったURLだけだと不完全です。
なので、ベースとなるURLである’https://www.lifehacker.jp/’を足してあげることで、「絶対パス」となり完全なURLとなるのです。

とめ

大分長くなってしまいましたが、最後に一つだけ・・・。

何故、数多サイトがある中で、スクレイピングは可能なのでしょうか?
それは、ウェブサイトは必ずHTTP(ハイパーテキスト・トランスファー・プロトコル)というプロトコル通信を前提に作成されており、クライアントサイドの言語は必ずHTML・CSS(Java-scriptもあり)で記載されているためです。

一方、多くのWEBサイトは別途存在するデータベースと連動しながら、動的に表示する値を編集したりしています。
この動的な部分をサーバーサイドといい、そこのプログラムはPHPやRuby、Python等様々な言語が存在するわけです。

ちなみに、僕が今やっているDjangoというのはPythonでサーバーサイドプログラムを書く際の、Webフレームワークというもので、あらかじめWEBサイト構築に必要な部品がある程度備えられていて、イチから全てを構築することが不要となっています。
ちょっと脱線しましたが、いずれその辺についても触れたいと思います。

是非、あなた自身も、自分のお気に入りのページなどで試してみてください。

スクレイピングは相手のサイトに少なからず負荷をかけます。
APIという、接続方法が提供されていてそちらからの情報取得が定められているサイトもありますので、注意しましょう。