くすりのしおりスクレイピング

 薬の名前を入力して効能や副作用・注意事項を検索するサイトはたくさんあるが、なかでも「くすりのしおり」は平易な言葉で書かれており、専門家ではなく一般の利用者を対象としたサービスである。

図1.くすりのしおり

図2.検索結果

図3.患者向け説明書

ここではGASを利用してスクレイピング技術を使ってこのサイトを薬剤データベースのように利用する方法を紹介する。

解析方法

まず、GASを使って図2の検索結果を入手する仕組みについて調査する。その場合に有力なツールがブラウザに備わった開発者ツールである。ここではGoogle Chromeの開発者ツールを利用して解析する。

図4は「くすりのしおり」の検索入力画面を開発者ツールを利用して解析している画面である。

図4.検索入力画面の解析

この画面はブラウザの[設定]→[その他のツール]→[デベロッパーツール]を順にたどっていけば表示される。検索窓は次のようなHTMLのフォームでできていることがわかる。

<div class="searchWithKeysBlock">
  <p class="keywords">
    <input type="text" id="q_word" name="q_word" class="placeHolders" value="" title="キーワードをご入力ください" style="color: silver;">
  </p>
</div>

この画面の検索窓から検索したい薬剤名「ビソプロロールフマル酸塩錠2.5mg「日医工」」を入力して[検索]ボタンを押すと次のような検索結果画面が表示される。

図5.検索結果画面の解析

右側の開発者ツールのメニューから[Network]→[Doc]とたどり、[Name]から「kensaku.cgi」をクリックして[Headers]タブを開き、その右に表示された画面を下方向にスクロールすると[Form Data]というタグがあり、CGIに渡されるFORMデータがわかる(下記)。

const formData = {
  'mode': 'key',
  'searchTarget': 1,
  'q_word': 'ビソプロロールフマル酸塩錠2.5mg「日医工」'
};

searchTargetパラメタに1を、q_wordパラメタに検索したい薬剤名を設定しているようである。

また、検索結果のHTMLデータは下記のようになっている。

<h2 class="blockLink">
  <a href="/siori/kekka.cgi?n=38552">ビソプロロールフマル酸塩錠2.5mg「日医工」</a>
  <img class="icon" src="/siori/images/icon_txt.gif" alt="詳細" width="20" height="22">
</h2>

スクレイピングしたい情報は薬剤情報のリンクであるhref属性と<a>タグで囲まれている薬剤名である。そこで、以下のような正規表現を使って2つのパラメタを抽出する。

/<a href="\/siori\/kekka\.cgi\?n=(\d+)">(.*)<\/a>/g

これによって次のようなデータがマッチし、部分文字列"38552"と"ビソプロロールフマル酸塩錠2.5mg「日医工」"が抽出される。

<a href="/siori/kekka.cgi?n=38552">ビソプロロールフマル酸塩錠2.5mg「日医工」</a>

前者は薬品を一意に識別するidで、後者は薬品名称である。

ところで、検索結果ページには薬品の画像がリンクされている。そのHTMLは上記の<h2>タグに後続する下記の<table>タグ内にある。

<table summary="">
  <tbody>
    <tr>
      <td>
        <ul class="figureSpecs cf">
          <li class="eng"><a href="/siori/english/kekka.cgi?n=43522" target="_blank">英語版あり</a></li>
          <li class="comp"><a href="http://www.nihon-generic.co.jp" target="_blank" class="exLink" title="別のタブ(ウィンドウ)で開きます">日本ジェネリック株式会社</a></li>
        </ul>
      </td>
      <td class="w20p img" rowspan="2"><img src="./kusuri_img/622591101_44553.jpg" alt=""></td>
    </tr>
    <tr>
      <td class="operation">通常、本態性高血圧(軽症?中等症)、狭心症、心室性期外収縮、虚血性心疾患または拡張型心筋症に基づく慢性心不全、頻脈性心房細動の治療に用いられます。</td>
    </tr>
  </tbody>
</table>

この中で、画像ファイルへのリンクは次の行である。

<td class="w20p img" rowspan="2"><img src="./kusuri_img/622591101_44553.jpg" alt=""></td>

この中から画像へのリンクを取り出すには、次のような正規表現を使うとよい。

  const re = RegExp('src="./(kusuri_img/.+_' + id + '.jpg)"');

ここで、idは先に抽出した薬品を一意に特定するidである。この正規表現によって画像のurlの一部である"kusuri_img/622591101_44553.jpg"が抽出できる。

GASプログラムによる実装

下記がGASプログラムで実装したものである。

/**
 * 「くすりのしおり」の検索
 */
