Help us understand the problem. What is going on with this article?

reCAPTCHAのサイトを毎日自動スクレイピングする (2/7: スクレイピング)

  1. 要件定義〜python環境構築
  2. サイトのスクレイピング機構を作る
  3. ダウンロードしたファイル(xls)を加工し、最終成果物(csv)を作成するようにする
  4. S3からのファイルダウンロード / S3へのファイルアップロードをつくる
  5. 2captchaを実装
  6. Dockerコンテナで起動できるようにする
  7. AWS batchに登録

スクレイピング機構の作成

今回のサイトはReactを使っており、javascriptの動作であるためseleniumを使います。

ファイル構成をおさらい
├── app
│   ├── drivers          seleniumドライバーを置く
│   └── source           
│       └── scraping.py  処理
└── tmp
    ├── files
    │   └── download     スクレイピングでダウンロードしたファイルを置く
    └── logs             ログ(seleniumログなど)

driverのダウンロード

どのブラウザを使ってseleniumを動かすのか、決める必要があります。
候補としては下記の3つくらいがありそう。

  • ヘッドレス専用ブラウザとして一世風靡した? PhantomJS
  • 最近 headlessモードができて今から主流になりつつある Chrome
  • 初代seleniumといえば Firefox

最初はChromeを使っていたのですが、後に出てくるXvfbとの相性が悪く、うまく動作しなかったため… Firefoxを使うことになりました。上記URLからドライバーをダウンロードし、/drivers/ 以下に配置してください。1

また、Firefoxのgeckodriverを動かすためには、OSにFirefoxがインストールされていることが必要となります。まだ入っていなければ 公式サイトからダウンロードしてもらえればと。

ダウンロード先フォルダを準備

ついにコーディングです。

  1. ダウンロードしたファイルを特定フォルダに入れる
  2. 1を加工して、成果物データ(csv)を作成。別のフォルダに入れる
  3. 2をS3に送る

としていくことにするため、ダウンロード先のフォルダを用意します。

scraping.py
date = datetime.now().strftime('%Y%m%d')
dldir_name = os.path.abspath(__file__ + '/../../../tmp/files/download/{}/'.format(date))
dldir_path = Path(dldir_name)
dldir_path.mkdir(exist_ok=True)
download_dir = str(dldir_path.resolve())

import文などは最後にまとめて紹介します。
…なんだかコードが冗長な気がしますが、動作したのでこれでいいことにします。

seleniumの起動までを書く

次に、seleniumでgeckodriverを起動させるところまでを記載します。
Firefoxの場合、普通に起動するとダウンロードダイアログが出てしまうので、それが出ないようにオプションをいろいろと指定する必要があります。

scraping.py
driver_path = os.path.abspath(__file__ + '/../../drivers/geckodriver') #driverの位置を指定します。
fp = webdriver.FirefoxProfile()
fp.set_preference("browser.download.folderList",2) # 「ダウンロードフォルダを指定する」ためのオプションだったような。
fp.set_preference("browser.download.dir", download_dir)
fp.set_preference("browser.download.manager.showWhenStarting",False) #何だろう?わからない
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
log_path = os.path.abspath(__file__ + '/../../../tmp/logs/geckodriver.log') #何もしないでいると、実行ファイルの位置にlogファイルが生成されてしまう
driver = webdriver.Firefox(firefox_profile=fp,executable_path=driver_path,service_log_path=log_path)

driver.maximize_window() #Windowサイズによってサイドメニューが消えたりする…
driver.implicitly_wait(10)  #10秒間指定の項目がなければエラー

要注意なのは helperApps.neverAsk.saveToDisk のオプションです。
ここで指定したファイル形式に限り、「ダウンロードしますか?」というダイアログがでないようになります。今回ダウンロードするxlsファイルはapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetになるようでした。2

ちなみに、chromeだとこんなに簡単でした

scraping.py
driver_path = os.path.abspath(__file__ + '/../../drivers/chromedriver')
options = webdriver.ChromeOptions()
options.add_experimental_option("prefs", {"download.default_directory": download_dir})
driver = webdriver.Chrome(executable_path=driver_path, options=options)

ログイン処理

今回のサイトの場合は、下記のようにするとログインができました。

scraping.py
#ログイン
driver.get(LOGIN_URL)
mail_address = driver.find_element_by_id("mail_address")
mail_address.send_keys(config.mail_address)
password = driver.find_element_by_id("password")
password.send_keys(config.password)
submit_button = driver.find_element_by_css_selector("button.submit")
submit_button.click()

…ただし、クリックしたときに reCAPTCHAが出ますので、手動でこれを解除する必要があります。
あとで2captchaを使って解決するものですが、この時点ではまだないので、出たら自分で解除します。

解除するまで処理を待ってもらうため、wait処理を入れます。下記の例だと、最大100秒待つことができます。

scraping.py
try:
  WebDriverWait(driver, 100).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "※ログイン後に表示される要素"))
  )
except TimeoutException:
    driver.quit()
    exit('Error:ログインに失敗しました')

データの取得

さて、ようやくデータの取得です。こんな感じにやりました。

scraping.py
# 検索ワードを使って検索
search_box = driver.find_element_by_xpath(SEARCH_BOX_XPATH)
search_box.clear()
search_box.click()
search_box.send_keys(word) #検索窓に入力
time.sleep(INTERVAL) #React処理を待つため
search_box.send_keys(Keys.RETURN) #Returnキー → 最初の検索結果が選択される

# メニューを開く
try:
    driver.find_element_by_xpath(MENU_XPATH).click()
except ElementNotInteractableException:
    #メニューが選択できないとき → アコーディオンが開かれていない
    driver.find_element_by_xpath(MENU_OPEN_XPATH).click()            
    driver.find_element_by_xpath(MENU_XPATH).click()

# ダウンロード
driver.find_element_by_xpath(DOWNLOAD_XPATH).click()
  • 最近のreactiveなフロントエンドフレームワークの場合、csspathでの指定が難しかったりします。そのため、基本的にはxpathを使って要素を指定することになりました。
  • React等の場合、javascriptの動作が終わるのを待たないとうまくいかないことが多々ありました。time.sleep()をうまく活用しましょう。
  • 要素がないときには ElementNotInteractableExceptionがthrowされます。「この要素があるか否か?」を調べるメソッドは無いようなので、うまく活用しましょう。
  • 通常のサイトなら、ダウンロードはわざわざクリックしなくても URLを叩けば手に入るものが多いかと思うのですが… 今回のサイトはclickする必要がありました

完成

これまでに紹介したものをつなげてやれば、一旦ダウンロード部分は完成です!
最後に、import文を紹介しておきます。

scraping.py
import os
import time
from pathlib import Path
from datetime import datetime

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import ElementNotInteractableException
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException

続きはまた。


  1. 確か、最新のもの(mac版)をダウンロードした気がします…。なお、macOS上の所定の位置にdriverを配置すると 起動時にファイルを指定しなくてよくなるようですが、今回はその方法を使っていません。 

  2. 正式なxlsのファイル形式は違うものなのですが、実際にダウンロードされるファイルの形式を指定する必要があります 

kamyu1201@github
javaエンジニア6年 → SE兼エンジニアMgr2年 → 現在は Product Manager の3年目です。 `ちゃんと使われるシステム` にこだわりたい。 モダンなシステム開発ができるよう、エンジニア再修行中。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした