野村XXはNetscapeを愛しています。 というかNetscapeのブックマークを、です。 IEのお気に入りの挙動に比べて素直なところが気に入っているんですが、 いかんせんユーザー数が減少の一途を辿っているせいか、 使い勝手の良い更新チェッカが見当たりません。 なら、作ってしまえというのが今回のネタ。 仕様 ・Netscape7のbookmarks.htmlからリンク先を抽出
・リンク先のページを過去に保存したものと比較
・更新されていたら自動でブラウザを立ち上げる
とりあえずこんな所でしょう。 あとはどれだけ手抜きして完成させるかだけです。
まずブックマークから<A HREF="http://...で始まるリンク先を切り出さなければなりません。 strstrとか使って地道にやってもいいんですが、ここはさくっと正規表現に頼ってしまいましょう。 正規表現のライブラリはboostを使用するということで、 使ったことのない方は、こことかこことか参照。 MS万歳な人はATLとかでお茶を濁すのもありでしょう。 とりあえず、たまーに役に立つのでHDDにboostを転がしておくと幸せになれます。 続いて必要はないのですが、せっかくなのでサイト名も切り出します。 Netscape7は文字コードがUTF-8になっているのでs-jisに変換しなければなりません。 今まで文字コードなんて気にもしなかったので、UTF-8 = unicode だと 思い込んでしまって大混乱してしまいました。
分かってしまうと大したことではなく、MultiByteToWideChar->WideCharToMultiByteの順で APIを呼び出してあげるだけで解決。
そういえばboostの正規表現ってUTF-8に対応しているかどうか謎です。 とりあえず動いているので良しとしておきましょう。 http接続は、ここを参考にWinInetAPIにお任せします。
InternetOpenUrlを使って、ついでにInternetSetOptionでタイムアウト値を セットしておくのもありでしょう(タイムアウト設定なしだと固まる可能性がある)。 あとはざくざくhtmlを読み込んでファイルに吐き出します。 過去の履歴と比較して更新されていたり、新規リンク先だったりした場合は ブラウザを立ち上げます。 これはShellExecuteでできます。 2番目の引数に"open"を指定してもURLを開けますが、 複数のブラウザを立ち上げることができない(ひとつのブラウザの表示内容が切り替わるだけ)ので 3番目の引数に実行ファイル名を直接入れて呼び出します。 以上で完成です、んー簡単ですね。 あんまり簡単なのでソースをそのまま張っておきます。 なんとなく今日はC++の気分なので、C++色の強いコードになってます。 と、作ってみたもののカウンタや、バナーのちょっとした変更とかでも 敏感に反応して更新判定されてしまうので、いまいち使いづらいです。 いきなりブラウザを立ち上げるのではなく、一度ユーザーに聞いてあげる方が 親切かもしれません。 実用性は?ですが更新チェッカとしては最短コードの部類ではないかと。 使い方は実行ファイルがある場所にdataというディレクトリを掘ってから、 コマンドラインで引数にブックマークファイル名を指定して起動するだけです。 というかリンク先の入ったhtmlなら何でもOKです。 IEのお気に入りをエクスポートしたファイルでは必要ありませんが、 Netscapeの場合は引数に-UTF8を付けてください。
#include <windows.h>
#include <wininet.h>
#include <memory.h>
#include <iostream>
#include <fstream>
#include <string>
#include <boost/regex.hpp>
#pragma comment( lib, "wininet.lib" )
#define MAXSTRING ( 1024 * 10 ) // なんとなく10K
struct URLSTRINGS{
std::string Host, Path, Name;
};
int main( char argc, char* argv[] )
{
// 引数解析( -UTF8はNetscapeのブックマーク用 あとはファイル名)
bool IsUTF8 = false;
char FileName[MAX_PATH];
for( int l = 0; l < argc; l++ ){
if( strcmp( argv[l], "-UTF8" ) == 0 ) IsUTF8 = true;
else strcpy( FileName, argv[l] );
}
// ブックマークからリンクアドレスを抽出
std::ifstream fin( FileName );
if( fin.fail() ) return -1;
char Buffer[MAXSTRING];
std::vector< URLSTRINGS > URL;
while( fin.getline( Buffer, MAXSTRING ) ){
boost::reg_expression<char> Regex = "(<A HREF=\"http://)([^/]*)([^\"]*)(\".*>)(.*)(<)";
boost::match_results<const char*> Results;
if( boost::regex_search( Buffer, Results, Regex ) ){
URLSTRINGS s;
s.Host = Results.str( 2 );
s.Path = Results.str( 3 );
if( IsUTF8 ){
// UTF-8 -> Unicode -> s-jis
MultiByteToWideChar( CP_UTF8, 0, Results.str( 5 ).c_str(), -1, (wchar_t *)Buffer, MAXSTRING );
char Buffer2[MAXSTRING];
WideCharToMultiByte( CP_ACP, 0, (wchar_t *)Buffer, -1, Buffer2, MAXSTRING, NULL, NULL );
s.Name = Buffer2;
}
else s.Name = Results.str( 5 );
URL.push_back( s );
}
}
fin.close();
// httpでリンク先ファイル取得
HINTERNET hInet = InternetOpen( "Tiny Link Checker", INTERNET_OPEN_TYPE_PRECONFIG, "", "", 0 );
if( !hInet ) return -1;
for( unsigned int i = 0; i < URL.size(); i++ ){
HINTERNET hConn = NULL, hReq = NULL;
bool IsExist = false;
bool IsSame = true;
hConn = InternetConnect( hInet, URL[i].Host.c_str(), INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);
if( !hConn ) goto FINISH;
hReq = HttpOpenRequest( hConn, "GET", URL[i].Path.c_str(),
"HTTP/1.0", NULL, NULL, 0, NULL );
if( !hReq ) goto FINISH;
if( !HttpSendRequest( hReq, NULL, 0, NULL, 0 ) ) goto FINISH;
{
BOOL Result;
// ファイル名にできない文字を_に置換
strncpy( Buffer, ( URL[i].Host + URL[i].Path ).c_str(), MAXSTRING - 1 );
for( int j = 0; j < MAXSTRING - 1; j++ ){
if( Buffer[j] == '\\' || Buffer[j] == '/' || Buffer[j] == ':' ||
Buffer[j] == '*' || Buffer[j] == '?' || Buffer[j] == '"' ||
Buffer[j] == '<' || Buffer[j] == '>' || Buffer[j] == '|' ) Buffer[j] = '_';
}
// 以前アクセスしたログを調べる
std::ifstream fin( ( std::string( ".\\data\\" ) + Buffer ).c_str(), std::ios::binary );
char *pCash = NULL;
if( fin.fail() == 0 ){
IsExist = true;
fin.seekg(0, std::ios::end);
int Size = fin.tellg();
pCash = new char[Size];
fin.seekg(0, std::ios::beg);
fin.read( pCash, Size );
fin.close();
}
// ログと比較
std::ofstream fout( ( std::string( ".\\data\\" ) + Buffer ).c_str(), std::ios::binary );
if( fout.fail() ) goto FINISH;
int Pos = 0;
do{
DWORD Size = 0;
ZeroMemory( Buffer, MAXSTRING );
Result = InternetReadFile( hReq, Buffer, MAXSTRING - 1, &Size );
if( Size == 0 ) break;
if( IsSame && IsExist ){
if( memcmp( pCash + Pos, Buffer, Size ) != 0 ) IsSame = false;
Pos += Size;
}
fout.write( Buffer, Size );
} while( Result );
delete[] pCash;
fout.close();
}
// ブラウザ起動(IEを使いたい時はiexplore.exe)
if( !IsSame || !IsExist ) ShellExecute( NULL, NULL, "Netscp.exe", ( URL[i].Host + URL[i].Path ).c_str(), NULL, SW_NORMAL );
// 結果表示
std::cout << "[" << i + 1 << "/" << URL.size() << "] " << URL[i].Name.c_str() << std::endl;
if( !IsExist ) std::cout << "- 新規 -" << std::endl;
else{
if( !IsSame ) std::cout << "- 更新 -" << std::endl;
else std::cout << "- ---- -" << std::endl;
}
std::cout << ( URL[i].Host + URL[i].Path ).c_str() << "\n\n";
FINISH:
if( hReq ) InternetCloseHandle( hReq );
if( hConn ) InternetCloseHandle( hConn );
}
return 0;
} |