マナー良いスクレイピング開発で心理的安全性を確保する


なにこれ

あれ?私、開発中のマナー悪くない?

スクレイピングやクローラーのプログラムを書く時に、毎回対象のWebサイトにアクセスしていませんか?
これから有り難く情報を拝借するのに、開発中の大量なアクセスでKPIや分析指標に影響を与えちゃってやたら迷惑かも?
単純に自分がハマっているだけなのに...

そんな心理的な負い目。少なからずあると思います。

...

...

そうだ、Webページをローカルに保存して開発した方が良さそう!

※完全に思いつきで試した開発方法なので全てのケースで適応はできないかもしれませんので悪しからず

この記事で得られるもの

  • Webページをローカルに保存してスクレイピングプログラムが書けるようになる
  • Goのテストがちょっとだけ書けるようになる
  • ある程度のマナーを身につけられる
  • 負い目が減る開発方法を身につけられる

どうやるの?

開発の手順としては以下の通り。

  1. スクレイピング対象のWebページをローカルに完全保存する
  2. ローカルに完全保存したWebページを対象に動作確認を行うテストを書く
  3. スクレイピングの処理を書く
  4. 途中で動作確認を行う場合は、テストを実行することでローカルに完全保存したWebページに向けて検証する
  5. テストで意図通りに動作したら、本番環境に向けてアクセスする。

サンプルを見ながら雰囲気を掴む

以前公開した 阿部寛をWebDriverでいじくるを例にします。GoでWebDriverを使う記事です。メインの処理なんかはこちらを読んでいただけると。

1. スクレイピング対象のWebページをローカルに完全保存する

これはお使いのブラウザから簡単にできますね。割愛します。

2. ローカルに完全保存したWebページを対象に動作確認を行うテストを書く

今回のプログラムではWebDriverを使って開発をするのですが、ローカルのHTMLにもバッチリアクセスできます。

まずはテストコードを書きます。テストコードの中でローカルに保存したWebページ(HTML)に対してアクセスするように定義します。準備はこれだけです。

main_test.go
package main

import (
    "testing"
)

func TestMain(t *testing.T) {
    // ローカルに保存したHTMLに向けてテストする
    pageURL = "file:///abe_hiroshi/DammyHtml/阿部寛のホームページ.html"
    screenShotFileName = "test_screen_shot.jpg"

    // 実際の処理を実行
    main()
}

3. スクレイピングの処理を書く

本処理を記載します。
ここで、テスト時に用いるプロパティについては同パッケージ内からはアクセス可能とするため、グローバル宣言を行いました。こうする事で開発時と運用時でアクセス先を変更する事が可能です。

簡単にトップページにアクセスするところまでを記載します。

テスト時にはテストコードにて上書きされた、pageURLとscreenShotFileNameが利用されます。

main.go
package main

import (
    "log"

    "github.com/sclevine/agouti"
)

// テストコードからもパラメタを変更できる様にパッケージ内ではどこからでも呼べる様にする
var pageURL = "http://abehiroshi.la.coocan.jp/"
var screenShotFileName = "abe_hiroshi.jpg"

func main() {
    driver := agouti.ChromeDriver()
    if err := driver.Start(); err != nil {
        log.Fatalf("driverの起動に失敗しました : %v", err)
    }
    defer driver.Stop()

    page, err := driver.NewPage(agouti.Browser("chrome"))
    if err != nil {
        log.Fatalf("セッション作成に失敗しました : %v", err)
    }

    // 阿部寛のウェブページに遷移する
    if err := page.Navigate(pageURL); err != nil {
        log.Fatalf("アクセスできませんでした : %v", err)
    }
}

4. 途中で動作確認を行う場合は、テストを実行することでローカルに完全保存したWebページに向けて検証する

こちらは上記で説明したように、テストから処理を実行すればローカルに向けてアクセスされるので、先方のサイトにアクセスされません。大量のアクセスも気にせず、思う存分開発もできます💪

5. テストで意図通りに動作したら、本番環境に向けてアクセスする。

これはそのままですね。処理が作成完了したらスクレイピング先の本番環境にアクセスして情報収集しましょう。

おわりに

こうやれば、多少でもマナー良く開発できるかも?と思って試してみました。
今回のボリュームであれば、ローカル保存でも十分に開発できましたが、もっとボリュームが大きくなると問題が出てくるかもしれません。

しかし、アクセスでスクレイピング先に迷惑をかけないことから、色々試したりする事に負い目を感じずに開発する事できました!私のようにスクレイピングに強くないエンジニアにはハマったポイントで何回も試すにはもってこいですね💪

以上です。マナー良く、自分も安心して開発をできる一助になったら嬉しいです!

サンプルのブランチあげておきます。
abe_hiroshi


extra 他のメリット

ここからはまだ試していないので効果がわかっていませんが、想定するメリットを記載します。

前提として、大手企業のシステムに送客するプログラムをWebDriverを用いて作成するケースを想定します。
(案外APIを建ててくれないとか、新規事業のため準備に時間が掛かるため、このようなケースは少なくないのかもしれません。先方にテスト環境が存在しない場合もあります。)

WebDriverを用いた処理のテストを簡単に書ける

WebDriverを用いた連携プログラムのテストを書くのに、外部システムの状態に依存されるテストは書きたくないし、WebDriverのモックを準備するのも大変。

しかし、他システムのWebページを完全保存しておけば、本処理についてはほぼ変更をかけずにテストを書けます。
そして、E2Eテストの準備ができずにテストを諦め、テストカバレッジ0%で安心できないプログラムである状態からある程度のテストカバレッジを担保できるようになります。質の良いテストとは言い難いですが、何もテストが存在しないよりは良いのでは?と思っています。

スクレイピング先の改修箇所がわかりやすい

Webページを完全保存する事で、スクレイピング先の改修があった場合に変更箇所をdiffですぐに特定できるかもしれません。

先方システムの改修を事前告知されず、急にエラーが多発した事が何回もあります。
先方システムの改修のため発生しているのか、自社プロダクトの改修によって引き起こしたバグなのかが調査するのも時間が掛かるのですが、Webページを完全保存していれば、diffにより改修が発生したのかどうかを判別する事が容易になると考えられます。
それにより調査の初速が上がり、対応完了までの時間が短くなると考えられます。

このへんは効果が見えたらまた書くかも。