スクレイピング

【Pythonスクレイピング】別タブを開いて自動処理する方法

記事内に商品プロモーションを含む場合があります

こんにちは、hokkyokunです。

Pythonスクレイピングでインターネット操作を自動処理する際に、
今開いているタブとは別のタブで操作をする必要があったので、コードの情報共有をします。

プログラミング代行いたします。

Python・VBAのコード対応です。

【VBA】
エクセルの自動処理全般

【Python】
スクレイピングによる、インターネット操作の自動化
デスクトップツール(データ解析等)
ワードプレスの自動化・効率化(自動更新、記事新規作成等の自動化)

ココナラで依頼を受けておりますので、お気軽にご相談ください。

依頼はこちらから

前準備

処理工程の整理

簡単に処理工程を書いておきます。
実現したいことは以下のようなことです。

  1. ドライバーの準備と現在のウィンドウハンドルを保存(オリジナルハンドル)
  2. 特定のリンクを取得
    別のタブでリンクを開く
  3. ウィンドウハンドルを別タブにスイッチ
  4. 何らかの処理を行う
  5. 別タブを閉じる
  6. オリジナルハンドルにスイッチ

Chrome driverのインストール

上記の工程はセレニウムを使って処理します。

セレニウムで処理するにはクロームドライバーを事前にインストールしておく必要があります。

スクレイピングには大きく分けて二つの方法があり、
セレニウムを使ったブラウザの自動操作とrequestを使った通信の自動化があります。

どちらも一長一短なのですが、初心者の場合はセレニウムを使って操作することをおすすめします。(本ページもセレニウムで操作することを前提として話を進めます)

セレニウムの長所は我々が普段行っている操作をイメージそのままで自動化できる点です。

例えば、グーグルの検索を開いて、文字を入力し、エンター。
その後、検索された内容を上から順に取得する。
という使い方も可能です。

requestの場合は上記のような使い方は難しいです。
(その分速度が比べ物にならないくらい速いですが)

クロームドライバーのインストールは慣れていないと英語ばかりで面食らうと思いますので、解説をしているウェブサイトを見ながら作業するのをお勧めします。

また、セレニウムの使い方については一冊書籍を買うことをお勧めします。
慣れている方はいいですが、Pythonを使った自動化に興味をもって始めた方は、全て自力で理解するのは大変なので、せめて一冊買っておくと良いと思います。

私は下記のPythonの書籍を買いました。
これ一冊持っているととりあえず基本的な使い方がわかるのでお勧めです。

いくつかPython系の本を買いましたが、この本はセレニウムについて解説してくれている貴重な本です(request系の書籍が非常に多い)。

セレニウムの自動化以外にもエクセルの自動操作方法も解説しているので、VBA使いの人にはかなりとっつきやすい本だと思います。

VBAで書いたコードがPythonではこうなるのか。と勉強になりますよ。

コード

インポート

必要なライブラリのインポートです

from selenium import webdriver
from selenium.webdriver.common.by import By

ドライバー準備と現在のウィンドウハンドルを保存

ウェブドライバーを準備して、見たいページのURLにアクセスします。

今開いているタブのウィンドウハンドルを保存しておきます。
最終的に後でこのタブに戻るために必要だからです。

# WebDriverの準備
driver = webdriver.Chrome()

# 対象のページにアクセス
driver.get("対象URL")

# 現在のタブのウィンドウハンドルを保存
original_window = driver.current_window_handle

特定のリンクを取得
別のタブでリンクを開く

リンクのあるタブを探す方法はいくつもありますが、ここではID属性が振られており、それを取得してタブを得ることができたとします。

link = driver.find_element(By.ID, "your_link_id")  # リンクを特定
driver.execute_script("window.open(arguments[0].href);", link)

ウィンドウハンドルを別タブにスイッチ

今回はウィンドウハンドルが元のハンドルと新しく作ったハンドルの二個です。
これを切り替えます。

# 新しく開いたタブに切り替え
# 開かれたウィンドウハンドルのリストを取得し、元のウィンドウハンドルでないものに切り替えます
windows = driver.window_handles
for window in windows:
    if window != original_window:
        driver.switch_to.window(window)
        break

別タブを閉じる

新しく作った別タブを閉じます。

driver.close()

最初のオリジナルハンドルにスイッチ

最初に作ったドライバーに戻ります。

# 元のタブに戻る
driver.switch_to.window(original_window)

実例

別タブで作業をする必要がある場面とは?

