Java HtmlUnit でコンテンツをフィルタする
HtmlUnit を簡易ブラウザエミュレータとして使う。
取得した Web ページを一部分書き換えたい。
Proxomitron などのプロキシサーバーを介して、フィルタリングするのもよい。
DOM の範囲で変更できる箇所なら、Document.Write でもよい。
JavaScript で URL を生成して、アクセスするものもある。
URL をアクセスするときにフィルタリングしたい。
import java.net.URL; import java.io.IOException; import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.util.FalsifyingWebConnection; public class test { public static void main(String[] args) throws Exception { WebClient webClient = new WebClient(BrowserVersion.FIREFOX_3); new FalsifyingWebConnection(webClient) { @Override public WebResponse getResponse(WebRequest request) throws IOException { WebResponse response; if ( request.getUrl().toString().contains("hoge.js") ) { // 空のコンテンツを返す response = createWebResponse(request, "", "application/javascript", 200, "OK"); } else { response = super.getResponse(request); } return response; } }; HtmlPage page = (HtmlPage)webClient.getPage(new URL("http://www.hoge.com")); System.out.print(page.asText()); } }
取得したコンテンツを HtmlUnit 内で書き換える。
new FalsifyingWebConnection(webClient) { @Override public WebResponse getResponse(WebRequest request) throws IOException { WebResponse response = super.getResponse(request); String content = response.getContentAsString(); content = content.replaceAll("var i=12345", "var i=0"); response = replaceContent(response, content); return response; } };
Perl WWW::HtmlUnit を Windows で使用する
HtmlUnit とは Java で作成されている HTML のテスト用のフレームワーク。JavaScript のテストにも対応している。
JavaScript を使用している Web ページを外部から取得してみたくなった。
最初に ActivePerl 5.12.1.1201 をインストールしていたが、
Windows の環境によって、dmake ではなく nmake が使用され、エラーとなる。
PPM でインストールできる GCC のバージョンも古いため、
Strawberry Perl 5.12.1.0 を使用する。(2010/09/04)
Windows 9x/2000/XP/Vista/7 の Win32 をターゲットとする。
Strawberry Perl 5.12.1.0 をインストール
C:\strawberry
コマンドプロンプトから
> set
PATH に C:\strawberry\perl\bin;C:\strawberry\perl\site\bin;C:\strawberry\c\bin;%PATH%
と、Perl.exe にパスが通っていることを確認する。
Java Development Kit をインストール
jdk-6u21-windows-i586.exe
C:\Program Files\Java\jdk1.6.0_21 を DOS 用の空白文字の無いパス 8 + 3 文字を調べておく。
C:\PROGRA~1\JAVA\JDK16~1.0_2
最初から短いパス名でインストールしたり、
NTFS のジャンクション機能を使用して、短いパスを作っておくのも良い。
コントロールパネルのシステムで環境変数を設定する
(その都度 コマンドプロンプトから set コマンドを使ってもよい)
set JAVA_HOME=C:\PROGRA~1\JAVA\JDK16~1.0_2
set PERL_INLINE_JAVA_JNI=1
Inline::Java で JNI を使わない場合は必要ない。
set PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin\client;%PATH%
システムの環境設定を変更した場合は、ここで再起動かログオフをして環境変数を適用させる。
コマンドプロンプトから
> set
で、設定した環境変数が表示出来ている場合はログインしなおす必要はない。
コマンドプロンプトから次のモジュールをインストールする。
Test-Pod-1.44
Inline--0.46
Inline-Java-0.52
WWW-HtmlUnit-0.09
> perl -V:make
で dmake が設定されていることを確認する。
> perl -MCPAN -e shell
cpan> install Test::Pod
cpan> install Inline
ここで Inline::Java をインストールするけれど、JNI を使用するオプションでは、リンクエラーとなる。
JNI を使用しないなら
cpan> install Inline::Java
にて、JNI のオプションに n と入力すればよい。
JNI を使用するなら
cpan> get Inline::Java
にて C:\strawberry\cpan\build\Inline-Java-0.52-??? が作成される。
Inline-Java-0.52-???\Java\Makefile.PL をテキストエディタで開き
196 行目の OTHERLDFLAGS の下に
. ' C:\\PROGRA~1\\JAVA\\JDK16~1.0_2\\lib\\jvm.lib '
を追加する。
dynamic_lib => { OTHERLDFLAGS => Inline::Java::Portable::portable('OTHERLDFLAGS') . ' C:\\PROGRA~1\\JAVA\\JDK16~1.0_2\\lib\\jvm.lib ' # 追加した行 },
cpan> install Inline::Java
PerlNatives extension や PerlInterpreter extension は有効にするとコンパイルエラーとなる。
このオプションを有効にする解決方法は調べていない。
オプションはデフォルトのまま Enter キーで入力していく。
WWW::HtmlUnit もそのままインストールすると make test で実行エラーとなる。
cpan> get WWW::HtmlUnit
C:\strawberry\cpan\build\WWW-HtmlUnit-0.09-???\lib\HtmlUnit.pm をテキストエディタで開く。
88 行目の return join ':', map { "$jar_path/$_" } qw( のコロンをセミコロンに変更する。 return join ';', map { "$jar_path/$_" } qw(
このエラーは、Unix 系では一つのディレクトリにドライブをマウントして使用するため、
パスにコロンは含まれず、区切り文字として利用できるが、
Windows ではドライブレターにコロンを使用しているため、パスの区切りとしてコロンは使用できない。
よって、HtmlUnit の jar ファイルを認識できなくなっていた。
他にも区切り文字としてコロンを使用している箇所もある。
以上で WWW::HtmlUnit を使用できる環境が整った。
SYNOPSIS にあるサンプルプログラムでも走らせて動作確認してみよう。
use WWW::HtmlUnit; my $webClient = WWW::HtmlUnit->new; my $page = $webClient->getPage("http://google.com/"); my $f = $page->getFormByName('f'); my $submit = $f->getInputByName("btnG"); my $query = $f->getInputByName("q"); $page = $query->type("HtmlUnit"); $page = $query->type("\n"); my $content = $page->asXml; print "Result:\n$content\n\n";
test.pl と保存して
> perl test.pl
取得した html が問題なく出力された。
HtmlUnit は Java のフレームワークだし、
JavaScript に対応するためなので Java で使う方がスマート。
今回は Perl の他のプログラムと連携させたかったのと、
JRuby + Celerity(HtmlUnit) では shiftjis をそのまま使用することが出来なかったため。
SEH、構造化例外処理のテスト
try ... catch() や __try ... __except() を使用できれば、そちらが簡単。
#include <stdio.h> #include <windows.h> LONG CALLBACK ExceptionHandler(EXCEPTION_POINTERS *ExceptionInfo) { printf("ExceptionHandler\n"); // c で次の命令へのアドレスを取得する方法が現在の所は不明。 ExceptionInfo->ContextRecord->Eip += 10; // 例外が発生した次の命令へ飛ばす return(EXCEPTION_CONTINUE_EXECUTION); } int main() { printf("null pointer exception\n"); SetUnhandledExceptionFilter(ExceptionHandler); *(int*)0 = 0x12345678; printf("end.\n\n"); return 0; }
インラインアセンブラで fs:[0] を使用する
#include <stdio.h> #include <windows.h> // __cdecl __stdcall のどちらでも動作したけれど、要確認 int __cdecl exception(EXCEPTION_RECORD *lpException, DWORD *lpFrame, CONTEXT *lpContext, DWORD* lpDispatch) { printf("ExceptionHandler\n"); context->Eip = context->Edx; return ExceptionContinueExecution; } int main() { printf("null pointer exception\n"); __asm { xor ebx,ebx mov eax, exception push eax push fs:[ebx] mov fs:[ebx], esp mov edx, offset nextstep // edx にジャンプ先アドレスを格納 xor ebx, ebx mov dword ptr [ebx], 12345678h nextstep: xor ebx, ebx mov eax, [esp] mov fs:[ebx], eax add esp, 8 } printf("end.\n\n"); return 0; }
TabMixPlus の javascript を改造する
タブを追加するときにアクティブタブの右に挿入させている
タブを削除した場合は左のタブへ移動する設定をしている
タブを閉じたときにアクティブタブの右側に未読のタブがあれば
一番右の未読のタブへ移動したくなった
未読タブが右側に無ければ、設定通りに左のタブへシフトさせる
TabMixPlus 0.3.7.4pre 090404
tabmixplus.jar->content\tabmixplus\tab\tab.js
TMP_PL_setUnreadTab()関数に追加
この関数はページの読み込み完了時に呼び出される
setUnreadTab: function TMP_PL_setUnreadTab(aTab) { if (aTab.parentNode.childNodes.length == 1) { var hidebutton = gBrowser.isBlankTab(aTab) || tabxPrefs.getBoolPref("keepLastTab"); TMP_setItem(aTab.parentNode, "hidebutton", hidebutton || null); } //追加部分 aTab.setAttribute("ma_pageLoad", "unread"); // 未読タブにフラグを付ける aTab.removeAttribute("tab-progress"); if (gPref.getBoolPref("extensions.tabmix.unreadTab") && aTab.hasAttribute("selected") && gPref.getBoolPref("extensions.tabmix.unreadTabreload") && !aTab.hasAttribute("dontremoveselected") && aTab.getAttribute("selected") == "false") aTab.removeAttribute("selected"); // see gBrowser.openLinkWithHistory in tablib.js if (aTab.hasAttribute("dontremoveselected")) aTab.removeAttribute("dontremoveselected") },
tabmixplus.jar->content\tabmixplus\tabmix.js
TMP_EL_handleEvent()関数の改造
この関数はタブのイベント処理をする
handleEvent: function TMP_EL_handleEvent(aEvent) { switch (aEvent.type) { case "SSTabRestoring": this.onSSTabRestoring(aEvent); break; case "TabOpen": this.onTabOpen(aEvent); break; case "TabClose": this.onTabClose(aEvent); break; case "TabSelect": // 追加部分 選択したタブからフラグを除去 if ( aEvent.target.hasAttribute("ma_pageLoad") ) aEvent.target.removeAttribute("ma_pageLoad"); this.onTabSelect(aEvent); break; case "DOMMouseScroll": this.onTabBarScroll(aEvent); break; case "load": this.onWindowOpen(aEvent); break; case "unload": this.onWindowClose(aEvent); break; } },
tabmixplus.jar->content\tabmixplus\minit\tablib.js
タブを閉じたときの挙動の処理
gBrowser.selectIndexAfterRemove = function (oldTab) { var currentIndex = this.mCurrentTab._tPos; if (this.mCurrentTab != oldTab) return currentIndex; var l = this.mTabs.length; if (l==1) return 0; var mode = this.mPrefs.getIntPref("extensions.tabmix.focusTab"); switch ( mode ) { case 0: // first tab return currentIndex == 0 ? 1 : 0; break; case 1: // left tab // 追加部分 var lastIndex = gBrowser.mTabContainer.childNodes.length - 1; for (var i=lastIndex; i>currentIndex; i--) { if ( gBrowser.mTabs[i].hasAttribute("ma_pageLoad") ) // 未読フラグの確認 return i; } // ここまで return currentIndex == 0 ? 1 : currentIndex-1; break; case 3: // last tab return currentIndex == l - 1 ? currentIndex - 1 : l - 1; break; case 6: // last opened var lastTab = this.getTabForLastPanel(); if (lastTab == oldTab && l > 1) { lastTab = document.getAnonymousElementByAttribute(this, "linkedpanel", this.mPanelContainer.childNodes[l-2].id); } return lastTab._tPos; case 4: // last selected var tempIndex = this.previousTabIndex(oldTab); // if we don't find last selected we fall back to default if (tempIndex > -1) return tempIndex; case 2: // opener / right (default ) case 5: // right tab default: if (mode != 5 && this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose") && "owner" in oldTab) { var owner = oldTab.owner; if (owner && owner.parentNode && owner != oldTab) // oldTab and owner still exist just return its position return owner._tPos; } } return currentIndex == l - 1 ? currentIndex - 1 : currentIndex + 1; }
TabMixPlus でタブを閉じたときに左のタブへを設定すれば右側にある未読のタブへ飛ぶ
追記 多段タブ設定にしていると段にタブがあふれたときに未読フラグがおかしくなった こりゃいかん
追記090412 Firefox 3.5 を導入する際にちょこっと改良
Perl HTML数値文字参照(HTMLエンティティとも呼ばれている模様)
あ
などの 主にWEB上でユニコード文字の文字コード化に使われているHTML数値文字参照の変換スクリプト
use Encode qw/ encode decode from_to /; use HTML::Entities; my $str = 'abcあDEF'; # HTML数値文字参照はセミコロンで終わっていなければならない HTML::Entities::decode_entities( $str ); # シフトJISに変換できない文字はHTML数値文字参照に $str = Encode::encode( 'shiftjis', $str, Encode::FB_HTMLCREF );
print $str > abcあDEF
use Encode qw/ encode decode from_to /; my $str = 'abcあDEF'; $str =~ s/\&\#(\d{1,5});?/encode('shiftjis',decode('utf16be',pack("n*",$1)),Encode::FB_HTMLCREF)/eg;
print $str > abcあDEF
PerlでSSLアクセス
ActivePerl v5.10.0
SSL の https:// にアクセスするために Crypt-SSLeay をインストール
cmdから
ppm install http://cpam.uwinnipeg.ca/PPMPackages/10xx/Crypt-SSLeay.ppd
use LWP::UserAgent; my $ua = new LWP::UserAgent; my $req = new HTTP::Request('GET', 'https://www.〜'); my $res = $ua->request($req); print $res->content . "\n";
インラインアセンブラの関数
__declspec( naked ) void __fastcall memcpy_test( void* dst, const void* src, size_t nsize ) { mov [esp-4], edi mov [esp-8], esi mov edi, ecx // dst mov esi, edx // src mov ecx, [esp+4] // nsize cld rep movsb mov esi, [esp-8] mov edi, [esp-4] ret 4 }
上記簡易memcpy関数は大抵の場合は問題なく動いたが、割り込みが入るプログラムではスタックが書き換わり、保存したedi、esiの内容が不定となった
push edi push esi ; pop esi pop edi
もしくは
sub esp, 8 mov [esp], edi mov [esp+4], esi ; mov esi, [esp+4] mov edi, [esp] add esp, 8
とespをキチンと指定する
MMXやSSEレジスタを使う場合も元の値に復元してやる
フラグレジスタは・・・とりあえず未定