Jsoup - JavaでHTMLコードを抽出して解析する

Jsoup は、書店 de Java 誰がコードを処理する HTML。これは、API 最適な方法を使用してデータを抽出および操作するのに非常に便利です。DOM, CSS et jquery.
jsoup  は HTML5 の特性を実装し、最新のブラウザーと同様にツリー (DOM) 内の HTML コードを解析します。JSOUPは、MIT ライセンス。ソースコードは にあります。GitHub.
機能:
- URL、ファイル、または文字列からHTMLコードを解析します.
- DOM ツリーと CSS.
- 要素とテキストを操作する HTML.
- ユーザーのサブスクリプションを消去して XSS 攻撃を防止する.
-整頓されたHTMLコードを作成します.
    < / ul>
    JSoupは、HTMLのさまざまなバージョンに適応するように設計されており、分析されたtree.

    例:

    ウィキペディアのページを取得し、それを変換してDOMとして解析し、ニュースセクションから項目のリストを選択します。
    ドキュメント doc = Jsoup.connect("http://fr.wikipedia.org").get();

    1 - ドキュメントの解析

    このコードは分析用です document HTML:

    String html = "<ヘッド>最初のタイトル"<br /> + "<ボディ><ドキュメント化するコード例";<br /><b>Document</b> doc = <b>Jsoup</b>.parse(html);<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">パーサーは、提供されたコードから明確なHTMLコードを作成するために最善を尽くします。</span><br /><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;">-閉じられていないタグ(例:<ブラブラは<に変身しますブラブラ</span><br /><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;">- 暗黙的なタグ 例: </span><br /><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;">    <TD> テーブルで囲まれている</span><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;"> <テーブル><tr><TD></span><br /><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;">-作成されたコードの構造は、HTML言語の標準(ヘッド、ボディ、およびページを構成する要素)を尊重します.</span><br /><span style="background-color: white;font-family: 'ソースSans Pro',sans-serif;"> </span></div><h3 style="background: white;mso-line-height-alt: 11.9pt;mso-margin-bottom-alt:自動;mso-margin-top-alt:自動;mso-outline-level: 3;"><b><span style="font-family: 'ソースSans Pro',sans-serif;">2- Stringからドキュメントを解析</span></b></h3><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">HTMLをJavaの文字列でまとめたので、それを分解してコンテンツを取得したり、適切に記述されているかどうかを確認したり、変更したりします。エントリは、ファイルまたはWebのリンクから読み取ることができます.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">事前定義された方法を使用する必要があります <i>Jsoup.parse(文字列html)</i> または <i>Jsoup.parse(文字列html, 文字列 uri)</i> ページがウェブからのものである場合。</span><span lang="EN-US" style="font-family: 'Source Sans Pro',sans-serif;">参照 </span><span style="font-family: 'Source Sans Pro',sans-serif;"><span lang="EN-US" style="color: black;mso-ansi言語:EN-US;mso-themecolor: text1;"><a href="http://jsoup.org/apidocs/org/jsoup/Jsoup.html">class Jsoup.</a></span></span><span lang="EN-US" style="font-family: 'Source Sans Pro',sans-serif;"><o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><div class="MsoNormal" style="background: #E7EBF2;ラインの高さ: 13.85pt;マージンボトム:.0001pt;マージンボトム:0cm;タブストップ: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;"><b><span lang="EN-US" style="font-family: 'Source Sans Pro',sans-serif;">String</span></b><span lang="EN-US" style="font-family: 'Source Sans Pro',sans-serif;"> html = "<html><ヘッド><title>タイトル"<br /> + "<ボディ><p>html en doc";<br /><b>Document</b> doc = <b>Jsoup</b>.parse(html);<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: inherit;">メソッド Jsoup.parse(文字列html, 文字列URI) 入力された HTML コードを新しい文書。<i>URI </i> 引数は、</i>相対 <i>URL を絶対 URL に変換するために使用されます。予定 ドキュメントが URL で初期化されます 回復。それが不可能な場合は、 Jsoup.parse(String html).<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: inherit;">パラメータとして渡される文字列はnullであってはならず、出力ドキュメントは</span><i style="font-family: inherit;"未満で構成されます。>head </i><span style="font-family: inherit;">et de </span><i style="font-family: inherit;">body</i><span style="font-family: inherit;">.もしあなたが </span>obturerez<span style="font-family: inherit;"> ツリーの抽出が行われていないことがわかる例外。正しく読んでください</span><span style="color: blue;font-family: inherit;"><a href="http://jsoup.org/apidocs/">documentation</a> </span><span style="font-family: inherit;">詳細については.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: inherit;">ドキュメントを取得すると、次のことができるようになります。リカバリ クラスのメソッドを持つデータ ドキュメントと上位グレード <span style="color: blue;"><a href="http://jsoup.org/apidocs/org/jsoup/nodes/Node.html">Node</a> </span>et <a href="http://jsoup.org/apidocs/org/jsoup/nodes/Element.html"><span style="color: blue;">Element</span></a>.</span><span style="font-family: Times New Roman, serif;"><o:p></o:p></span></div><h3 style="background: white;mso-line-height-alt: 11.9pt;mso-margin-bottom-alt:自動;mso-margin-top-alt:自動;mso-outline-level: 3;"><b><span style="font-family: 'ソースSans Pro',sans-serif;">3- body</span></b></h3><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">分析したいボディスーツがあります。コードには、たとえば comments.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">解決策は、Jsoup.parseBodyFragment(String html).<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><div class="MsoNormal" style="background: #E7EBF2;ラインの高さ: 13.85pt;マージンボトム:.0001pt;マージンボトム:0cm;タブストップ: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;"><b><span style="font-family: 'ソースSans Pro',sans-serif;">String</span></b><span style="font-family: 'Source Sans Pro',sans-serif;"> html="<div です><段落";<br /><b>Document</b> doc = <b>Jsoup</b>.parseBodyFragment(html);<br /><b>Element</b> body = doc.body();<o:p></o:p></span></div><div class="MsoNormal" style="background: white;margin-bottom: 0.0001pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><div class="MsoNormal" style="background: white;margin-bottom: 0.0001pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"> </span><br /><span style="font-family: 'Source Sans Pro',sans-serif;"><i>parseBodyFragment</i> メソッドは、空のドキュメントを作成し、解析された HTML を本文に挿入します。もし、<span style="color: #990000;">Jsoup.parse(String html)</span>の場合、 同じ 結果は、出力を ボディフラグメントは、指定されたすべての要素が解析されたことを保証します.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;margin-bottom: 0.0001pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">Document.body() メソッドは要素を取得します 下(子)体は、以下と同等です。<span style="color: #990000;">doc.getElementsByTag("body")</span>.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: Times New Roman, serif;">次の場合は注意を怠らないことをお勧めします。回復するスクリプトを含むサイトからのデータ潑。のドキュメントを見る</span><a href="http://jsoup.org/apidocs/org/jsoup/safety/Whitelist.html" style="font-family: 'Source Sans Pro',sans-serif;"><span style="color: blue;">whitelist</span></a><span style="font-family: Times New Roman, serif;"> そして、注文 clean(String html, Whitelist whitelist).</span></div><h3 style="background: white;"><b><span style="font-family: Times New Roman, serif;">4- URLからドキュメントを読み込む</span></b></h3><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;">Web から HTML ドキュメントを検索して解析し、データを見つけるには、<i>Jsoup.connect(String url)</i>.<o:p></o:p></span></div><div class="MsoNormal" style="background: white;マージンボトム:.0001pt;マージンボトム:0cm;mso -行の高さ- alt:11.9pt;"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><pre style="background: #E7EBF2;行の高さ:13.85pt;"><span class="typ"><b><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">Document</span></b></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> doc </span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">=</span></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> </span></span><span class="typ"><b><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">Jsoup</span></b></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">.</span></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">connect</span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">(</span></span><span class="str"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: 'Source Sans Pro',sans-serif;">"http://siteweb.com/"</span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">).</span></span><span class="kwd"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">get</span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">();</span></span><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"><br /><span class="typ"><b>String</b></span><span class="pln"> title </span><span class="pun">=</span><span class="pln"> doc</span><span class="pun">.</span><span class="pln">title</span><span class="pun">();</span></span></pre><div class="MsoNormal"><span style="font-family: Times New Roman, serif;"<>span style="color: #990000;" メソッド>connect(String url)</span> は、新しい<span style="line-height: 18.3999996185303px;">connexion</span><span style="line-height: 115%;"> get() は HTML ファイルを検索して解析します。a エラーが表示された場合、例外は</span><span style="line-height: 18.4px;">being</span><span style="line-height: 115%;"> </span><span style="line-height: 18.4px;">triggered</span><span style="line-height: 115%;">.<o:p></o:p></span></span></div><div class="MsoNormal"><span style="font-family: Times New Roman, serif;">インターフェース </span><span style="font-family: 'ソースSans Pro',sans-serif;行の高さ:18.39999996185303px;">connexion</span><span style="font-family: 'ソースSans Pro',sans-serif;行の高さ:18.39999996185303px;"> </span><span style="font-family: Times New Roman, serif;"><span style="line-height: 115%;">は </span><span style="line-height: 18.3999996185303px;">chaining</span><span style="line-height: 115%;"> 具体的な回答はこちら :<o:p></o:p></span></span></div><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;行の高さ:115%;"><br /></span></div><pre style="background: #E7EBF2;行の高さ:13.85pt;"><span class="typ"><b><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">Document</span></b></span><span class="pln"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> doc </span></span><span class="pun"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">=</span></span><span class="pln"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> </span></span><span class="typ"><b><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">Jsoup</span></b></span><span class="pun"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">.</span></span><span class="pln"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">connect</span></span><span class="pun"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">(</span></span><span class="str"><span lang="EN-US" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: 'Source Sans Pro',sans-serif;">"http://siteweb.com"</span></span><span class="pun"><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">)</span></span><span lang="EN-US" style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"><br /><span class="pln"> </span><span class="pun">.</span><span class="pln">data</span><span class="pun">(</span><span class="str">"query"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Java"</span><span class="pun">)</span><br /><spanクラス="pln"> </span><span class="pun">.</span><span class="pln">userAgent</span><span class="pun">(</span><span class="str">"Mozilla"</span><span class="pun">)</span><br /><span class="pln"> </span><span class="pun">.</span><span class="pln">cookie</span><span class="pun">(</span><span class="str">"auth"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"token"</span><span class="pun">)</span><br /><spanクラス="pln"> </span><span class="pun">.</span><span class="pln">timeout</span><span class="pun">(</span><span class="lit">3000</span><span class="pun">)</span><br /><span class="pln"> </span><span class="pun">.</span><span class="pln">post</span><span class="pun">();</span></span></pre><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;">このメソッドは、http プロトコルと https プロトコルの URL のみをサポートします。ファイルをアップロードする必要がある場合は、<span style="color: #990000;" メソッドを使用するのが最善です。>parse(File in, String charsetName)</span>.<o:p></o:p></span><br /><span style="font-family: 'Source Sans Pro',sans-serif;"> </span></div><h3><b><span style="font-family: 'Source Sans Pro',sans-serif;">5-ファイルからドキュメントを読み込みます</span></b></h3><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;">HTMLを含むファイルがあり、それをロードし、分析し、データを抽出して操作します。メソッド <i>Jsoup.parse(File in, String encoding, String Uri).</i><o:p></o:p></span></div><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;"><br /></span></div><pre style="background: #E7EBF2;行の高さ:13.85pt;"><span class="typ"><b><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">File</span></b></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> input </span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">=</span></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> </span></span><span class="kwd"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">new</span></span><span class="pln"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"> </span></span><span class="typ"><b><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">File</span></b></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">(</span></span><span class="str"><span style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; font-family: 'Source Sans Pro',sans-serif;">"/temp/siteinput.html"</span></span><span class="pun"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;">);</span></span></pre><pre style="background: #E7EBF2;行の高さ:13.85pt;"><span style="background-attachment: initial;background-clip: initial;background-image: initial;background-origin: initial;背景位置:初期;background-repeat:initial;background-size: 初期値;font-family: 'ソースSans Pro',sans-serif;"><span class="typ"><b>Document</b></span><span class="pln"> doc </span><span class="pun">=</span><span class="pln"> </span><span class="typ"><b>Jsoup</b></span><span class="pun">.</span><span class="pln">parse</span><spanclass="pun">(<span style="line-height: 13.85pt;">siteinput</span></span></span><span class="pun" style="line-height: 13.85pt;">,</span><span class="pln" style="line-height:13.85pt;"> </span><span class="str" style="line-height: 13.85pt;">"UTF-8"</span><span class="pun" style="line-height: 13.85pt;">,</span><span class="pln" style="line-height:13.85pt;"> </span><span class="str" style="line-height: 13.85pt;">"http://siteweb.com/"</span><span class="pun" style="line-height: 13.85pt;">);</span></pre><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;">Uri パラメーターは、ドキュメント内の < 要素の前の相対 URL を解決するために使用されますhrefです>見つかります。空の String.</span></div><div class="MsoNormal"><span style="font-family: 'Source Sans Pro',sans-serif;" を渡すことができます。>別の同様のメソッドがあります parse(File in, String encoding)。parse() メソッドは、ファイル パスを URI として使用します。この方法は、ローカルサーバーで作業する場合に効果的です.</span></div> </article> <!-- Système de commentaires - Option 1 : Authentification requise --> <!-- Section des commentaires --> <section class="comments-section"> <div class="comments-header"> <h3 class="comments-count">Commentaires (<span id="commentCount">0</span>)</h3> </div> <!-- Messages d'alerte --> <div id="alertMessage" class="alert"></div> <!-- Formulaire pour les utilisateurs connectés --> <div id="commentForm" class="comment-form hidden"> <h4>Laisser un commentaire</h4> <textarea id="commentContent" class="comment-textarea" placeholder="Écrivez votre commentaire ici..." maxlength="1000" ></textarea> <div style="display: flex; justify-content: space-between; align-items: center;"> <span style="font-size: 0.85rem; color: #64748b;"> <span id="charCount">0</span>/1000 caractères </span> <button class="btn btn-primary" onclick="submitComment()"> Publier le commentaire </button> </div> </div> <!-- Message pour les utilisateurs non connectés --> <div id="authRequired" class="comment-auth-required"> <h4>Connectez-vous pour commenter</h4> <p>Rejoignez la discussion et partagez vos connaissances avec la communauté</p> <div class="auth-buttons"> <a href="/login" class="btn btn-secondary">Se connecter</a> <a href="/register" class="btn btn-primary">Créer un compte</a> </div> </div> <!-- Liste des commentaires --> <div id="commentsList" class="comments-list"> <div class="loading"> <div class="spinner"></div> <p>Chargement des commentaires...</p> </div> </div> <!-- Pagination --> <div id="pagination" class="pagination"></div> </section> </main> <!-- Right Sidebar --> <aside class="aside"> <!-- Author Box --> <div class="aside-section"> <h3>À propos de l'auteur</h3> <div class="author-box"> <div class="author-avatar">CJ</div> <div class="author-info"> <h4>CodeurJava</h4> <p>Passionné de Java et de développement</p> </div> </div> </div> <!-- Related Articles --> <div class="aside-section"> <h3>Articles similaires</h3> <a href="https://www.codeurjava.com/ja/php/html、css、php、mysqlを使用したログインフォーム.html" class="related-article">HTML/CSS、PHP、MySQLによるログインフォーム</a> <a href="https://www.codeurjava.com/ja//asciiテーブルとヘキサからasciiへのコンバーター.html" class="related-article">テキスト/ASCIIコンバータ</a> <a href="https://www.codeurjava.com/ja/python/for-in-を使用して-python-でリストを参照する.html" class="related-article">Pythonでリストを閲覧する方法</a> <a href="https://www.codeurjava.com/ja/java/javaでハッシュマップを閲覧する方法.html" class="related-article">JavaでHashMapを閲覧する方法</a> </div> <!-- Tags --> <div class="aside-section"> <h3>Tags</h3> <div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem;"> </div> </div> <!-- Newsletter --> <div class="aside-section aside-section-subscribe" style="background: var(--gradient); color: white;"> <h3>Newsletter</h3> <p style="margin-bottom: 1rem; font-size: 0.9rem;">Recevez nos derniers articles directement dans votre boîte mail</p> <input type="email" id="newsletter-email" placeholder="Votre email" style="width: 100%; padding: 0.8rem; border-radius: 10px; border: none; margin-bottom: 1rem;"> <button id="newsletter-submit" type="submit" style="width: 100%; background: white; color: var(--primary-color); border: none; padding: 0.8rem; border-radius: 10px; font-weight: 600; cursor: pointer;">S'inscrire</button> </div> </aside> </div> <!-- Footer --> <footer class="article-footer"> <p>© 2025 CodeurJava. Tous droits réservés.</p> </footer> <script src="/assets/js/newsletter.js"></script> <script> // Configuration const API_BASE = '/api'; const ARTICLE_ID = 1; // À remplacer par l'ID réel de l'article let currentPage = 1; let totalPages = 1; let isAuthenticated = false; let currentUser = null; let csrfToken = ''; // Generate Table of Contents dynamically function generateTOC() { const tocList = document.getElementById('toc-list'); const headings = document.querySelectorAll('.article-content h2'); headings.forEach((heading, index) => { // Ensure heading has an ID if (!heading.id) { heading.id = 'section-' + index; } // Create TOC item const li = document.createElement('li'); const a = document.createElement('a'); a.href = '#' + heading.id; a.textContent = heading.textContent; if (index === 0) a.classList.add('active'); li.appendChild(a); tocList.appendChild(li); }); } // Call on page load document.addEventListener('DOMContentLoaded', generateTOC); // Progress Bar window.addEventListener('scroll', () => { const winScroll = document.body.scrollTop || document.documentElement.scrollTop; const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; const scrolled = (winScroll / height) * 100; document.getElementById('progressBar').style.width = scrolled + '%'; }); // Table of Contents Active State window.addEventListener('scroll', () => { const sections = document.querySelectorAll('.article-content h2[id]'); const tocLinks = document.querySelectorAll('.toc-list a'); let current = ''; sections.forEach(section => { const sectionTop = section.offsetTop; if (scrollY >= (sectionTop - 200)) { current = section.getAttribute('id'); } }); tocLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href').slice(1) === current) { link.classList.add('active'); } }); }); // Smooth scrolling for TOC links document.addEventListener('click', (e) => { if (e.target.matches('.toc-list a')) { e.preventDefault(); const targetId = e.target.getAttribute('href').slice(1); const targetElement = document.getElementById(targetId); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Update URL without jumping history.pushState(null, null, '#' + targetId); } } }); // Header Scroll Effect window.addEventListener('scroll', () => { const header = document.querySelector('header'); if (window.scrollY > 100) { header.style.background = 'rgba(255, 255, 255, 0.98)'; header.style.boxShadow = '0 4px 30px rgba(0, 0, 0, 0.08)'; } else { header.style.background = 'rgba(255, 255, 255, 0.95)'; header.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.05)'; } }); // Fonction pour initialiser prettyprint function initializePrettyPrint() { // Wrapper automatique des balises pre.prettyprint document.querySelectorAll('pre.prettyprint').forEach(pre => { if (!pre.parentElement.classList.contains('code-wrapper')) { const wrapper = document.createElement('div'); wrapper.className = 'code-wrapper'; const header = document.createElement('div'); header.className = 'code-header'; // Détecter le langage const langClass = Array.from(pre.classList).find(c => c.startsWith('lang-')); const lang = langClass ? langClass.replace('lang-', '').toUpperCase() : 'CODE'; header.innerHTML = ` <span class="code-language" data-lang="${lang.toLowerCase()}">${lang}</span> <button class="copy-button" onclick="copyCode(this)">Copier</button> `; pre.parentNode.insertBefore(wrapper, pre); wrapper.appendChild(header); wrapper.appendChild(pre); } }); // Si vous utilisez Google Prettify, décommentez cette ligne // PR.prettyPrint(); } // Fonction de copie du code function copyCode(button) { const codeBlock = button.closest('.code-wrapper').querySelector('pre'); const textArea = document.createElement('textarea'); textArea.value = codeBlock.textContent; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); button.textContent = 'Copié !'; button.classList.add('copied'); setTimeout(() => { button.textContent = 'Copier'; button.classList.remove('copied'); }, 2000); } // Coloration syntaxique simple (sans Google Prettify) function applySyntaxHighlighting() { document.querySelectorAll('pre.prettyprint').forEach(pre => { let code = pre.innerHTML; // Java keywords const javaKeywords = [ 'public', 'private', 'protected', 'class', 'interface', 'extends', 'implements', 'static', 'final', 'void', 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'new', 'this', 'super', 'import', 'package', 'try', 'catch', 'finally', 'throw', 'throws', 'abstract', 'synchronized', 'volatile' ]; // Types const types = [ 'String', 'int', 'boolean', 'double', 'float', 'long', 'char', 'byte', 'short', 'Integer', 'Boolean', 'Double', 'Float', 'Long', 'Character', 'Byte', 'Short', 'List', 'ArrayList', 'Map', 'HashMap', 'Set', 'HashSet' ]; // Literals const literals = ['true', 'false', 'null']; // Apply highlighting // Comments code = code.replace(/(\/\/.*$)/gm, '<span class="com">$1</span>'); code = code.replace(/(\/\*[\s\S]*?\*\/)/g, '<span class="com">$1</span>'); // Strings code = code.replace(/(".*?")/g, '<span class="str">$1</span>'); code = code.replace(/('.')/g, '<span class="str">$1</span>'); // Numbers code = code.replace(/\b(\d+)\b/g, '<span class="lit">$1</span>'); // Keywords javaKeywords.forEach(keyword => { const regex = new RegExp('\\b(' + keyword + ')\\b', 'g'); code = code.replace(regex, '<span class="kwd">$1</span>'); }); // Types types.forEach(type => { const regex = new RegExp('\\b(' + type + ')\\b', 'g'); code = code.replace(regex, '<span class="typ">$1</span>'); }); // Literals literals.forEach(literal => { const regex = new RegExp('\\b(' + literal + ')\\b', 'g'); code = code.replace(regex, '<span class="lit">$1</span>'); }); pre.innerHTML = code; }); } // ======================================== // SYSTÈME DE COMMENTAIRES // ======================================== // Initialisation document.addEventListener('DOMContentLoaded', function() { checkAuthentication(); loadComments(); // Compteur de caractères document.getElementById('commentContent').addEventListener('input', function() { document.getElementById('charCount').textContent = this.value.length; }); }); // Vérifier l'authentification async function checkAuthentication() { try { const response = await fetch(`${API_BASE}/auth.php?action=check`); const data = await response.json(); isAuthenticated = data.loggedIn; currentUser = data.user; csrfToken = data.csrf_token || ''; // Afficher/masquer les éléments selon l'état d'authentification document.getElementById('commentForm').classList.toggle('hidden', !isAuthenticated); document.getElementById('authRequired').classList.toggle('hidden', isAuthenticated); } catch (error) { console.error('Erreur lors de la vérification d\'authentification:', error); } } // Charger les commentaires async function loadComments(page = 1) { try { const response = await fetch( `${API_BASE}/comments.php?action=get&article_id=${ARTICLE_ID}&page=${page}` ); const data = await response.json(); if (data.success) { renderComments(data.comments); updatePagination(data.pagination); document.getElementById('commentCount').textContent = data.pagination.total; } else { showAlert('Erreur lors du chargement des commentaires', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Afficher les commentaires function renderComments(comments) { const container = document.getElementById('commentsList'); if (comments.length === 0) { container.innerHTML = ` <div style="text-align: center; padding: 3rem; color: #64748b;"> Aucun commentaire pour le moment. Soyez le premier à commenter ! </div> `; return; } container.innerHTML = comments.map(comment => renderComment(comment)).join(''); } // Rendre un commentaire function renderComment(comment, isReply = false) { const initials = comment.username.substring(0, 2).toUpperCase(); const statusBadge = comment.status === 'pending' ? '<span class="comment-status status-pending">En attente</span>' : ''; return ` <div class="comment${isReply ? '-reply' : ''}" data-id="${comment.id}"> <div class="comment-header"> <div class="comment-author"> <div class="comment-avatar">${initials}</div> <div class="comment-author-info"> <span class="comment-author-name"> ${escapeHtml(comment.username)} ${comment.role === 'admin' || comment.role === 'moderator' ? `<span style="color: #10b981; font-size: 0.8rem;">✓ ${comment.role}</span>` : ''} </span> <span class="comment-date">${comment.formatted_date}</span> </div> </div> ${statusBadge} </div> <div class="comment-content"> ${escapeHtml(comment.content)} ${comment.is_edited ? '<span style="font-size: 0.8rem; color: #64748b;"> (modifié)</span>' : ''} </div> ${!isReply ? ` <div class="comment-actions"> <button class="comment-action ${comment.user_liked ? 'liked' : ''}" onclick="likeComment(${comment.id})"> <span>${comment.user_liked ? '❤️' : '🤍'}</span> <span>${comment.likes}</span> </button> <button class="comment-action" onclick="toggleReplyForm(${comment.id})"> 💬 Répondre </button> <button class="comment-action" onclick="reportComment(${comment.id})"> 🚩 Signaler </button> ${comment.can_edit ? ` <button class="comment-action" onclick="editComment(${comment.id})"> ✏️ Modifier </button> ` : ''} ${comment.can_delete ? ` <button class="comment-action" onclick="deleteComment(${comment.id})"> 🗑️ Supprimer </button> ` : ''} </div> <div id="replyForm${comment.id}" class="reply-form"> <textarea id="replyContent${comment.id}" class="comment-textarea" placeholder="Votre réponse..." style="min-height: 80px;" ></textarea> <div style="display: flex; gap: 1rem; justify-content: flex-end;"> <button class="btn btn-secondary" onclick="toggleReplyForm(${comment.id})"> Annuler </button> <button class="btn btn-primary" onclick="submitReply(${comment.id})"> Répondre </button> </div> </div> ` : ''} ${comment.replies && comment.replies.length > 0 ? ` <div class="comment-replies"> ${comment.replies.map(reply => renderComment(reply, true)).join('')} </div> ` : ''} ${comment.has_more_replies ? ` <button class="comment-action" style="margin-left: 3rem; margin-top: 0.5rem;" onclick="loadMoreReplies(${comment.id})"> Voir plus de réponses (${comment.reply_count - 3}) </button> ` : ''} </div> `; } // Soumettre un commentaire async function submitComment() { const content = document.getElementById('commentContent').value.trim(); if (content.length < 3) { showAlert('Le commentaire doit contenir au moins 3 caractères', 'error'); return; } try { const formData = new FormData(); formData.append('action', 'add'); formData.append('article_id', ARTICLE_ID); formData.append('content', content); formData.append('csrf_token', csrfToken); const response = await fetch(`${API_BASE}/comments.php`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { document.getElementById('commentContent').value = ''; document.getElementById('charCount').textContent = '0'; showAlert(data.message || 'Commentaire publié', 'success'); loadComments(1); // Recharger la première page } else { showAlert(data.error || 'Erreur lors de la publication', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Soumettre une réponse async function submitReply(parentId) { const content = document.getElementById(`replyContent${parentId}`).value.trim(); if (content.length < 3) { showAlert('La réponse doit contenir au moins 3 caractères', 'error'); return; } try { const formData = new FormData(); formData.append('action', 'add'); formData.append('article_id', ARTICLE_ID); formData.append('content', content); formData.append('parent_id', parentId); formData.append('csrf_token', csrfToken); const response = await fetch(`${API_BASE}/comments.php`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { toggleReplyForm(parentId); showAlert(data.message || 'Réponse publiée', 'success'); loadComments(currentPage); } else { showAlert(data.error || 'Erreur lors de la publication', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Liker un commentaire async function likeComment(commentId) { if (!isAuthenticated) { showAlert('Vous devez être connecté pour liker', 'info'); return; } try { const formData = new FormData(); formData.append('action', 'like'); formData.append('comment_id', commentId); formData.append('csrf_token', csrfToken); const response = await fetch(`${API_BASE}/comments.php`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { loadComments(currentPage); } else { showAlert(data.error || 'Erreur', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Signaler un commentaire async function reportComment(commentId) { if (!isAuthenticated) { showAlert('Vous devez être connecté pour signaler', 'info'); return; } const reason = prompt('Raison du signalement:\n1. spam\n2. inappropriate\n3. harassment\n4. other'); if (!reason) return; const details = prompt('Détails supplémentaires (optionnel):'); try { const formData = new FormData(); formData.append('action', 'report'); formData.append('comment_id', commentId); formData.append('reason', reason); formData.append('details', details || ''); formData.append('csrf_token', csrfToken); const response = await fetch(`${API_BASE}/comments.php`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { showAlert(data.message || 'Commentaire signalé', 'success'); } else { showAlert(data.error || 'Erreur', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Supprimer un commentaire async function deleteComment(commentId) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce commentaire ?')) { return; } try { const formData = new FormData(); formData.append('action', 'delete'); formData.append('comment_id', commentId); formData.append('csrf_token', csrfToken); const response = await fetch(`${API_BASE}/comments.php`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { showAlert('Commentaire supprimé', 'success'); loadComments(currentPage); } else { showAlert(data.error || 'Erreur', 'error'); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur de connexion au serveur', 'error'); } } // Afficher/masquer le formulaire de réponse function toggleReplyForm(commentId) { const form = document.getElementById(`replyForm${commentId}`); form.classList.toggle('active'); if (form.classList.contains('active')) { document.getElementById(`replyContent${commentId}`).focus(); } } // Charger plus de réponses async function loadMoreReplies(commentId) { try { const response = await fetch( `${API_BASE}/comments.php?action=replies&comment_id=${commentId}` ); const data = await response.json(); if (data.success) { // Recharger les commentaires pour afficher toutes les réponses loadComments(currentPage); } } catch (error) { console.error('Erreur:', error); showAlert('Erreur lors du chargement des réponses', 'error'); } } // Pagination function updatePagination(pagination) { currentPage = pagination.current_page; totalPages = pagination.total_pages; const container = document.getElementById('pagination'); if (totalPages <= 1) { container.innerHTML = ''; return; } let html = ''; // Bouton précédent html += ` <button class="pagination-btn" onclick="loadComments(${currentPage - 1})" ${currentPage === 1 ? 'disabled' : ''}> ← Précédent </button> `; // Numéros de page for (let i = 1; i <= Math.min(totalPages, 5); i++) { html += ` <button class="pagination-btn ${i === currentPage ? 'active' : ''}" onclick="loadComments(${i})"> ${i} </button> `; } if (totalPages > 5) { html += `<span>...</span>`; html += ` <button class="pagination-btn ${totalPages === currentPage ? 'active' : ''}" onclick="loadComments(${totalPages})"> ${totalPages} </button> `; } // Bouton suivant html += ` <button class="pagination-btn" onclick="loadComments(${currentPage + 1})" ${currentPage === totalPages ? 'disabled' : ''}> Suivant → </button> `; container.innerHTML = html; } // Afficher une alerte function showAlert(message, type = 'info') { const alert = document.getElementById('alertMessage'); alert.className = `alert alert-${type} show`; alert.textContent = message; setTimeout(() => { alert.classList.remove('show'); }, 5000); } // Échapper le HTML function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } </script>