[beautifulsoup] Python でのスクレイピング

Posted on 2019/03/19 in programming , Updated on: 2019/04/01

はじめに

BeautifulSoup は、HTMLをパース(解析)し、情報を抽出するライブラリ。Python の正規表現を利用するよりも高速で、柔軟な解析が可能。WebページからHTMLをダウンロードするには、別途 requests などのライブラリを使用する。本記事では、yahooニューストップページのhtmlを"yahoo_news.html"として、ローカルにある前提で説明する。

インストール

$ pip3 install beautifulsoup4

インポート

from bs4 import BeautifulSoup

soup を作る

まず、html文を BeautifulSoup に渡して、BeautifulSopuオブジェクトととして soup を作成する。(変数名は任意だが、soupとするのが慣例?)。

# htmlファイルを開く。日本語が含まれるので、encodingを追記
html = open('yahoo_news.html', encoding='utf-8')

# html を BeautifulSoup に渡して soup を作る
soup = BeautifulSoup(html, 'html.parser')

ここで、BeautifulSoup() に渡している'html.parser'は利用するパーサ(構文解析プログラム)を指定する。他にも外部ライブラリのhtmlパーサが存在する。

パーサの種類

name 説明
html.parser Pythonにデフォルトで付属されている
html5lib 外部ライブラリhtmlパーサ。多少のhtml崩れに強い。動作は遅い。
lxml 外部ライブラリhtmlパーサ。高速に動くのが特徴

find_all(), find() メソッド

取り出したいタグを文字列としてfind_all()メソッドに渡す。すると指定したタグのすべての要素が入った、リストのようなBeautifulSoup.element.ResultSetが返ってくる。

soup.find_all('a')

# [<a href="https://ard.yahoo.co.j..... ,
#  <a data-ylk="slk:h_ytop;pos:0" ..... ,
#  .  
#  .
#  .

先頭の一つだけを取り出す場合は、find()メソッドを使う。

soup.find('a')
# <a href="https://ard.yahoo.co.j ..... yoppy!</a>

# find_all()はリストを返すので、インデックスで取り出すでもOK
soup.find_all('a')[0]
# <a href="https://ard.yahoo.co.j ..... yoppy!</a>


find_all, find への引数の例

渡す引数 取得できる要素
find('a') aタグ要素
find('h2') h2タグ要素
find(id='header') id名が header の要素
find(class_='single_class') class名が single_class の要素
find('div', class_='single_class') <div>要素で、class名が single_class の要素

select() メソッド

select()メソッドを適用して、検索したい要素のCSSセレクタを渡して呼び出すことでタグを取得可能。マッチングしたパターンのリストを返す。

CSS セレクタを使ったマッチングパターン例

セレクタ マッチする対象
select('div') <div>要素
select('#author') id属性がauthorである要素
select('.single') CSSのクラスがsingleである要素
select('body > span') <body>要素内の<span>要素
select('input[name]') name属性を持つ<input>要素
select('input[type="button"]') type属性の値がbuttonである<input>要素

タグの属性を取得

.text, .string

取り出した要素の文字列(内容)を取得するには、.text, .stringを使う。

soup.find('a').text

# '魚を食べられなくなる前に、できることを Gyoppy!'

find_all()を使って複数取り出す場合は、リスト内包表記等を用いて、

a_list = soup.find_all('a')
[w.text for w in a_list]

# ['魚を食べられなくなる前に、できることを Gyoppy!',
#  'Yahoo! JAPAN',
#  'ヘルプ',
#  .
#  .
#  .
#  'ヘルプ・お問い合わせ']

.get() メソッド

a タグ内の URLのみを取り出したい(href属性)ときなどは、get()メソッドを使う。

soup.find('a').get('href')

# 'https://ard.yahoo.co.j .... yoppy.yahoo.co.jp/'

属性指定に正規表現を利用する

メソッドの引数に正規表現を渡すことも可能。

import re

# href 内に'yahoo'が含まれるタグを全て取得
soup.find_all(href=re.compile('yahoo'))

# [<a href="https://ard.yahoo.co.j... </a>,
#  <a data-ylk="slk:h_ytop;pos:0" ... </a>,
#  <a data-ylk="slk:h_hlp;pos:0" h... </a>,
#  .
#  .
#  .