質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.36%

  • JavaScript

    11208questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • HTML

    6165questions

    HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

  • Python

    3818questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

HTMLからJavaScriptとして実行される部分だけを抜き出す方法

解決済

回答 4

投稿 2017/11/22 15:16 ・編集 2017/11/23 12:50

  • 評価
  • クリップ 0
  • VIEW 393

fire_fire_2

score 1

前提

HTMLを入力したらJavaScriptの部分だけを抜き出すようなプログラムを作成したいです。
javascript自体が外部ファイル化している場合はひとまず無視してください。

用途は言えませんが、実現したい具体的なことは以下のとおりです。

入力するHTMLファイル

<HTML>
<HEAD>
</HEAD>
<BODY>
<a href="#" onclick="alert('OKOK'); return false;"></a>
<script>
document.write('ABCDEFG')
</script>
</BODY>
</HTML>

修正(2017/11/22)
"が被って実行できないということなので'に修正

抜き出しのプログラムを実行

alert('OKOK'); return false;
document.write('ABCDEFG')

試したこと

XMLとして、JavaScriptが書かれている属性名や要素名で(script要素やonclick属性など)を指定して取り出す事はできましたが、HTMLの仕様としてJavaScriptが書かれる属性というのは結構たくさんありますので全部を指定して取り出すのは現実的でないと思っています。

実現したいこと

できれば、JavaScriptエンジンとかJavaScriptだと認識できるようなライブラリなどを利用して簡単にJavaScript部分だけを取り出せればと思っております。言語はできればPythonで書きたいです。
みなさんのお知恵を貸してください。よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fire_fire_2

    2017/11/22 21:24

    やはり、要素や属性を抜き出すしか方法はないんでしょうか?seleniumみたいなWebDriverを使って、プラウザがHTMLをパースした時にJavaScriptとして実行すべき部分を抜き出す、みたいな方法はないですかねー?

    キャンセル

  • defghi1977

    2017/11/22 22:01

    穿った書き方をするとevalを噛ませればscript要素以外にもスクリプトは仕込めるわけで, もう少し条件や用途を絞らないと解決策は見つからないかと

    キャンセル

  • fire_fire_2

    2017/11/23 12:49

    evel関数も何かしらのスクリプトと認識されるとおもうので、中でどのようなスクリプを書いていてもJavaScriptで扱われる文字列として認識されるとおもうのですがどうなのでしょうか?ブラウザが使っているHTMLパーサーなどをプログラム内で使うことはできないんでしょうか?そうすればJavaScriptだと認識できるところというのはわかる気もするんですが。

    キャンセル

回答 4

checkベストアンサー

+5

JavaScriptエンジンとかJavaScriptだと認識できるようなライブラリ

上記のようなものはHTMLパーサーがscript部分を抜き出した後に適用されるものだと思います。本件の焦点はjavascriptの構文ではなくHTMLの構文ではないでしょうか?精度のある結果を求めるならHTMLとしてパースし、結果のDOMからしかるべきelementのattributeやtextを抜き出す方法が妥当だと思います。ただ、そうしたものは本回答欄に書くには少々大きすぎる類のものではないかと思います。

JavaScriptが書かれる属性というのは結構たくさんありますので全部を指定して取り出すのは現実的でない

仕様を網羅して全ての属性を指定するのが現実的でないかどうかはよくわかりませんが、しかし誰かがそういうプログラムを作るとしたら結局のところ「全部指定して取り出す」他はない気がします。つまり「ご質問のようなことを既に実装しているツールを探す」のでないなら全部指定することは避けられない気がします。

ただ、なんとなくですが「全てのelementの"on"で始まる名前のattributeを取り出せば少なくともイベント関連は網羅できそう」な気がするので、それで間に合わせるという妥協はできるのかも知れません。イベント以外でjavascriptを指定できる属性があるかどうか残念ながら自分は知りません。


ところで「対象のHTMLのパターンをある程度限定できる」ならそうまで厳密な手法をとらずともパターンマッチでそこそこの結果を得ることができると思います。

例えば以下のようなものでもなにがしかの役には立つかもしれません。個人的に手軽な気がしたのでNode.jsで試してみましたが、近頃のスクリプト言語では「テキスト処理が不得意」なんてものはないと思うのでなんでもよいと思います。

// ES2015(ES6)
const fs = require('fs');

