#はじめに
ICFとは,WHOが作成した国際機能分類のことで,人の健康状態をコードを用いて表現することができるツールです.例えば,「b280 痛みの感覚」は身体部位の損傷やその可能性を示す不愉快な感覚のことで,「b280 4」のようにコードの後ろにレベルをつけることで,そのICF項目がどれほど問題のある状態なのかを表せます.ちなみにレベル4は重症なので痛みの感覚がないことを表すコードになってると思います.(筆者は医療関係者ではないため細かな間違いは多々あると思います.)
このICFコードですが,研究で使いたいと思ったら,調べた限りデータが公開されていなかったので自分でデータベース化しようと思いました.そこで,ICFをweb上で検索できる「ICFコード検索データベース」というサイトをスクレイピングする方法を試してみたという記事になります.
#準備
BeautifulSoupをインストールします!
$ pip install beautifulsoup4
#方針
ICFは「b280」のように「"アルファベット" + 〇〇〇〇〇」といった構成になっていて,〇には0~9の数字が入ります.つまり「アルファベット一文字と最大5桁の10進数の組み合わせ」です.今回はbrute force(総当たり)的なアプローチでコードの全パターンを生成し,生成した各コードを「ICFコード検索データベース」上でクエリに指定し,その検索結果からデータを取得しようと思います.
#コーディング
全体では,「brute forceでクエリに指定するICFコードを生成」→「ICFコードをスクレイピングのURLに挿入して,リクエストを送り,結果を受け取る」→「beautifulsoupで受け取ったレスポンスを解析して必要なデータを抜き取る」→「取ってきたデータをcsvで出力する」という流れでコードを書いていきます.
##brute forceでクエリに指定するICFコードを生成
「アルファベット一文字と最大5桁の10進数の組み合わせ」を全パターン生成していきます!書いそびれていましたが,アルファベットは例に挙げた'b'だけでなく,'b','s','d','e'の4つです.
BSDE = ['b', 's', 'd', 'e']
def icf(n):
if (int(n/10)):
return icf(int(n/9)) + str(n%9)
return str(n%9)
for head in BSDE:
for N in [1, 2, 3, 4, 5]:
for i in range(9**N):
num = icf(i).zfill(N)
code = head + num
zfillを使うのが,少しテクニカルだと思っています.
計算量は多いですが,今回はクエリに使用するため,1~5桁の全パターンを作らないといけません.例えば,「b00280」をクエリにしても「b280 痛みの感覚」という検索結果は出てこないです.従って,このように組みました.
##ICFコードをスクレイピングのURLに挿入して,リクエストを送り,結果を受け取る
実際に「ICFコード検索データベース」の"ICFコードで検索"に「b280」とクエリを指定して検索すると,遷移先のURLは以下になります.
http://www.icfcy-jpn.org/e-angel/icfdb/icfdb_all.cgi?IDv001=&IDn001=AND&keys1=b280&keys2=&keys3=&keys4=&keys5=&keys6=&print=10)
ちょうど真ん中あたりに「keys1=b280」のようにクエリが確認できます.そのため,ここにbrute forceで生成したICFコードに毎回置き換えて指定することで,生成したICFコードをクエリとした検索結果を受け取ることができます.
import requests
from urllib.request import urlopen
pre_url = 'http://www.icfcy-jpn.org/e-angel/icfdb/icfdb_all.cgi?IDv001=&IDn001=AND&keys1='
post_url = '&keys2=&keys3=&keys4=&keys5=&keys6=&print=1'
url = pre_url + code + post_url #codeは先に生成したICFコード
html = urlopen(url)
##beautifulsoupで受け取ったレスポンスを解析して必要なデータを抜き取る
from bs4 import BeautifulSoup
write_list=[]
soup = BeautifulSoup(html, "html.parser") #htmlは先の検索結果を格納したもの
try:
table = soup.findAll("table")[0]
rows = table.findAll("td", {"width":"85%"})
write_list.append([rows[0].get_text(), rows[1].get_text(), rows[3].get_text(), rows[4].get_text()])
except:
pass
「ICFコード検索データベース」の検索結果はHTMLでいうとtableとして返ってくるのでbeautifulsoupを使ってtable要素だけを抽出します.また,さらにtable要素の中でも必要なデータ(今回は,"ICFコード", "タイトル", "カテゴリー", "解説"のみ)を抽出する.
ここで,「b00280」と検索しても0をスルーして「b280」がヒットすることはないので,0件ヒットになってしまいます.その場合を想定してtry&exceptでエラー処理を記述します.
##取ってきたデータをcsvで出力する
import csv
with open('icf_database.csv', 'w') as f:
writer = csv.writer(f)
writer.writerows(write_list) #write_listには抽出した必要なデータが二次元配列で格納されている
画像のようにcsvをexcelにインポートするとICFデータベースを取得できたことが確認できます.文字化けする場合は,文字コードが[UTF-8]になっているかを確認してみてください.