そもそもなんで別タブ処理が必要なのでしょうか。

普通に使っていたらいらない気もします。
私が別タブ処理を必要になった経緯はこんな感じです。

  1. 最初に遷移したページでテーブルを取得する
  2. テーブルには各行に一つずつリンクがある
  3. それをひとつずつクリックして中身を確認したいが、
  4. クリックして中身を確認後、戻って処理の続きをする場合、
    どこまで処理を進めていたか分からなくなるので、コードが複雑化する
  5. テーブルのあるオリジナルタブとは別のタブを開いて処理⇒別タブを閉じて、オリジナルタブに戻り、続きから処理
    これだとコードがシンプルになる。

図で解説します。

ちょっと特殊ではありますが、今は使っていないドメインのワードプレス管理画面で自分の記事内容を自動確認するプログラムを書く必要がありました

投稿記事それぞれのリンクを取得し、一つずつクリックして中身を確認していきます。
例えば「【レンタルサーバー比較…」という記事をクリックし、中身を確認します。

この状態から記事一覧に戻るのも面倒なのですが、仮に戻った場合、どこまで処理が終わっているかを何らかの形でカウントさせなければいけません。

よしんばカウントをしたとしても、ページを繰る作業が入るとかなり複雑なロジックを立てなければいけなくなり、非常に面倒なコードになります。

↑11個目の記事から2ページ目になるので、ページを繰る作業が必要になります。

そこで記事一覧のあるタブはそのままにしておき、記事内容は別タブに表示するようにして、コード管理をかなり簡単にしました。

実例コード

コードを書いてみました。
やりたいことは以下の通りです。

  • ドライバーの準備とウィンドウハンドルの保存
  • 自分のワードプレス管理画面にログイン
  • 左側のメニュー一覧から「投稿」ボタンを探し、クリック
  • 日付のソートボタンを押して、昇順にする
  • 記事一覧のテーブルを取得し、一番目から記事内容を別タブで表示させる
  • 処理を行って、別タブを閉じる。
  • 二番目の記事を…。これを繰り返す。
  • 最後まで終わったら、記事一覧の2ページ目に移り、また繰り返す。
  • 任意のカウント数まで処理を繰り返す。
def main():
    #ドライバー用意
    driver = webdriver.Chrome()
    driver.get(URL)
    # 現在のタブのウィンドウハンドルを保存
    original_window = driver.current_window_handle
    time.sleep(2)

    #ログイン
    user_login = driver.find_element(By.ID,"user_login")
    user_login.send_keys(USER_ID)

    user_pass = driver.find_element(By.ID,"user_pass")
    user_pass.send_keys(PASS_WORD)

    wp_submit = driver.find_element(By.ID,"wp-submit")
    wp_submit.click()

    time.sleep(2)

    #メニューから投稿ボタンを押す
    menu_list = driver.find_elements(By.CLASS_NAME,"wp-menu-name")
    for menu in menu_list:
        if menu.text == "投稿":
            break
    menu.click()
    time.sleep(2)

    #日付のソート(更新日の古い順)
    date_ = driver.find_element(By.ID,"date")
    date_desc = date_.find_element(By.CSS_SELECTOR,".sorting-indicator.desc")
    date_desc.click()
    time.sleep(1)

    #カウント数
    count = 0
    while count <= MAX_COUNT:
        t_body = driver.find_element(By.ID,"the-list")
        trs = t_body.find_elements(By.TAG_NAME,"tr")
        for i,tr in enumerate(trs):
            print(i+1)
            if count > MAX_COUNT:
                break
            
            article_title = tr.find_element(By.CSS_SELECTOR,".title.column-title.has-row-actions.column-primary.page-title")
            article_title_anker = article_title.find_element(By.TAG_NAME,"a")
            driver.execute_script("window.open(arguments[0].href);", article_title_anker)
            time.sleep(1)

            windows = driver.window_handles
            for window in windows:
                if window != original_window:
                    driver.switch_to.window(window)
                    break
            
            #何らかの処理
            #ここでは省略
            
            driver.close()

            driver.switch_to.window(original_window)
        #次のページに遷移
        next_page = driver.find_element(By.CSS_SELECTOR,".next-page.button")
        next_page.click()

最後に

かなり特殊な使い方をしましたが、セレニウムが使えると結構マニアックなウェブ自動処理も可能です。

もし何か自動化ツールが必要でしたら、プログラミング代行いたします。

ココナラからご依頼可能ですので、よろしくお願いいたします。

依頼はこちらから