function* pickupScript(htmlFile) {
  const html = fs.readFileSync('./sample.html', { encoding: 'utf8' });
  const elmatr = [
    // イベントの一覧は例えばhttps://www.w3schools.com/tags/ref_eventattributes.asp
    // などみて列挙すれば列挙できなくはないと思います。100個に届かないぐらいと思います。
    // window
    "onafterprint",
    "onbeforeprint",
    ...
    // misc
    "onshow",
    "ontoggle",
  ].map(eventName => {
    const p = '[^a-z_]' + eventName + '\\s*=\\s*(["\'])(.+?)\\1';
    return new RegExp(p, 'ig');
  });
  for (let re of elmatr) {
    for (let m; m = re.exec(html); ) {
      yield m[2];
    }
  }

  // javascript以外であっても取り出してしまいます。
  // 1行にscriptが1つしか書かれていないパターンしか想定していません。
  // タグの構文は典型的と思えたパターンしか配慮していません。
  const re2 = /^\s*<script(\s+[a-z]+\s*=["'].*?["'])*\s*>\s*$/i
  const re3 = /^\s*<\/script>\s*$/i
  let inside = false;
  for (let line of html.split('\n')) {
    if (!inside)
      inside = line.match(re2);
    else {
      inside = !line.match(re3);
      if (inside) yield line;
    }
  }
}

for (let g = pickupScript('./sample.html'), m; m = g.next().value; ) {
  console.log(m);
}

sample.html

<HTML>
<HEAD>
  <script>
  function onLoad() {
    document.getElementById('a').innerHTML = 'ABCDEFG';
  }
  </SCRIPT>
</HEAD>
<BODY ONLOAD='onLoad()'>
  <a href="#" onclick="alert('OK'); return true;"></a>
  <a href="#" onclick="alert('NG'); return false;"></a>
  <div id="a"></div>
</BODY>
</HTML>


結果

onLoad()
alert('OK'); return true;
alert('NG'); return false;
  function onLoad() {
    document.getElementById('a').innerHTML = 'ABCDEFG';
  }

投稿 2017/11/22 21:52

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/23 16:14

    ワンラインになっている<Script>hogehoge()</script>からhogehoge()を抜き出す処理も追加してもらえませんか?nodeで結構ですので・・。

    キャンセル

  • 2017/11/23 16:29 編集

    const re4=/<script>(.+?)<\/script>/ig;
    for (let m; m = re4.exec(html); ) {
    yield m[1];
    }
    をpickupScript本体に追加するぐらいでしょうか。ただし上記のコードの意味を把握せずに使っていると落とし穴が(マッチできない・不当にマッチする)ことになりますのでそこはご注意を。yambejpさんがおっしゃるようにあくまで「そこそこの精度」でしかありませんので。
    ---すみません、typoあったので直しました。

    キャンセル

  • 2017/11/23 16:27

    ありがとうございます。正規表現苦手なので助かりました。

    キャンセル

  • 2017/11/23 17:16

    '''
    const re2 = /^\s*<script(\s+[a-z]+\s*=["'].*?["'])*\s*>\s*$/i
    const re3 = /^\s*<\/script>\s*$/i
    const re4 = /<script>(.+?)<\/script>/i;
    let inside = false;
    let onelinescript = false;
    for (let line of html.split('\n')) {
    if (!inside){
    inside = line.match(re2);
    onelinescript = line.match(re4);
    if(onelinescript){
    // console.log(onelinescript[1]);
    yield onelinescript[1];
    onelinescript = false;
    }
    }else{
    inside = !line.match(re3);
    if (inside) yield line;
    }
    }
    '''
    先程のコードを参考に、上のように行が分かれているScriptタグを処理する部分でついでに、ワンラインのスクリプトタグも処理できるように改良しました。しかし、これでは<script>hogehoge()</script>は抜き出せても、<script type="text/javascript">hogehoge()</script>は抜き出せなかったので、re4の正規表現を
    '''
    const re4 = /<script(\s+[a-z]+\s*=["'].*?["'])*\s*>(.+?)<\/script>/i;
    '''
    このようにしたのですが、うまく抜き出せれません。訂正すべきところを教えてください。

    キャンセル

  • 2017/11/23 17:25

    RegExp.execute(文字列)とすると、結果は配列になるのですが、正規表現の中に括弧でグループ化されている部分が以下のように2個あったとき
    /...(パターンA)...(パターンB).../
    executeの結果は3要素となり、0番目がマッチした全体部分、1番目がパターンAの部分にマッチしたもの、2番目がBの部分にマッチしたものとなります。これはJavascriptに限った話ではなく、グループ化された正規表現のマッチ結果としてよく用いられる手法ですので覚えておくとよいと思います。さて変更されたre4にはグループが2つあり、興味あるのは2番目の方ですので、自分が挙げた最初のコメント中の変数mを用いるとm[2]と書けばよいです。

    キャンセル

  • 2017/11/23 20:52

    なるほど!、すごい初歩的な事なのにご丁寧にありがとうございました。

    キャンセル

+3

まず

<a href="#" onclick="alert("OKOK"); return false;">

クォーテーションが競合しているので、例示のonclickは実行できません。
またonclickが実行するものがかならずしもjavascriptとは言えません
加えてjavascript自体が外部ファイル化シている場合どういう仕様にしたいのかもわかりません。
総じて基本的には無理だとおもって差し支えないと思います。

逆に拡大解釈していいならonXXX=○○の箇所(トリガーはclick以外もある)と
<script~</script>までをそれぞれ正規表現などで抽出すれば
精度は低いですが文字列を取り出すことはできるとおもいます。

投稿 2017/11/22 15:34

編集 2017/11/22 15:36

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

とりあえず、javascriptでイベントを呼ぶ属性を出力するコードを書いてみました。
サンプル

var onAttributes = Object.getOwnPropertyNames(HTMLElement.prototype).filter( x => x.startsWith('on') );
document.querySelectorAll('*').forEach( e => onAttributes.forEach( p => e[p] && console.log(e[p].toSource()) ) );

投稿 2017/11/22 17:50

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

Python にはスクレイピングのモジュールがあります。これで<script>のなかみを取り出すことができます。

https://qiita.com/itkr/items/513318a9b5b92bd56185

ここなど見てください。

投稿 2017/11/22 21:24

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.36%

関連した質問

同じタグがついた質問を見る

  • JavaScript

    11208questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • HTML

    6165questions

    HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

  • Python

    3818questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。