【Python】スクレイピングで取得先のページがHTMLではなくJavaScriptでページを構成していた場合の対応

【Python】スクレイピングで取得先のページがHTMLではなくJavaScriptでページを構成していた場合の対応

スクレイピングしてみたら

前回スクレイピングの方法について書きました。

 

そして、いざ実際の複合機の管理画面のデータをスクレイピングで取得してみたら驚いた。

  • HTMLタグがごそっとない!
  • <body>の直下に<script>があって</script>の次が</body>
  • サイトの構成丸々とJavascriptで作られているやんけ!

JSでサイト表示部分のプログラムまでスクレイピングで取得できていたのでサイトの構造はだいたい理解できた。

さて、

  • スクレイピングで取得できるのは文字列
  • HTMLタグで特定の文字列がとれるけど、jsの変数名はタグじゃないから特定できない。
  • コード全体の文字列は取得可能

さて、どうしたか。

文字列にして分割して分割しました。

着目した点

  • JS部分は一行ごとに「;」がある。
  • 欲しい値はコードの中間部分
  • 欲しい値は変数に配列として記述されている。

やったこと

まずスクレイピングで取得したコードの文字列を「;」で分割して配列に格納した。

#スクレイピング対象のページにアクセス
res = requests.get(conf.printer_job_url)
content = res.content
 
#スクレイピングしたページデータを整形する
soup = BeautifulSoup(content, 'html.parser')
 
# prettify()で文字列に出力
#;で分割して配列に格納する
array_html = soup.prettify().split(';')

これでスクレイピングで取得したページのデータを配列に格納できた。

次にその中からJSの変数の値を取り出すことに

  • 配列を一個づつ読み込み変数名をfindで探す。
  • 変数名がヒットしたら文字列から値の部分の文字列だけを取得する。
  • jsではカンマで区切られていたので同様にカンマで区切って配列へ格納する。
  • 配列は1配列毎[]とカンマで区切られているので、全体をカンマで区切ると大変なことになる。
  • なので、配列の区切りを別のものに変更する。
  • “],[“→”別の区切り文字”
# 配列を1個ずつ読み込む
 for item_html in array_html:
     # 変数名を検索する
     if item_html.find('var jHst=') >= 0:
         # 変数を見つけた
         # 変数名の部分を削除する
         item_html = item_html[14:]
         # 文字列の長さを取得する
         nagasa = len(item_html)
         # 後ろの'"]'を削除する
         nokosu = nagasa - 2
         item_html = item_html[:nokosu]
         # 区切り文字を変換する
         item_html = item_html.replace('],[','KOKO')
         # 区切り文字で分割して配列に格納する
         lists = item_html.split('KOKO')

これで1件分の値を1行の文字列で配列に格納できた。
次に1行の文字列から1項目ごとに分解する。

まずは配列の値になっていたので文字列上ではシングルコーテーションが付いている。
邪魔なので取っ払う。
次に文字列から配列にするためにカンマで分割する。
これで1項目ごと配列にできたので後は煮るなり焼くなり処理を実施する。

for list in lists:
     # シングルコーテーションを削除する
     list = list.replace('\'','')
     
     # カンマで分割して配列に格納する
     items = list.split(',')
     
   #何らかの処理
      
 # 値の部分を処理したらループを抜ける
 break

文字列に区切り文字が入っているとエラーになる

今回のスクレイピングで取得した項目で1項目にはファイル名が入るんだが、
ファイル名にカンマが入っていると文字列の分割の時に配列の数が合わなくなりその後の処理でエラーを乱発する事態が起こってしまった。

後ろの項目に日付部分がある。
日付以外の分割部分が日付の配列位置にずれると日付判定の処理でエラーになる。

’’シングルコーテーションで囲まれているカンマを区切り文字から省けないかなと調べてみたがわからない。
偶然見つけた方法があったのでなんとかそれで対応できた。

  • 通常は左の区切りの方向を右にする。
  • 区切る回数を固定にする。
'右から4個に分割する
 items  = list.rsplit(',',4)

今回は事前に欲しい部分がスクレイピングの段階でわかっていたのでずいぶん泥臭い方法で対応することができた。
欲しかった部分が配列になっていたので、本来ならAPIとかで処理出来たらもっとスマートにできたんだろうと思う。