i++

プログラム系のメモ書きなど

Robobrowser + α によるスクレイピングの忘備録

Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-』を読んでしばらく Robobrowser を使ってみようと思うので、その忘備録。

edx.hatenablog.com

基本

browser = RoboBrowser(
    parser='html.parser',
    # Cookie が使用できないと表示されてログインできない問題を回避するため、通常のブラウザの User-Agent を使う。
    user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:45.0) Gecho/20100101 Firefox/45.0)'
)
broswer.open(スクレイピング対象のページのURL)
# 取得したページの中身の確認。デバッグ時以外は不要。
# print(broswer.parsed)

user_agent の指定はサイトによっては不要。少なくとも Amazon.co.jp については必要(設定しない状態で open すると、取得できる html で Cookie を有効にして下さい、という旨のメッセージが入っていることがわかる)。

以降のコード例は基本的に上記の続きで broswer が Robobrowser インスタンスを表すものとする。

CSSセレクターを使った要素の取得

select 関数で CSSセレクターを使用可能。条件に一致する要素の配列が取得できる。

条件を指定して要素を取得する関数としては find_all 関数も利用できるので、好みで。find_all の方が柔軟かもしれない。

実際に使用したことのある一部の例のみ記載。

探したい要素 書き方
特定のクラスを持つ要素 要素.クラス a.active
特定の要素の子孫 要素 要素 body h1
特定の要素の直接の子 要素 > 要素 body > h1
特定のID属性 #ID #main
属性が特定の値 要素[属性=“値”] a[href=“/sample/index.html”]
属性が特定の値で始まる 要素[属性^=“値”] span[id^=“itemPrice_” ]
属性が特定の値で終わる 要素[属性$=“値”] img[src$=“.jpg” ]

以下の例では g-item-details というクラスを持った div 要素を取得し、取得した各要素に更に select を実行して必要な値を取得している

for item_info_element in browser.select('div.g-item-details'):
    title = item_info_element.select('a[id^="itemName_"]')
    assert len(title) == 1
    # text でテキストを取得。
    title = title[0].text.strip()
    # href 属性をキーにリンク先を取得
    # 要 from urllib.parse import urljoin
    url = urljoin(browser.url, title[0]['href'])
    # URL の ref= 以降は不要。
    url = item.url[:item.url.find('ref=')]
    price = item_info_element.select('span[id^="itemPrice_"]')
    # 必要な処理を続ける

特定のテキストを持ったリンク

get_link 関数の第一引数でテキストを指定する。取得したリンクを開くには follow_link 関数を使用する。

# この例ではリンク先(href の値)が特定の URL パターンに一致することも確認している
sign_in_link = browser.get_link('サインイン', href=re.compile(r'https://www\.amazon\.co\.jp/ap/signin\?'))
# assert sing_in_link is not None
# browser.follow_link(sign_in_link)

フォーム

action をキーに get_form 関数を使って取得。 name 要素をキーに各入力欄にアクセスして値を設定し、submit_form 関数で実行

browser.follow_link(sign_in_link)
form = browser.get_form(action='https://www.amazon.co.jp/ap/signin')
if form is None:
    raise Exception('Failed to get signin link.')
form['email'] = '.....@gmail.com'
form['password'] = 'secret, of course!'
browser.submit_form(form, headers={
    'Referer': browser.url,
    'Accept-Language': 'ja,en-US;q=0.7;q=0.3'
})

Amazon では Referer と Accept-Language の設定が必要。必要ないサイトもある。この辺りの必要性は実際に確かめるしか無い?

関数のリトライ設定

Retrying パッケージを使用して、関数に @retry デコレータを使用する

@retry(stop_max_attempt_number=3, wait_exponential_multiplier=1000)
def signin() -> RoboBrowser:
    # 処理に失敗した場合は raise Exception('失敗の理由') などで例外を発生させる

github.com