個人的神殿

プログラミング

BeautifulSoupでスクレイピング 基本のまとめ

まぁこれもネット上にはたくさん情報があるが、やはり自分で何かをやらないと頭に入らないような気がするのでここにまとめておこうと思う。

今回は以下のサイトを対象に試してみる。
wiki3.jp

まずはhtmlを取得したいのだが、pythonに標準で付いているurllib.requestだとエラーが出てしまうので、requestsモジュールを使うことにする。
www.yoheim.net

import requests

url = "https://wiki3.jp/occult_"
html = requests.get(url)
print(html.text)

こんな感じでhtmlを文字列として取得できる。

<!DOCTYPE html>
<html lang="ja" prefix="og: http://ogp.me/ns#">
<head>

<script async src="https://www.googletagmanager.com/gtag/js?id=UA-60774119-9"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-60774119-9');
  gtag('config', '');
</script>
<meta charset="utf-8">
……

次はBeautifulSoupを使っていく。

まず、bs4をインストールする。

pip install bs4

そしてこのように書く。

import requests
from bs4 import BeautifulSoup

url = "https://wiki3.jp/occult_"
html = requests.get(url)

bs_obj = BeautifulSoup(html.text, "html.parser")
print(bs_obj.find("h1"))

するとこのようになる。

<h1><a class="uk-link-muted" href="/occult_">Hiroのオカルト図書館</a></h1>

BeautifulSoupの第1引数に解析対象のHTMLを渡し、第2引数でどのパーサーを使用するか決める。
"html.parser"はpythonの標準ライブラリのparserなのだが、
vaaaaaanquish.hatenablog.com

↑のサイトを参考にしたら、もっと良いパーサーを使ったほうが良いらしい。
なので

pip install lxml
bs_obj = BeautifulSoup(html.text, "lxml")

もしくは

pip install html5lib
bs_obj = BeautifulSoup(html.text, "html5lib")

と書くことにする。

次はhtmlから必要な情報を取る作業を行いたい。

①.find()
何か特定のHTMLタグの、「最初の」1つだけ抽出することができる。
例えば最初のh1タグを取り出したいというときは、

print(bs_obj.find("h1"))
<h1><a class="uk-link-muted" href="/occult_">Hiroのオカルト図書館</a></h1>

タグを抜いて、「Hiroのオカルト図書館」という文字列だけを抜きたいときは、textにアクセスする。

print(bs_obj.find("h1").text)
Hiroのオカルト図書館

属性値にアクセスしたい場合は、get()を使い、属性の名前を指定する。
例えば上のh1タグの中のaタグの中のclass属性を得たい場合は、

print(bs_obj.find("h1").find("a").get("class"))

こんな感じでリストになって返ってくる。

['uk-link-muted']


②find_all()
find()と違い、条件に合致するタグをリストとして全て返す。

print(bs_obj.find_all("h1"))
[<h1><a class="uk-link-muted" href="/occult_">Hiroのオカルト図書館</a></h1>, <h1>メニュー</h1>, <h1 class="uk-text-muted uk-h3"><span class="tc_whitesmoke"><span class="tc_snow">最近更新したページ</span></span></h1>, <h1><span class="tc_gainsboro">カウンター</span></h1>,
<h1>トップページ</h1>]

これをfor文で回して文字列を抽出してみると

[print(bs_obj.string) for bs_obj in bs_obj.find_all("h1")]
Hiroのオカルト図書館
メニュー
最近更新したページ
カウンター
トップページ


全てのリンク先を得たい場合は、aタグの中のhref属性を調べれば良いのでこうする。

[print(link.get("href")) for link in bs_obj.find_all("a")]
/
/signUp
/guide

/occult_/edit?mode=1

/occult_/edit/1
/occult_/edit/2
...


h1タグとh2タグといったように複数のタグを同時に抽出したい場合は{}でくくる。

print(bs_obj.find_all({"h1", "h2"}))

さらにh1タグとh2タグの中の、class属性が"uk-text-muted"のものを抽出したい場合は

print(bs_obj.find_all({"h1", "h2"}, {"class": "uk-text-muted"}))
[<h2 class="uk-text-muted uk-h3"><span class="tc_black">グリモア魔術</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">古代魔術</span> </h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">ルネッサンス魔術</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">現代魔術</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">魔女術</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">錬金術</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">秘密結社</span></h2>, <h2 class="uk-text-muted uk-h3"><span class="tc_black">カバラ</span></h2>, <h1 class="uk-text-muted uk-h3"><span class="tc_whitesmoke"><span class="tc_snow">最近更新したページ</span></span></h1>]