Webスクレイピングでは通常、クローラーで取得したHTMLの中から、metaタグにあるページのメタ情報やtableタグにある表形式のデータなどを抽出する。
scriptタグの中に埋め込まれた動的スクリプト内で、varで宣言されたJavaScriptの変数(JSON)に欲しい情報が入っている場合、下記のような方法で抽出できる。
環境
Python 3.9
macOS
コード
import json
import re
from bs4 import BeautifulSoup as bs
def extract_js_var(soup, js_var):
script = soup.find('script', text=re.compile(js_var, re.DOTALL))
regex = '(?:var ' + js_var + ' = )({.*?})(?:;)'
json_str = re.search(regex, script.string).group(1)
return json.loads(json_str)
if __name__ == "__main__":
html = '''
<script>var initialResponse = {"publishDate":"2022-06-28","author":"cgshimo","category":"Python"};</script>
'''
soup = bs(html, 'html.parser')
initial_response = extract_js_var(soup, 'initialResponse')
print(initial_response)
実行結果
$ python3 sample.py
{'publishDate': '2022-06-28', 'author': 'cgshimo', 'category': 'Python'}
extract_js_var 関数をちょこっと解説
1行目
Beautiful Soupのfindメソッドは、第一引数(name)で指定されたタグ名「script」を検索し、最初に見つけた要素を返す。
textで検索対象の正規表現を渡している。ここでは、JavaScriptの変数名を正規表現オブジェクトにコンパイルしている。
2行目
varで宣言された変数(JSON)を抽出するための正規表現を定義している。
抽出(キャプチャ)対象は、2番目に丸括弧で囲われた({.*?})
にマッチする文字列。
丸括弧内の冒頭に?:
をつけることでキャプチャ対象としないことができ、余計な文字列を簡単に除ける。
この正規表現は、実際のHTMLのscriptタグ内の変数の定義のされ方によって適宜修正する。
3行目
2行目で定義した正規表現パターンで、1行目で見つけたscriptタグ要素内の文字列からJSONの抽出を試みる。
末尾のgroup
メソッドで、正規表現内の({.*?})
でキャプチャした文字列を選択している。
4行目
3行目で抽出したJSON文字列をPythonの辞書型にパースして、関数の返り値とする。
このコードの懸念点
ここで使用している正規表現では、下記のようなJSONをパースするときにエラーが出てしまう。
key2のvalueに、};が入っており、これにマッチしてしまう。
JSONにデコードできない文字列として抽出してしまう。
このようなパターンは、JSONの中にスクリプト言語のソースコードが文字列として入ってきた場合などが考えられる。
# エラーとなるケース
var initialResponse = {"key1": "value1", "key2": "val};ue2"};
正規表現をもう少し工夫して、汎用的な関数にできそう。