100日後にエンジニアになるキミ - 72日目 - プログラミング - スクレイピングについて3

昨日までのはこちら

[100日後にエンジニアになるキミ - 70日目 - プログラミング - スクレイピングについて](https://qiita.com/otupy/items/f53e2e7d16adf80b5acf)

[100日後にエンジニアになるキミ - 66日目 - プログラミング - 自然言語処理について](https://qiita.com/otupy/items/5828ac7450c5ba8d6931)

[100日後にエンジニアになるキミ - 63日目 - プログラミング - 確率について1](https://qiita.com/otupy/items/0196897ae4d2134794fa)

[100日後にエンジニアになるキミ - 59日目 - プログラミング - アルゴリズムについて](https://qiita.com/otupy/items/f1a5e81633b868f3b5ed)

[100日後にエンジニアになるキミ - 53日目 - Git - Gitについて](https://qiita.com/otupy/items/0abaad82a7f3fea73f1f)

[100日後にエンジニアになるキミ - 42日目 - クラウド - クラウドサービスについて](https://qiita.com/otupy/items/d1765f1541a104ed6133)

[100日後にエンジニアになるキミ - 36日目 - データベース - データベースについて](https://qiita.com/otupy/items/7af17635d576677606dc)

[100日後にエンジニアになるキミ - 24日目 - Python - Python言語の基礎1](https://qiita.com/otupy/items/6d1e5453f530e07b7aec)

[100日後にエンジニアになるキミ - 18日目 - Javascript - JavaScriptの基礎1](https://qiita.com/otupy/items/2f71d5f9c754b29d9555)

[100日後にエンジニアになるキミ - 14日目 - CSS - CSSの基礎1](https://qiita.com/otupy/items/61a97863f6df14171994)

[100日後にエンジニアになるキミ - 6日目 - HTML - HTMLの基礎1](https://qiita.com/otupy/items/cd2315dcb38671cfded0)

今回もスクレイピングの続きです。

前回はリクエストを行い情報取得をするところまで行いました。
今回からは通信後のデータの解析方法をやっていきます。

## HTMLを解析する方法

レスポンスボディは基本テキスト型(文字列)構造であるため
そのままでは必要な情報を取り出すのに都合が悪いです。

HTMLの構文を解析するにはPython言語では`BeautifulSoup`ライブラリを用います

`BeautifulSoup`ライブラリを用いて、文字列型のデータを
`BeautifulSoup型`に変換します。

```python
from bs4 import BeautifulSoup

変数名 = BeautifulSoup(レスポンスオブジェクト名.content , "HTMLパーサー名")
```

これで`BeautifulSoup型`にデータ変換できます。

試しに生のHTMLを変換してみましょう。

```python

from bs4 import BeautifulSoup

html='''
<HTML>
<head><title>タイトルだよ</title></head>
<body>
  <h1>h1だよ</h1>
  <h2>h2だよ</h2>
  <div class='c1'>
    <a class='a1' href='http://sample.com'><p>sampleへのリンク</p></a>
  </div>
  <div id='id1'>
    <p><a href='http://sample.com'>aaa</a></p>
  </div>
</body>
</HTML>
'''

# html.parserを用いてレスポンスをBeautifulSoupに変換
soup = BeautifulSoup(html , "html.parser")
print(type(soup))
```
<class 'bs4.BeautifulSoup'>

データ型は`bs4.BeautifulSoup`です。
変数をそのままprintして中身をみると文字列型と同じように見ることができます。

これ以降は`BeautifulSoup型`のメソッドなどがつかえるようになり
それを使ってHTMLの構文にしたがって処理をすることが出来ます。

### HTMLパーサー

`パース`とは一定の規則で記述されたテキストを解析し、プログラムで扱いやすいようなデータに変換する処理のことです。

パーサーはHTMLを`パース`する際のプログラムです。

Python言語で指定できる`HTMLパーサー`は主に次の3つがあります。

| パーサー名  |              内容                           |
|-------------|---------------------------------------------|
| html.parser | デフォルトのパーサー                               | 
| lxml        | C言語の高速実装な解析器,複雑な構造や動的な物に少し弱い                     
| html5lib    | html5の規則に対応したパーサー                      |

`BeautifulSoup`での変換時には何かしらの`パーサー`を指定します。

### HTMLについて

HTMLは木構造(入子構造)のマークアップ言語で、ハイパーリンクや画像等のマルチメディアを埋め込むハイパーテキストとしての機能、見出しや段落といったドキュメントの抽象構造フォントや文字色の指定などの見た目の指定、などといった機能があります。

`タグ`とは、`<`で始まり、`>`で終わっている部分(マークアップ)のみを指し
`要素(エレメント)`は`開始タグ～終了タグ`に囲まれた全体を指す概念です。

**要素(エレメント)**
`<開始タグ>コンテンツ</終了タグ>`

HTMLには様々な`タグ`が存在し、構文解析は`タグ`の構造を読み解いてゆく方法で行います。

### BeautifulSoupでの構文解析

さて構文解析をするサンプルを用意して構文解析してみましょう。

```python

from bs4 import BeautifulSoup

html='''
<HTML>
<head><title>タイトルだよ</title></head>
<body>
  <h1>h1だよ</h1>
  <h2>h2だよ</h2>
  <div class='c1'>
    <a class='a1' href='http://sample.com'><p>sampleへのリンク</p></a>
  </div>
  <div id='id1'>
    <p><a href='http://sample.com'>aaa</a></p>
  </div>
</body>
</HTML>
'''

soup = BeautifulSoup(html , "html.parser")
```

**任意のタグを指定して要素の取得**

以下、BeautifulSoup型の変数を`soup`とします。

`soup.タグ`

```python
#任意の部分を個別抽出
h1 = soup.html.body.h1
p1 = soup.html.body.p
a1 = soup.html.body.a
z1 = soup.html.body.z
print(h1)
print(p1)
print(a1)
print(z1)
print(type(a1))

-------------------------------------------

<h1>h1だよ</h1>
<p>sampleへのリンク</p>
<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
None
<class 'bs4.element.Tag'>
```

タグが複数ある場合は最初に見つかったものが返り、見つからない場合はNoneが返ります。

**任意のidやクラスで要素を探す**
​`soup.find(id="id名")`

`soup.find(class_="クラス名")`

`soup.find(text="テキスト")`

任意で検索したものが複数有った場合は最初に見つかった要素(HTMLタグ)が取得できます。この場合も取得できない場合は`None`となります。

```python
div = soup.find('div',id='id1')
p   = div.find('p')
a   = p.find('a')
print(div.a)

------------------------------------

<a href="http://sample.com">aaa</a>
```

```python
title = soup.find(id="id1")
body = soup.find(class_="c1")
text = soup.find(text="aaa")
print(title)
print(body)
print(text)

------------------------------------

<div id="id1">
<p><a href="http://sample.com">aaa</a></p>
</div>

<div class="c1">
<a href="http://sample.com"><p>sampleへのリンク</p></a>
</div>

aaa
```

**複数の条件で任意のidやクラスで要素を探す**

`soup.find('タグ名',attrs={'要素名':'値','要素名':'値'})`

```python 
# attrs = の後に辞書型で指定する
a_tag = soup.find("a", attrs={"class": "a1", "href": "http://sample.com"})
print(a_tag)

------------------------------------

<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
```

**任意のidやクラスで複数の要素を取得する**

`find_all('タグ名')`

​`soup.find_all(id="id名")`

`soup.find_all(class_="クラス名")`

`soup.find_all(text="テキスト")`

`soup.find_all('タグ名' , attrs={'要素名':'値' , '要素名':'値'})`

```python
links = soup.find_all("a")
print(links)

----------------------------

[<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>,
 <a href="http://sample.com">aaa</a>]
```

`find_all`を用いると条件に該当するHTMLタグの情報全てが取得され
結果はリスト型になります。
何も取得できない場合は要素なしの空のリスト型になります。

タグはリスト型で複数指定が出来ます

```python
links = soup.find_all(["a", "h1"])
for link in links[0:5]:
    print(link)

----------------------------

<h1>h1だよ</h1>
<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
<a href="http://sample.com">aaa</a>
```

**正規表現を用いる方法**

タグやid,クラス名での検索方法では完全一致で検索されます。

部分一致で検索したい場合は`re`ライブラリを用いて探すことが出来ます。

`re.compile('検索文字列')`

この条件で指定した要素は部分一致で検索されます。

```python
import re
soup.find_all("タグ名", text=re.compile("検索文字列"))
```

```python
import re

# re.compile　でテキストに「aa」が含まれるものを抽出
links = soup.find_all("a", text=re.compile("リ"))

for link in links[0:5]:
    print(link)

----------------------------

<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
```

**cssセレクタを使ったタグの取得**

`CSSセレクタ`とは、どの要素がそのスタイル規則によって影響されるかを指定するものでスクレイピングでは指定方法を工夫することで、特定の部分の要素を抽出することができます。

なおBeautifulSoupを使ったスクレイピングで`CSSセレクタ`は`HTMLパーサー`にも依存します。

CSSセレクタで要素を一つだけ取り出す
`soup.select_one(セレクタ)`

CSSセレクタで複数要素を取り出す
`soup.select(セレクタ)`

```python
# select 結果はリスト型
links = soup.select('a[href^="http://s"]')
for link in links[0:5]:
    print(link)

---------------------------

<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
<a href="http://sample.com">aaa</a>
```

### CSSセレクタの種類

下記のようなセレクタの指定ができます。

| セレクタ             | 例                    | 例の説明                                                                     
|----------------------|-----------------------|------------------------------------------------------------------------------|---|
| .class               | .intro                | class="intro" を持った全要素を選択する                                       |   |
| #id                  | #firstname            | id="firstname" を持った要素を選択する                                        |   |
| *                    | *                     | 全要素を選択する                                                             |   |
| element              | p                     | 全ての p 要素を選択する                                                    |   |
| element,element      | div, p                | 全ての div 要素と、全ての p 要素を選択する                               |   |
| element element      | div p                 | div 要素内の、全ての p 要素を選択する                                    |   |
| element>element      | div > p               | 親要素が div 要素である、全ての p 要素を選択する                         |   |
| element+element      | div + p               | div 要素の直後に配置された全ての p 要素を選択する                        |   |
| element1~element2    | p ~ ul                | p 要素が先行しているすべての ul 要素を選択する                           |   |
| [attribute]          | [target]              | target 属性を持った全ての要素を選択する                                      |   |
| [attribute=value]    | [target=_blank]       | target="_blank" を持った全ての要素を選択する                                 |   |
| [attribute~=value]   | [title~=flower]       | 単語 "flower" を含む title 属性を持った全ての要素を選択する                  |   |
| [attribute^=value]   | a[href^="https"]      | href 属性値が "https" で始まるすべての a 要素を選択する                    |   |
| [attribute*=value]   | a[href*="w3schools"]  | href 属性値が部分文字列 "w3schools" を含んでいるすべての a 要素を選択する  |   |

### 属性の取得

上記で取得したものはタグ(要素)になります。
要素の中から属性を取り出すには下記のように行います。

`タグ.get("属性名")`

例：
aタグのhref属性
`aタグ.get('href')`

### テキストの取得

`開始 - 終了タグ`で囲まれた部分がテキストになります。
テキストは`タグ.text`で抽出できますが、タグが取得できていない場合は
抽出はできないので注意が必要です。

```
print(soup.a)
print(soup.a.text)

----------------------------

<a class="a1" href="http://sample.com"><p>sampleへのリンク</p></a>
sampleへのリンク
```


## まとめ

HTMLからBeautifulSoupへの変換方法
BeautifulSoupからタグの取得の仕方
タグから属性やテキストの取得の仕方をやりました。

これで情報のほとんどを取得できたはずです。
様々なやり方があるので、試しながら少しづつ覚えましょう。

君がエンジニアになるまであと28日

## 作者の情報
乙pyのHP：
http://www.otupy.net/

Youtube：
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw

Twitter：
https://twitter.com/otupython