Python
初心者
BeautifulSoup

Pythonによるスクレイピング&機械学習のお勉強その1-2 - BeautifulSoupでスクレイピング

今回の目標

このシリーズでは教科書(文献1)に沿ってPythonによるスクレイピングと機械学習を学びます。今回は第1章「クローリングとスクレイピング」から1-2「BeautifulSoupでスクレイピング」を学びます。
前回のペースだとかなり長丁場になりそうだったので、簡潔にすすめてゆきたいところです。

原則、教科書のサンプルプログラムを作成してゆきますが、著作権に配慮し、できるだけそのままではなく類題を作成して勉強してゆく方針です。

方法と結果

  • 準備

その0で作成した学習用docker環境でpythonの実行を行います。
BeautifulSoup4はdocker環境にインストール済です。

$ docker run -t -i -v $HOME/src:$HOME/src pylearn2 /bin/bash

類題1-2-1: BeautifulSoupの基本的な使い方

ローカルのHTMLファイルから任意の要素を抽出する例題です。類題として、HTMLファイルをローカルファイルから読み込むこと、ファイルの読み込み部分をライブラリ化することにします。

まず、解析対象のHTMLです。

scraping.html
<HTML>
  <BODY>
    <H1 id="title">スクレイピングとは?</H1>
    <p id="body">Webページを解析すること。</p>
    <p>任意の箇所を抽出すること。</p>

    <ul>
      <li><a href="http://******">uta</a></li>
      <li><a href="http://******.jp">oto</a></li>
  </BODY>
</HTML>

次にライブラリファイルを作ります。このファイルは更新しつつ今後も使い回す予定です。

pylearn.py
# -*- coding: utf-8 -*-

# pylearn.py Python学習のための頻出機能ライブラリ

# 変更履歴
# v1 ファイル読み込みを追加

def readFile(filename):
    try:
        with open(filename, 'r') as fp:
            return fp.read()
    except:
        print('readFileでファイル読み込みに失敗しました: ', filename)


if __name__ == '__main__':
        print('''Do NOT use this file as main program.
 Usage: 
 import pylearn''')

メインプログラムを作ります。

bs-test1-cclef.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylearn as pl
from bs4 import BeautifulSoup as bs

#HTMLファイルの読み込み
html = pl.readFile('scraping.html')

#HTMLの解析
soup = bs(html, 'html.parser')

#抽出し、表示する
print(soup.html.body.h1)
p1 = soup.html.body.p
print(p1)
print(p1.next_sibling.next_sibling)
  • 実行結果
$ python ./bs-test1-cclef.py
<h1 id="title">スクレイピングとは?</h1>
<p id="body">Webページを解析すること。</p>
<p>任意の箇所を抽出すること。</p>

類題1-2-2: BeautifulSoupのfind()とfind_all()メソッドを使う

findメソッドやfind_allメソッドでHTMLを検索する課題がありました。まとめて一つのソースとして実装します。

bs-test2-link-cclef.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylearn as pl
from bs4 import BeautifulSoup as bs

#HTMLファイルの読み込み
html = pl.readFile('scraping.html')

#HTMLの解析
soup = bs(html, 'html.parser')

#find()メソッドの練習
print(soup.find(id="title"))
print(soup.find(id="body"))

#find_all()メソッドの練習
links = soup.find_all("a")
print(links)
  • 実行結果
$ python ./bs-test2-link-cclef.py
<h1 id="title">スクレイピングとは?</h1>
<p id="body">Webページを解析すること。</p>
[<a href="http://******">uta</a>, <a href="http://******.jp">oto</a>]

類題1-2-3 ダウンロードとBeautifulSoupの組み合わせ

ネットワークからダウンロードしたデータをスクレイピングする課題です。例題として、前の記事でも使ったクジラ飛行机先生の郵便番号APIが返すXMLを解析します。

まず、pylearnライブラリにダウンロード機能を実装します。

pylearn.py
# 変更履歴
# v1 ファイル読み込みを追加
# v2 ダウンロードを追加

import requests

#ファイルを読み込み、内容を返す
def readFile(filename):
    try:
        with open(filename, 'r') as fp:
            return fp.read()
    except:
        print('readFileでファイル読み込みに失敗しました: ', filename)

