HtmlUnitは、HTMLページのモデリングが可能なヘッドレスブラウザです。ページをプログラムでモデリングした後、フォームへの入力や送信、ページ間の移動などのタスクを実行してページとやり取りできます。これを使ってウェブスクレイピングを行い、データを抽出して後で操作することも、自動テストを作成してプログラムが期待通りにウェブページを作成しているか確認することもできます。
HtmlUnitを使ったWebスクレイピング
HtmlUnitとGradleを使ってウェブスクレイピングを実装するために、IntelliJ IDEA IDEが使用されますが、好みのIDEやコードエディタを使ってもかまいません。
IntelliJはGradleとの完全な機能統合をサポートしており、JetBrainsのサイトからダウンロードできます。Gradleは、アプリケーションのビルドやパッケージの作成をサポートするビルド自動化ツールです。また、依存関係を追加して、シームレスに管理できます。IntelliJ IDEAの最新バージョンでは、GradleおよびGradle拡張機能がデフォルトでインストールされ、有効になっています。
このチュートリアルのすべてのコードは、このGitHubリポジトリで見つけることができます。
Gradleプロジェクトを作成する
IntelliJ IDEでGradleプロジェクトを新規作成するには、メニューオプションから「ファイル」 > 「新規」 > 「プロジェクト」を選択して、新規プロジェクトウィザードを表示します。プロジェクト名を入力し、目的の場所を選択します。
HtmlUnitを使用してJava言語でウェブスクレイピングアプリケーションを作成するため、Javaを選択する必要があります。さらに、Gradleビルドシステムを選択します。次に「作成」をクリックします。これで、デフォルトの構造と必要なファイルをすべて備えたGradleプロジェクトが作成されます。例えば、build.gradle
ファイルには、このプロジェクトをビルドするのに必要なすべての依存関係が含まれています。
HtmlUnitをインストールする
HtmlUnit
を依存関係としてインストールするには、「表示」 > 「ツールウィンドウ」 > 「依存関係」を選択して、「依存関係」ウィンドウを開きます。
次に、「htmlunit」を検索して、「追加」を選択します。
build.gradle
ファイルのdependencies
セクションにHtmlUnitがインストールされたことが確認できるはずです。
HtmlUnitをインストールしたので、いよいよ静的および動的なウェブページからデータをスクレイピングしましょう。
静的なページをスクレイピングする
このセクションでは、静的なウェブページであるHtmlUnit Wikiをスクレイピングする方法について学びます。このウェブページには、タイトル、目次、小見出しのリスト、各小見出しのコンテンツなどの要素が含まれます。
HTMLウェブページの各要素には属性があります。例えば、ID
はHTMLドキュメント全体の中で要素を一意に識別する属性で、Name
はその要素を識別する属性です。Name
属性は一意ではなく、HTMLドキュメント内の複数の要素が同じ名前を持つことがあります。ウェブページ内の要素は、これらの属性のいずれかを使用して識別できます。
また、そのXPathを利用して要素を識別することもできます。XPathは、ウェブページのHTML内の要素を識別し、ナビゲートするためにパスのような構文を使用します。
以下の例では、これらの方法の両方を使用してHTMLページ内の要素を識別します。
ウェブページをスクレイピングするためには、HtmlUnit WebClientを作成する必要があります。WebClientは、Javaアプリケーション内のブラウザを表します。WebClientの初期化は、ブラウザを起動してウェブページを表示するのに似ています。
WebClientを初期化するには、次のコードを使用します。
WebClient webClient = new WebClient(BrowserVersion.CHROME);
このコードは、Chromeブラウザを初期化します。その他のブラウザにも対応しています。
webClientオブジェクトで利用可能なgetPage()
メソッドを使用してウェブページを取得できます。ウェブページを入手したら、さまざまな方法でウェブページからデータをスクレイピングできます。
ページタイトルを取得するには、次のコードに示すようにgetTitleText()
メソッドを使用します。
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURl);
System.out.println(page.getTitleText());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
すると、ページのタイトルが次のように出力されます。
HtmlUnit - Wikipedia
さらに一歩踏み込んで、ウェブページで利用可能なすべてのH2要素を取得しましょう。この例では、ページ内の2つのセクションでH2が利用可能です。
- 左側のサイドバーには、コンテンツが表示されます。ご覧のように、Contentsセクションの見出しはH2要素です。
- ページのメインボディ:すべての小見出しはH2要素です。
コンテンツボディ内のH2要素のXPathを使用して、すべてのH2を取得できます。XPathを見つけるには、任意のH2要素を右クリックし、「検査」を選択します。次に、ハイライトされた要素を右クリックし、「コピー」 > 「フルXPathをコピー」を選択します。
これにより、XPathがクリップボードにコピーされます。例えば、コンテンツボディ内のH2のXPath要素は、/html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2です
。
XPathを使用してすべてのH2要素を取得するには、getByXpath()
メソッドを使用します。
String xPath = "/html/body/div[1]/div/div[3]/main/div[2]/div[3]/div[1]/h2";
String webPageURL = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
HtmlPage page = webClient.getPage(webPageURL);
//Get all the headings using its XPath+
List<HtmlHeading2> h2 = (List<HtmlHeading2>)(Object) page.getByXPath(xPath);
//print the first heading text content
System.out.println((h2.get(0)).getTextContent());
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
最初のH2要素のテキストコンテンツは、次のように出力されます。
Benefits[edit]
同様に、getElementById()
メソッドを使用し、IDを介して要素を取得できます。また、getElementByName()
メソッドを使用して、その名前から要素を取得できます。
次のセクションでは、これらのメソッドを使用して、動的なウェブページをスクレイピングします。
HtmlUnitを使用して動的なウェブページをスクレイピングする
このセクションでは、ログインフォームに入力して送信するにより、HtmlUnitのフォーム入力とボタンクリックの機能について学びます。また、ヘッドレスブラウザを使ってウェブページをナビゲートする方法についても学びます。
動的なウェブスクレイピングを実証するために、Hacker Newsのウェブサイトを使用しましょう。ログインページは次のようになっています。
次のコードは、前のページのHTMLフォームコードです。このコードは、「ログインラベル」を右クリックしてから、「検査」をクリックして取得できます。
<form action="login" method="post">
<input type="hidden" name="goto" value="news">
<table border="0">
<tbody>
<tr><td>username:</td><td><input type="text" name="acct" size="20" autocorrect="off" spellcheck="false" autocapitalize="off" autofocus="true"></td></tr>
<tr><td>password:</td><td><input type="password" name="pw" size="20"></td></tr></tbody></table><br>
<input type="submit" value="login"></form>
HtmlUnitを使ってフォームに入力するには、webClient
オブジェクトを使ってウェブページを取得します。このページには、ログインとアカウントの作成という2つのフォームがあります。ログインフォームは、getForms().get(0)
メソッドで取得できます。また、フォームに一意の名前がある場合は、getFormByName()
メソッドを使用できます。
次に、getInputByName()
メソッドとname
属性を使用して、フォーム入力(つまりユーザー名とパスワードフィールド)を取得する必要があります。
ユーザー名とパスワードの値を、setValueAttribute()
メソッドを使用して入力フィールドに設定し、送信ボタンをgetInputByValue()
メソッドを使用して取得します。click()
メソッドを使用してボタンをクリックすることもできます。
ボタンがクリックされ、ログインに成功すると、送信ボタンのターゲットページがHTMLPage
オブジェクトとして返され、以降の操作に使用できるようになります。
以下のコードは、フォームの取得して、記入し、送信する方法を示しています。
HtmlPage page = null;
String webPageURl = "https://en.wikipedia.org/wiki/HtmlUnit";
try {
// Get the first page
HtmlPage signUpPage = webClient.getPage(webPageURL);
// Get the form using its index. 0 returns the first form.
HtmlForm form = signUpPage.getForms().get(0);
//Get the Username and Password field using its name
HtmlTextInput userField = form.getInputByName("acct");
HtmlInput pwField = form.getInputByName("pw");
//Set the User name and Password in the appropriate fields
userField.setValueAttribute("draftdemoacct");
pwField.setValueAttribute("test@12345");
//Get the submit button using its Value
HtmlSubmitInput submitButton = form.getInputByValue("login");
//Click the submit button, and it'll return the target page of the submit button
page = submitButton.click();
} catch (FailingHttpStatusCodeException | IOException e) {
e.printStackTrace();
}
フォームが送信され、ログインに成功すると、ユーザーのホームページが表示され、右隅にユーザー名が表示されます。
ユーザー名の要素には、ID「me」があります。以下のコードに示すように、getElementById()
メソッドを使用してユーザー名を取得し、ID「me」を渡すことができます。
System.out.println(page.getElementById("me").getTextContent());
ユーザー名がウェブページからスクレイピングされ、出力として表示されます。
draftdemoacct
次に、ページの最後にある「その他」ハイパーリンクボタンをクリックして、Hacker Newsサイトの2ページ目に移動する必要があります。
「その他」ボタンオブジェクトを取得するには、「検査」オプションを使用して「その他」ボタンのXPathを取得し、インデックス0
を使用して最初のlink
オブジェクトを取得します
click()
メソッドを使用して「その他」リンクをクリックします。link
がクリックされると、リンク先のページがHtmlPage
オブジェクトとして返されます。
HtmlPage nextPage = null;
try {
List<HtmlAnchor> links = (List<HtmlAnchor>)(Object)page.getByXPath("html/body/center/table/tbody/tr[3]/td/table/tbody/tr[92]/td[2]/a");
HtmlAnchor anchor = links.get(0);
nextPage = anchor.click();
} catch (IOException e) {
throw new RuntimeException(e);
}
この時点で、HtmlPage
オブジェクトには2ページ目が含まれているはずです。
HtmlPage
のURLを出力して、2ページ目が正常に読み込まれたかどうかを確認できます。
System.out.println(nextPage.getUrl().toString());
以下は、2ページ目のURLです。
https://news.ycombinator.com/news?p=2
Hacker Newsサイトの各ページには、30件のエントリがあります。これが、2ページ目のエントリがシリアル番号31から始まる理由です。
2ページ目の最初のエントリのIDを取得し、それが31に等しいかどうかを確認しましょう。前と同様に、
「検査」オプションを使用して、最初のエントリのXPathを取得します。次に、リストから最初のエントリを取得し、そのテキストコンテンツを表示します。
String firstItemId = null;
List<Object> entries = nextPage.getByXPath("/html/body/center/table/tbody/tr[3]/td/table/tbody/tr[1]/td[1]/span");
HtmlSpan span = (HtmlSpan) (entries.get(0));
firstItemId = span.getTextContent();
System.out.println(firstItemId);
最初のエントリのIDが表示されるようになりました。
31.
このコードは、HtmlUnitを使用して、フォームへの入力、ボタンのクリック、ウェブページのナビゲーションを行う方法を示しています。
まとめ
この記事では、HtmlUnitを使って静的・動的なウェブサイトをスクレイピングする方法を学びました。また、ウェブページをスクレイピングして構造化データに変換することで、HtmlUnitの高度な機能の一端を知ることができました。
IntelliJ IDEAのようなIDEを使用してこれを行う場合、要素属性を手動で検査して見つけ、要素属性を使ってスクレイピング関数をゼロから記述する必要があります。それに比べて、Bright DataのウェブスクレイパーIDEは、堅牢なブロック解除プロキシインフラストラクチャ、便利なスクレイピング機能、および一般的なウェブサイト用のコードテンプレートを提供します。IPブロックやレート制限の問題なしにウェブページをスクレイピングするには、効率的なプロキシインフラストラクチャが必要です。また、プロキシは、地理的位置の異なるユーザーをエミュレートするのに役立ちます。
Talk to one of Bright Data’s experts and find the right solution for your business.