function queryDrugs(keyword) {
  const BASE_URL = 'https://www.rad-ar.or.jp';
  const URL = BASE_URL + '/siori/kensaku.cgi';
  const formData = {
    'mode': 'key',
    'searchTarget': 1,
    'q_word': keyword
  };
  const options = {
    'method' : 'post',
    'payload' : formData
  };  
  const response = UrlFetchApp.fetch(URL, options);
  const html = response.getContentText('UTF-8');
 
  const re = /<a href="\/siori\/kekka\.cgi\?n=(\d+)">(.*)<\/a>/g;
  let arr = null;
  const results = [];
  while(arr = re.exec(html)) {
    results.push({
      'name': arr[2],
      'url': BASE_URL + '/siori/kekka.cgi?n=' + arr[1],
      'image': BASE_URL + '/siori/' + getDrugImageById(html, arr[1]),
    });
  }
  return results;
}

/**
 * 検索結果の画面htmlからidを指定して薬の画像のURLを取得
 * html ...<td class="w20p img" rowspan="2"><img src="./kusuri_img/622591101_44553.jpg" alt=""></td>...
 */
function getDrugImageById(html, id) {
  const re = RegExp('src="./(kusuri_img/.+_' + id + '.jpg)"');
  const arr = html.match(re);
  return arr[1];
}

関数queryDrugs()は薬剤名を入力として受け取り、その検索結果を返す。たとえば「ビソプロロールフマル酸塩錠2.5mg」を入力してこの関数を実行すると次のような結果を返す。

[
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「日新」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38426",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622296801_38426.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「JG」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=44552",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622591001_44552.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「ZE」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38117",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622267901_38117.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「サワイ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38209",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622296701_38209.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「テバ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38230",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622284601_38230.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「トーワ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=37772",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622339301_37772.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠0.625mg「日医工」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=37626",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622271001_37626.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「JG」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=44553",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622591101_44553.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「ZE」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=37938",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621399302_37938.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「サワイ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38208",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621573402_38208.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「テバ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=42047",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622060402_42047.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「トーワ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38406",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621399401_38406.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「日医工」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38552",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621399602_38552.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠2.5mg「日新」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=46845",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622064802_46845.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「JG」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=44554",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/622591201_44554.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「ZE」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38116",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621399902_38116.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「サワイ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38273",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621400901_38273.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「テバ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=42049",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621400302_42049.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「トーワ」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38407",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621400001_38407.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「日医工」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38553",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621400502_38553.jpg"
 },
 {
  "name": "ビソプロロールフマル酸塩錠5mg「日新」",
  "url": "https://www.rad-ar.or.jp/siori/kekka.cgi?n=38427",
  "image": "https://www.rad-ar.or.jp/siori/kusuri_img/621400601_38427.jpg"
 }
]

このように、薬剤名とその薬剤文書情報のURL、そして薬品の画像のurlの配列が返される。

プロトタイプ

作成した関数queryDrugs()を利用してプロトタイプを試作した。LINE公式アカウントアプリに「@1」を入力すると薬剤(固定値)を検索して薬剤名とそのURLをレスポンスとして返す簡単なものである。

function debug_respondUser(res, userSheet, userMessage) {
  switch(para[1]) {
    case '1':
      const drugs = queryDrugs('ビソプロロールフマル酸塩錠2.5mg「日医工」');
      for(drug of drugs) {
        res.pushLineText(drug.name + '\n' + drug.url);
      }
      break;
    default:
      res.pushLineText('パラメタエラー:' + para[1]);
  }
}

上記でpara[1]に入力パラメタ(@に続く数字)が格納されている。これをスマホで実行したのが次の画面である。

図6.実行結果
応答メッセージ内のURLをタップすると次のような画面が表示される。
図7.薬剤情報画面
このようにシームレスに「くすりのしおり」の機能を利用することができた。

デモシステム

薬剤名:


薬剤情報から情報抽出

図7の薬剤情報画面から項目を指定して情報を抽出する方法を説明する。
下図は薬剤情報画面をブラウザの開発者ツールで確認しているところである。今回は「作用と効用」を抽出してみる。
図8.作用と効用

作用と効果の部分を開発者ツールで探してHTMLを取り出すと次のようになっている。

<h3>この薬の作用と効果について</h3>\n\t\t\t\t\t\t\t\t\t\t\t\t<div class="indent1">交感神経の興奮を心臓に伝えるβ<sub>1</sub>受容体を遮断し、心臓の過剰な働きを抑えることにより、降圧作用、抗狭心症作用、抗不整脈作用、抗心不全作用を示します。<br>通常、本態性高血圧症(軽症?中等症)、狭心症、心室性期外収縮および慢性心不全(虚血性心疾患または拡張型心筋症に基づく)、頻脈性心房細動の治療に用いられます。</div>\n