#URLを指定し、データのダウンロードを行う requestsライブラリを使用
def download(URL, getparam={}, text=True):
    try:
        response = requests.get(URL, getparam)
        if response.status_code == 200:
            if text == True:
                return response.text
            else:
                return response.content
    except:
        print('ダウンロード失敗。ステータスコード=', response.status_code)
        raise IOError


if __name__ == '__main__':
        print('''Do NOT use this file as main program.
 Usage: 
 import pylearn''')

次に解析プログラムです。前の記事のようにランダムな郵便番号のデータをダウンロードします。

bs-zip-cclef.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylearn as pl
from bs4 import BeautifulSoup as bs
import random

url = 'http://******/xml/get.php'


#GETパラメータを生成する
def generate_param():
    zip_1st = random.randint(70, 905)
    zip_2nd = random.randint(0, 100)

    str1 = ''
    if zip_1st < 100:
        str1 += '0'
    str1 += str(zip_1st)

    str2 = ''
    if zip_2nd < 10:
        str2 += '000'
    str2 += '00' + str(zip_2nd)

    zip = str1 + str2
    print('生成された郵便番号は', zip, 'です')
    return { 'fmt': 'xml', 'zn': zip}

# 乱数を初期化
random.seed()

# GETパラメータを作成
param = generate_param()

# XMLデータをダウンロード
xml = pl.download(url, param)

# soup
soup = bs(xml, 'html.parser')

# データを抽出
ken = soup.find('ken').string
shi = soup.find('shi').string
cho = soup.find('cho').string
print(ken, shi, cho)
  • 実行結果
python ./bs-zip-cclef.py
生成された郵便番号は 5300052 です
大阪府 大阪市北区 南扇町

類題1-2-4 CSSセレクタを使う

CSSセレクタを使って必要な要素を抽出します。解析するHTMLは

meigen.html
<html>
  <body>
    <div id="meigen">
      <h1>トルストイの名言</h1>
      <ul class="items">
    <li>汝の心に教えよ、心に学ぶな</li>
    <li>謙虚な人は誰からも好かれる。</li>
    <li>強い人々は、いつも気取らない。</li>
      </ul>
    </div>
  </body>
</html>

です。早速類題プログラムを作ります。

bs-select-cclef.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylearn as pl
from bs4 import BeautifulSoup as bs

#ファイルを読み込む
html = pl.readFile('meigen.html')

#soup
soup = bs(html, 'html.parser')

#CSSセレクタで取り出す
print(soup.select_one('div#meigen > h1').string)

#リスト部分を取り出す
print(soup.select('div#meigen > ul.items > li'))
  • 実行結果
トルストイの名言
[<li>汝の心に教えよ、心に学ぶな</li>, <li>謙虚な人は誰からも好かれる。</li>, <li>強い人々は、いつも気取らない。</li>]

うーん、select(_one)メソッドに渡す文字列にCSSセレクタ独自の文法がある、ということのようですね。覚えにくそう...

類題1-2-5 Yahoo!ファイナンスの為替情報を取得する

教科書ではYahoo!ファイナンスでUSD/JPYの情報を取得する課題が示されています。少し改変して実装してみます。

bs-kawase-cclef.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pylearn as pl
from bs4 import BeautifulSoup as bs
import random

url = "http://stocks.finance.yahoo.co.jp/stocks/detail/"

# 乱数の初期化

# 取得する通貨ペアを決める
r = random.randint(0, 2)
if r == 0:
    pair = ('usdjpy', 'USD/JPY')
elif r == 1:
    pair = ('eurjpy', 'EUR/JPY')
else:
    pair = ('eurusd', 'EUR/USD')

# GETパラメータ
param = { 'code': pair[0] }

# HTMLを取得
html = pl.download(url, param)

# soup
soup = bs(html, 'html.parser')

# 価格を出力
print(pair[1], '=', soup.select_one('.stoksPrice').string)
  • 実行結果
$ python ./bs-kawase-cclef.py
EUR/JPY = 129.619724

今回達成したこと

  • BeautifulSoup4の基本的な使い方を学んだ
  • 学習用コード頻出機能のライブラリ化を始めた

Python部分は大丈夫でしたが、CSSセレクタの書き方がまだ身についてない自覚があります。

参考文献

  1. クジラ飛行机, Pythonによるスクレイピング&機械学習[開発テクニック], ソシム株式会社, 2016