この記事は一休.comアドベントカレンダー2017の3日目です。
一休.comの開発基盤をやっています akasakas です。
BeautifulSoup4でスクレイピング
スクレイピングでBeautifulSoup4を扱う機会が多いです。 BeautifulSoup4はいろんな便利機能が揃ってますが、自分は全部覚えられないし、使いこなせなません(苦笑)
正直、BeautifulSoup4にあるいくつかのメソッドがそれなりに使えれば、十分スクレイピングできます。
なので、今回はBeautifulSoup4を使い、スクレイピングをして、各種メソッドを紹介します。
スクレイピング対象
一休.com Advent Calendar 2017 - Qiita
やってみること
カレンダーから日付/担当者/タイトルを取得&出力してみます
結果
day is 1 author is ninjinkun title is 単純なコードでアプリ内のコンバージョン経路を計測する ---- day is 2 author is ryo511 title is 一休.comのJavaScriptユニットテスト環境 ---- ... ---- day is 24 author is zimathon title is 開発組織の目的型組織への移行 ---- day is 25 author is ninjinkun title is 締めます ----
コード
from bs4 import BeautifulSoup from urllib.request import urlopen html = urlopen("https://qiita.com/advent-calendar/2017/ikyu") soup = BeautifulSoup(html, "html.parser") for advent_calendar_week in soup.tbody: for advent_calendar_day in advent_calendar_week.find_all("td", {"class": "adventCalendarCalendar_day"}): print(f"day is {advent_calendar_day.p.string}") print(f"author is {advent_calendar_day.find_all('div')[0].a.get_text().strip()}") print(f"title is {advent_calendar_day.find_all('div')[1].string}") print("----")
1つずつブレイクダウン
下準備:htmlパース
これだけです
from bs4 import BeautifulSoup from urllib.request import urlopen html = urlopen("https://qiita.com/advent-calendar/2017/ikyu") soup = BeautifulSoup(html, "html.parser")
soupオブジェクトにガツっと結果が入ってます。 このオブジェクトから日付・担当者・タイトルをピックアップします。
カレンダーを取得し、ループで回す
for advent_calendar_week in soup.tbody: for advent_calendar_day in advent_calendar_week.find_all("td", {"class": "adventCalendarCalendar_day"}):
soup.tbody
でtbodyを取得しています。
この中にある <td class="adventCalendarCalendar_day">
が欲しいので、そこから、さらに find_all("td", {"class": "adventCalendarCalendar_day"})
で <td class="adventCalendarCalendar_day">
を全部ピックアップして、ループで回してます。
日付と担当者とタイトル
print(f"day is {advent_calendar_day.p.string}") print(f"author is {advent_calendar_day.find_all('div')[0].a.get_text().strip()}") print(f"title is {advent_calendar_day.find_all('div')[1].string}")
pタグに日付があるので、 p.string
で日付が取得できます
divタグの1つ目が担当者、2つ目がタイトルとなります。
個人的に感じたポイント
必要なデータは一括で取得
soup.{tag}
で必要なデータは一括で取得できます
find_allで必要な情報だけうまく取得する
divタグの特定クラスをまとめて取得したい場合にfind_allが便利です。
上の例だと
find_all("td", {"class": "adventCalendarCalendar_day"})
で、tdタグの特定クラスだけをまとめて取得しています。
別法
下のリストから日付/担当者/タイトルを取得&出力してみます
結果
day is 12 / 1 author is ninjinkun title is 単純なコードでアプリ内のコンバージョン経路を計測する ---- day is 12 / 2 author is ryo511 title is 一休.comのJavaScriptユニットテスト環境 ---- ... ---- day is 12 / 24 author is zimathon title is 開発組織の目的型組織への移行 ---- day is 12 / 25 author is ninjinkun title is 締めます ----
コード
from bs4 import BeautifulSoup from urllib.request import urlopen html = urlopen("https://qiita.com/advent-calendar/2017/ikyu") soup = BeautifulSoup(html, "html.parser") for advent_calendar_day in soup.find_all("div", {"class" : "container"})[4]: print(f"day is {advent_calendar_day.find('div', {'class' : 'adventCalendarItem_date'}).string}") print(f"author is {advent_calendar_day.a.get_text().strip()}") if advent_calendar_day.find('div', {'class' : 'adventCalendarItem_entry'}) is None: print(f"title is {advent_calendar_day.find('div', {'class' : 'adventCalendarItem_comment'}).string}") else: print(f"title is {advent_calendar_day.find('div', {'class' : 'adventCalendarItem_entry'}).get_text()}") print("----")
1つずつブレイクダウン
リストを取得し、ループで回す
for advent_calendar_day in soup.find_all("div", {"class" : "container"})[4]:
divタグの class="container"
の中で、下のリストのオブジェクトを取得し、1行ずつ回しています。
投稿済みと未投稿の分類
以下のように、投稿済みと未投稿でタイトルのDOMが少々異なります。
投稿済み
<div class="adventCalendarItem_commentWrapper"> <div class="adventCalendarItem_entry"> <a data-confirm="Are you sure to follow a link to this website? http://user-first.ikyu.com/entry/singleton-tracking" href="http://user-first.ikyu.com/entry/singleton-tracking" target="_blank">単純なコードでアプリ内のコンバージョン経路を計測する <i class="fa fa-external-link"></i> </a> </div> </div>
未投稿
<div class="adventCalendarItem_commentWrapper"> <div class="adventCalendarItem_comment">BeautifulSoup4を実際に使ってみつつ、各メソッドを解説してみる</div> </div>
<div class="adventCalendarItem_entry">
の有無で判定し、タイトルを取得しています。
if advent_calendar_day.find('div', {'class' : 'adventCalendarItem_entry'}) is None: print(f"title is {advent_calendar_day.find('div', {'class' : 'adventCalendarItem_comment'}).string}") else: print(f"title is {advent_calendar_day.find('div', {'class' : 'adventCalendarItem_entry'}).get_text()}")
まとめ
BeautifulSoup4を使い、スクレイピングをしてみました。 ここでご紹介した機能はごく一部になりますが、それでも使えれば十分スクレイピングができました。
明日は id:kentana20 さんによる「宿泊サービスにおけるUI改善の取り組み」です。
参考