[BlueLeaf1336]> PROBLEMS> MizuhoGetter>

MizuhoGetter > HTMLソースの取得(2)

historyTOP

2004/10/30:作成
2004/10/31:更新
2004/11/01:更新

overviewTOP

次の6種類のURLに対して、連番部分をインクリメントしながらHTMLソースを取得します。取得したHTMLソースに

<TITLE>404 Not Found</TITLE>

が含まれていたら、インクリメントを中断します。(IEで適当なURLを入力すると HTTP 404 未検出 となりますが、プログラムで取得すると 404 Not Found です。なんで違うんですかねぇ?)

2004/10/31TOP

ゆっくり考えてみると、やっぱり元の方法、ちゃんとリンクのページを読み込んでそこに書いてあるリンクを辿って読み込むべきページを探すほうがよさそうです。連番ページ(上の7つのうち「0001」をインクリメントしていくページ)は別に問題がないですが、日付をインクリメントしていくページだと、何年何月からやるのかを考えないといけなくなります。しかも、予定していた「Not Found」が見つかった時点でやめるというのも、「h1609」から常に始めるなんて事をやっていると、いつかは、「h1609」のページ自体が(過去のデータの方に移動されるだろうから)なくなってしまいすぐに「Not Found」という話になってしまいます。そうすると「Not Found」でもしばらく続けて先を読んでいくなんてことになったりして、全然楽じゃないやん、ということに。

そういうことですか。作り始める前に気づいてよかった。

HTMLの取得その2 - 進捗状況通知付きTOP

何よりもまず、寄り道をします。次のようなサイトを見てみると、インターネット上のファイルのサイズを取得できるというように書いてあります。実際に取得できます。できない場合にどうするかについては深く考えません。

ファイルサイズを取得して何をするかというと、プログレスバーを表示します。プログレスバー大好きです。いかにも何かを処理してますヨ、感がなんともいえないです。

HTMLの取得その2 - ソースコードTOP

既にしてほぼ勝手に転載状態のソースコードを、よりわかりにくく修正します。まずは、宣言です。何となくいるかも、と思った進捗の状態を列挙型で宣言しています。サイズ取得後とファイル読み取り中と読み取り完了後にこのフラグのいずれかがコールバックされます。

それから、状況通知用のコールバック関数です。多分こういうのをコールバックと呼ぶはずなんですが、早い話、「何かあったらこの関数を呼んでくれ」といって関数そのものを渡しておく、というものです。で、ここでは(プログレスバーの最大値をセットしたいので)ダウンロードしたいファイルのサイズがわかったとき、(プログレスバーを徐々に進ませたいので)ダウンロード中の既に読込済みのサイズ、それから(何となく)全部のファイルをダウンロードし終わったとき、の3箇所、もちろんダウンロード中は1024バイト固定のバッファで読み取っているので、ファイルサイズがでかくなればなるほど何度も呼び出されますが、とにかく、そういうタイミングで関数が呼ばれるようにしてあります。

type
    //  進捗の種類
    TProgressMode = (pmFirst = 0, pmProgress = 1, pmFinish = 2);
    //  進捗状況通知コールバック関数
    TProgress = procedure (const MaxValue, CurValue: integer;
                           const Mode: TProgressMode) of object;
//  HTMLソース取得
function GetHtmlFile(Stream: TStream; const URL: string; Prog: TProgress = nil): Boolean;

それから関数の本体。むやみやたらと長くなってますが、長くなった部分もはっきりいうとパクッてます。

//  ---------------------------------------------------------------------------
//  HTMLソースの取得
function GetHtmlFile(Stream: TStream; const URL: string; Prog: TProgress = nil): Boolean;
const
    UserAgent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows 98)';
var
    hSess: HINTERNET;
    hServ: HINTERNET;
    Buf: string;
    Len, Sum: Cardinal;
    dwLength: integer;
    dwSize, Dummy: Cardinal;
begin
    Result:=False;

    //  インターネットにつながってるかチェック
    if not IsOnline() then exit;

    //  セッションを開いて
    hSess := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
    try
        //  いけてたら
        if Assigned(hSess) then
        begin
            //  URLを開く
            hServ := InternetOpenUrl(hSess, PChar(url), nil, 0, INTERNET_FLAG_RELOAD, 0);
            try
                //  いけてたら
                if Assigned(hServ) then
                begin
                    //  ファイルサイズを取得
                    if HttpSendRequest(hServ, nil, 0, nil, 0) then
                    begin
                        dwSize := SizeOf(dwLength);
                        Dummy := 0;
                        dwLength := 0;
                        HttpQueryInfo(hServ,
                                      HTTP_QUERY_CONTENT_LENGTH + HTTP_QUERY_FLAG_NUMBER,
                                      @dwLength, dwSize, Dummy);
                    end;

                    //  プログレスコール
                    if Assigned(Prog) then Prog(dwLength, 0, pmFirst);

                    //  全部読み込めるまでくり返す
                    Sum := 0;
                    while true do
                    begin
                        //  読み取りバッファのサイズを 1024 バイトにセット
                        SetLength(Buf, 1024);
                        //  読み取り
                        InternetReadFile(hServ, @Buf[1], Length(Buf), Len);
                        //  読み込んだサイズを調べる(0なら最後までいけた)
                        Inc(Sum, Len);
                        if Len = 0 then Break;
                        //  プログレスコール
                        if Assigned(Prog) then Prog(dwLength, Sum, pmProgress);
                        //  読み込んだサイズにバッファを調節
                        SetLength(Buf, Len);
                        //  引数のストリームに書き込む
                        Stream.Write(Buf[1], Length(Buf));
                    end;
                    //  プログレスコール
                    if Assigned(Prog) then Prog(dwLength, Sum, pmFinish);
                    //  ストリームの現在位置を最初に戻す
                    Stream.Position := 0;
                    Result := True;
                end;
            finally
                //  URLを閉じる
                InternetCloseHandle(hServ);
            end;
        end;
    finally
        //  セッションを閉じる
        InternetCloseHandle(hSess);
    end;
end;

こんな感じで使えます。

//  進捗状況の表示
procedure TForm1.Progress(const MaxValue, CurValue: integer; const Mode: TProgressMode);
begin
    Gauge1.MaxValue := MaxValue;
    Gauge1.Progress := CurValue;
end;

//  テスト
procedure TForm1.Button1Click(Sender: TObject);
var
    MS: TMemoryStream;
begin
    MS := TMemoryStream.Create;
    GetHtmlFile(MS, Edit1.Text, Progress);
    Memo1.Lines.LoadFromStream(MS);
    MS.Free;
end;

このページでは何もしなかったわけですが、長くなったので、次に持ち越します。

EOFTOP