そこで、以下の正規表現パターンを使って作用と効用の部分を取り出す。

/<h3>この薬の作用と効果について<\/h3>\s*<div class="indent1">(.*)<\/div>/

以下にこの方法を用いて作用と効果を抽出する関数getActionEffectを示す。

function getActionEffect(url) {
  const response = UrlFetchApp.fetch(url);
  const html = response.getContentText('UTF-8');
 
  const re = /<h3>この薬の作用と効果について<\/h3>\s*<div class="indent1">(.*)<\/div>/;
  const arr = html.match(re);

  return arr[1].replace(/<.*?>/g,'');
}

この関数を使って薬剤「ビソプロロールフマル酸塩錠2.5mg「日医工」」から作用と効果を抽出するプログラムを以下に示す。

function test_getActionEffect() {
  const url = 'https://www.rad-ar.or.jp/siori/kekka.cgi?n=38552';
  const action_effect = getActionEffect(url);
  Logger.log('action_effect=' + action_effect);
}

このプログラムの実行結果は次のようになった。

action_effect=交感神経の興奮を心臓に伝えるβ1受容体を遮断し、心臓の過剰な働きを抑えることにより、降圧作用、抗狭心症作用、抗不整脈作用、抗心不全作用を示します。通常、本態性高血圧症(軽症〜中等症)、狭心症、心室性期外収縮および慢性心不全(虚血性心疾患または拡張型心筋症に基づく)、頻脈性心房細動の治療に用いられます。

関数getActionEffectは抽出した作用と効果からHTMLタグを除去している点に注意。

薬剤情報からの情報抽出②

作用と効果だけでなく薬剤情報の全項目を抽出するプログラムを以下に示す。

function getDrugItems(url) {
  const TAG = [
    {'key':'作用・効果','heading':'この薬の作用と効果について'},
    {'key':'使用前の注意','heading':'次のような方は使う前に必ず担当の医師と薬剤師に伝えてください。'},
    {'key':'用法・用量','heading':'用法・用量(この薬の使い方)'},
    {'key':'生活上の注意','heading':'生活上の注意'},
    {'key':'副作用','heading':'この薬を使ったあと気をつけていただくこと(副作用)'},
    {'key':'保管方法','heading':'保管方法その他'},
  ];
  const response = UrlFetchApp.fetch(url);
  const html = response.getContentText('UTF-8');
  const items = {};
  for(tag of TAG) {
    const re = new RegExp('<h3>' + tag.heading + '</h3>(.*?)</tr>\\s*<tr','s');
    const arr = html.match(re);
    const value = arr[1].trim().replace(/<.*?>/g,'').trim();
    items[tag.key] = value;
  }
  //画像のURLを取得
  const re = /<td width="30%" align="center" valign="middle"> <img alt="" src=".\/kusuri_img\/(.*?).jpg/;
  const arr = html.match(re);
  items['image_url'] = 'https://www.rad-ar.or.jp/siori/kusuri_img/' + arr[1] + '.jpg';
  return items;
}

この関数は、薬剤情報のURLを引数に取り、下記のような薬剤情報項目を出力する。

