Python スクレイピング
lxml によるスクレイピング
正規表現スクレイピングはいかがでしたか?
細かくスクレイピング条件を設定できるのはすばらしいのですが、
正規表現を書き下すのはなかなか難しいですね。
私のような初学者は、もっとやさしい方法がないか探してしまいます。
そんな人にお薦めなのが lxml です。
これをつかうと CSS を条件にスクレイピングすることができます。
CSS セレクタ-は CSS で装飾する要素を指定するための表記方法です。
例えば
body > h1
と表記すると
body 要素の直接の子である h1 要素を指定することができます。
html ファイルが下記のようになっていたとすると
<body>
<h1>
abcd
</h1>
・・・・・
</body>
abcd
を指定する事が可能です。
もしbody のなかに h1 要素が1つしかないとすれば
body h1
とすることも可能です。
body 要素の任意の子要素を指定するなら
body > *
となります。
lxml インスト-ル
lxml と cssselect をインスト-ルします。
cssselect は CSS セレクタを利用するときに必要になります。
Ubuntu 20.04 LTS の場合は
lxml は
/usr/local/lib/python3.8/dist-packages
(4.6.1)
cssselect は
/usr/local/lib/python3.8/dist-packages
(1.1.0)
のなかにすでに入っていました。
インスト-ルされていないときは
yamada@ubuntu-20:~$ sudo pip3 install lxml cssselect
でインスト-ルします。
なお、lxlm は C 拡張モジュ-ルなのでインスト-ル時コンパイルに必要なパッケ-ジが必要なようです。
よって、インスト-ルできない場合は先に
yamada@ubuntu-20:~$ sudo apt install -y libxml2-dev libxslt1-dev libpython3.8-dev zlib1g-dev
を実行してください。
lxml スクレイピングファイル作成
正規表現スクレイピングファイルを作成したときと同じように lxml スクレイピングファイルを作成していきます。
- お決まりの作法
Pyton がインスト-ルされている場所を示めします。
#!/usr/bin/python3
Pyton3 のインスト-ル場所がわからない場合は
$ which python3
で知ることができます。
ファイルで使用する文字コ-ドを設定します。
#coding: utf-8
HTTP ヘッダーを出力
print("Content-type: text/html; charset=UTF-8\n\n")
これで Python から出力された文字は Web で見ることができるようになります。 - ライブラリを取り込み
取り込む必要があるライブラリは1つです。
import lxml.html
- HtmlElementTree オブジェクト作成
Python スクレイピング概要 のなかで作成した sc_re_hd_enc.py を実行したときできた
sc_dp.html ファイル
を読み込み、parse()関数で
HtmlElementTree オブジェクト tree
を作成します。
tree = lxml.html.parse('sc_dp.html') - HtmlElementHtml オブジェクト作成
getroot()メソッドで HtmlElement オブジェクト html を作成します。
html = tree.getroot() - href 属性を絶対 URL に変換
make_links_absolute()メソッドを使用します。
引数の URL を基準として、すべての a 要素の href 属性を絶対 URL に変換してくれます。html.make_links_absolute('https://gihyo.jp/')
- セレクター作成
あらかじめ、抜粋したい部分のセレクター listb を作成します。
id="listBook" である要素の子である li 要素の子である itemprop="url"
という属性を持つ a 要素を指定したいので
listb = '#listBook > li > a[itemprop="url"]'
とします。
要するに
<div id="bookList">
以下に<li> の項が沢山出現します。
その中の<a itemprop="url" と書かれた項を </li> まで抜粋しなさい
ということになります。 - 指定セレクターオブジェクト作成
指定セレクター listb からなるオブジェクト htmlist を作成します。
htmlist = html.cssselect(listb) - for 文で書籍のURLとタイトルを取得
セレクタ-で取得した htmlist には 沢山の a 要素の href 属性が入っていますのでここから 書籍の URL とタイトルを順次取得します。
for ah htmlist:
url = ah.get('href')
get() メソッドで属性の値が取得できます。
また、書籍のタイトルは itemprop="name" という属性を持つ p 要素から取得します。
p = ah.cssselect('p[itemprop="name"]')[0]
cssselect メソッドは配列指定が必要です。
[0] がないとエラ-になります。
title = p.text_content()
wbr 要素などが含まれるので text ではなく text_content() メソッドを使います。 - 取得した書籍の URL とタイトルを保存
あらかじめ
books = []
として設定しておいた配列 books に順次、
取得した書籍の URL とタイトル title
を保存します。
books.append({'url': url, 'title': title})
- 保存したデ-タ確認
print(books)
として保存したデ-タを確認します。
なお、抜粋デ-タを CSV あるいは JSON 形式で保存する方法は
抜粋したデ-タをファイル保存
を見てください。
以上をまとめると以下のようになります。
#!/usr/bin/python3
#coding: utf-8
print("Content-type: text/html; charset=UTF-8\n\n")
import lxml.html
# HTMLファイル読込 getroot()メソッドで HtmlElement オブジェクト作成
tree = lxml.html.parse('sc_dp.html')
html = tree.getroot()
# 引数の URL を基準として、すべての a 要素の href 属性を絶対 URL に変換
html.make_links_absolute('https://gihyo.jp/')
books = []
# cssselect()メソッドで、セレクターに該当するa要素のリストを取得して
# 個々の a 要素に対して処理を行う。
# セレクターの意味:id="listBook"である要素 の子である
# li要素 の子である itemprop="url"という属性を持つ a 要素
listb = '#listBook > li > a[itemprop="url"]'
htmlist = html.cssselect(listb)
for ah in htmlist:
# a要素のhref属性から書籍のURLを取得する。
url = ah.get('href')
# 書籍のタイトルは itemprop="name"
# という属性を持つp要素から取得する。
p = ah.cssselect('p[itemprop="name"]')[0]
title = p.text_content()
# wbr要素などが含まれるのでtextではなくtext_content()を使う。
# 書籍のURLとタイトルを出力する。
# print(url,"<br>", title,"<br>")
books.append({'url': url, 'title': title})
print(books)
本内容を TeraPad 等で作成したら、ファイル名を適当に
sc_rape_by_lxml.py
として保存します。
文字コ-ドは、UTF-8N
BOM なし
改行コ-ドは、LF
です。
for 文の中のインテンドには注意してください。
Tab 設定と半角空白設定を混在させると for 文がうまく動きませんよ。
私は for 文の中のインテンドは半角空白2文字設定で統一しています。
保存先はホスト Wimdows OS の 共有フォルダ c:\vb_public_html にして
ブラウザでアクセスできるようにします。
なお、
c:\vb_public_html
はゲスト OS Ubuntu のなかの
/home/yamada/public_html
とリンクしていて共有フォルダの関係にあります。
詳細は
ユ-ザ-ごとの公開ディレクトリを用意する
を読んでみてください。
ここまでで、lxml スクレイピングファイル sc_rape_by-lxml.py を作成する事ができました。
引き続き、このファイルの動作確認をします。