{
 "作用・効果": "交感神経の興奮を心臓に伝えるβ1受容体を遮断し、心臓の過剰な働きを抑えることにより、降圧作用、抗狭心症作用、抗不整脈作用、抗心不全作用を示します。通常、本態性高血圧症(軽症?中等症)、狭心症、心室性期外収縮および慢性心不全(虚血性心疾患または拡張型心筋症に基づく)、頻脈性心房細動の治療に用いられます。",
 "使用前の注意": "以前に薬を使用して、かゆみ、発疹などのアレルギー症状が出たことがある。徐脈、房室ブロック、洞房ブロック、洞不全症候群、糖尿病性ケトアシドーシス、代謝性アシドーシス、心原性ショック、肺高血圧による右心不全、心不全、末梢循環障害、未治療の褐色細胞腫がある。\n\n妊娠、妊娠している可能性がある、授乳中\n\n他に薬などを使っている(お互いに作用を強めたり、弱めたりする可能性もありますので、他に使用中の一般用医薬品や食品も含めて注意してください)。",
 "用法・用量": "あなたの用法・用量は(医療担当者記入)\n\n本態性高血圧症(軽症?中等症)、狭心症、心室性期外収縮:通常、成人は1回2錠(主成分として5mg)を1日1回服用しますが、治療を受ける疾患や年齢・症状により適宜増減されます。虚血性心疾患または拡張型心筋症に基づく慢性心不全:通常、成人は1回主成分として0.625mgを1日1回の服用から始めます。服用を2週間以上続けて忍容性がある(なにごともなく飲み続けられる)場合には、1回1.25mg1日1回に増量されます。その後、忍容性がある場合には4週間以上の間隔で段階的に増量され、忍容性がない場合には減量されます。用量の増減は必ず段階的に行われ、1回0.625mg、1.25mg、2.5mg、3.75mg、または5mgのいずれかを1日1回服用します。年齢・症状により開始用量は更に低用量で、増量幅は更に小さくなる場合もあります。通常、維持量として1回1.25?5mgを1日1回服用します。維持量は適宜増減されますが、最高服用量は1日1回2錠(5mg)までとなっています。頻脈性心房細動:通常、成人は1回1錠(主成分として2.5mg)を1日1回服用しますが、効果が不十分な場合には1回2錠(5mg)に増量されます。なお、年齢・症状により適宜増減されますが、最大量は1日2錠(5mg)までとなっています。本剤は1錠中に主成分2.5mgを含有します。いずれの場合も、必ず指示された服用方法に従ってください。\n\n飲み忘れた場合は気がついたときにできるだけ早く飲んでください。ただし、次に飲む時間が近い場合は飲み忘れた分は飲まないで1回分を飛ばし、次に飲む時間に1回分を飲んでください。絶対に2回分を一度に飲んではいけません。\n\n誤って多く飲んだ場合は医師または薬剤師に相談してください。\n\n医師の指示なしに、自分の判断で飲むのを止めないでください。",
 "生活上の注意": "めまいやふらつきがあらわれることがありますので、自動車の運転など危険を伴う機械を操作する際には十分注意してください。",
 "副作用": "主な副作用として、徐脈、低血圧、めまい、ふらつき、倦怠感、浮腫、呼吸困難などが報告されています。このような症状に気づいたら、担当の医師または薬剤師に相談してください。まれに下記のような症状があらわれ、[ ]内に示した副作用の初期症状である可能性があります。このような場合には、使用をやめて、すぐに医師の診療を受けてください。\n息切れ、呼吸困難、失神(気を失う)[心不全、完全房室ブロック、高度徐脈、洞不全症候群]\n以上の副作用はすべてを記載したものではありません。上記以外でも気になる症状が出た場合は、医師または薬剤師に相談してください。",
 "保管方法": "乳幼児、小児の手の届かないところで、直射日光、高温、湿気を避けて保管してください。薬が残った場合、保管しないで廃棄してください。",
 "image_url": "https://www.rad-ar.or.jp/siori/kusuri_img/621399602_38552.jpg"
}

プロトタイプ

関数getDrugItemsを使って薬剤検索機能を実装した。"@薬剤名"と入力すると、薬剤を検索して最大6つまでの結果をテンプレートカルーセルメッセージに編集して出力する。プログラムを以下に示す。

/**
 * 「@ビソプロロールフマル酸塩錠」などの薬剤検索
 */
function search_drug_info(para, res, userSheet, userMessage) {
  const drugs = queryDrugs(para[1]).slice(0,6);

  const cCols = new CarouselColumn();

  for(let drug of drugs) {
    const items = getDrugItems(drug.url);
    const defaultAction = {
      "type": "uri",
      "label": "詳しく見る",
      "uri": drug.url
    };
    const actions = [];
    actions.push({
      "type": "postback",
      "label": "購入",
      "data": "action=buy&itemid=123"
    });
    actions.push({
      "type": "message",
      "label": "カートに入れる",
      "text": "1週間後に北海道を予約"
    });
    actions.push(defaultAction);
    cCols.add(items.image_url, drug.name.substr(0,40), items['作用・効果'].substr(0,57) + '・・・', defaultAction, actions);
  }
  res.pushLineTemplateCarousel('"' + para[1] + '"の検索結果', cCols.columns); 
}

この関数で、paraは入力データを正規表現で分解したもので、para[1]に検索薬剤名称が格納されている。resはLINE Messaging APIのレスポンスメッセージクラスである(詳細はこの記事参照)。

「@ビソプロロールフマル酸塩」と入力したときの実行結果のスクリーンショットを下図に示す。

図9.薬剤検索
カルーセルにはgetDrugItems関数で取得した薬剤の画像や「作用・効果」を表示している。画像か「詳しく見る」ボタンをタップすると、次のように「くすりのしおり」の該当する薬剤情報画面へブラウザが開いて分岐する。
図10.くすりのしおりの薬剤情報



0 件のコメント:

コメントを投稿