3

私は最初にこれを投稿しました: HttpWebRequest: How to find a postal code at Canada Post with a WebRequest with x-www-form-enclosed? .

AnthonyWJones の提案に従って、彼の提案に従ってコードを変更しました。

調査を続けていると、Canada Post の content-type が"application/xhtml+xml, text/xml, text/html; charset=utf-8" である可能性が高いことに気付きました。

私の質問は次のとおりです。

  1. このようなコンテンツ タイプの Web サイトに対して Web リクエストを行うにはどうすればよいでしょうか。
  2. NameValueCollection オブジェクトを使い続ける必要がありますか?
  3. 前の質問で貴重な情報を惜しみなく提供してくれた Scott Lance によると、WebRequest はコンテンツ タイプが何であれ、情報のタイプを返す必要があります。
  4. コンテンツ タイプが変更されたため、コードを変更する必要がありますか?

私の進行状況を理解しやすくするために、ここに私のコードを示します。

internal class PostalServicesFactory {
/// <summary>
/// Initializes an instance of GI.BusinessSolutions.Services.PostalServices.Types.PostalServicesFactory class.
/// </summary>
internal PostalServicesFactory() {
}
/// <summary>
/// Finds a Canadian postal code for the provided Canadian address.
/// </summary>
/// <param name="address">The instance of GI.BusinessSolutions.Services.PostalServices.ICanadianCityAddress for which to find the postal code.</param>
/// <returns>The postal code found, otherwise null.</returns>
internal string FindPostalCode(ICanadianCityAddress address) {
    if (address == null)
        throw new InvalidOperationException("No valid address specified.");

    using (ServicesWebClient swc = new ServicesWebClient()) {
        var values = new System.Collections.Specialized.NameValueCollection();

        values.Add("streetNumber", address.StreetNumber.ToString());
        values.Add("numberSuffix", address.NumberSuffix);
        values.Add("suite", address.Suite);
        values.Add("streetName", address.StreetName);
        values.Add("streetDirection", address.StreetDirection);
        values.Add("city", address.City);
        values.Add("province", address.Province);

        byte[] resultData = swc.UploadValues(@"http://www.canadapost.ca/cpotools/apps/fpc/personal/findByCity", "POST", values);

        return Encoding.UTF8.GetString(resultData);
    }
}

private class ServicesWebClient : WebClient {
    public ServicesWebClient()
        : base() {
    }
    protected override WebRequest GetWebRequest(Uri address) {
        var request = (HttpWebRequest)base.GetWebRequest(address);
        request.CookieContainer = new CookieContainer();
        return request;
    }
}
}

このコードは実際には、郵便番号検索で処理するために必要な情報を入力する必要があるフォームの HTML ソース コードを返します。私が望むのは、HTML ソース コードまたは見つかった郵便番号を含むものを取得することです。

編集:これが私が今得たWebExceptionです:「このタイプの動詞でコンテンツ本文を送信できません。」(これは、フランス語の例外「Impossible d'envoyer un corps de contenu avec ce type de verbe.」からの翻訳です。)

これが私のコードです:

    internal string FindPostalCode(string url, ICanadianAddress address) {
    string htmlResult = null;

    using (var swc = new ServiceWebClient()) {
        var values = new System.Collections.Specialized.NameValueCollection();

        values.Add("streetNumber", address.StreetNumber.ToString());
        values.Add("numberSuffix", address.NumberSuffix);
        values.Add("suite", address.Suite);
        values.Add("streetName", address.StreetName);
        values.Add("streetDirection", address.StreetDirection);
        values.Add("city", address.City);
        values.Add("province", address.Province);

        swc.UploadValues(url, @"POST", values);
        string redirectUrl = swc.ResponseHeaders.GetValues(@"Location")[0];
        => swc.UploadValues(redirectUrl, @"GET", values);
    }

    return htmlResult;
}

例外の原因となった行は「=>」で示されます。メソッドとして GET を使用することはできないようですが、これは私に言われたことです...

ここで何が欠けているのか分かりますか? Justin (回答を参照) が私に推奨したことを実行しようとしています。

助けてくれてありがとう!:-)

4

1 に答える 1

2

スクリーンスクレイピングの世界への導入として、あなたは非常に難しいケースを選びました! Canada post の検索ページは次のように機能します。

  1. 最初のページはアドレス値を受け入れるフォームです
  2. このページは 2 番目の URL に POST します。
  3. その 2 番目の URL は、(HTTP 302 リダイレクトを使用して) 3 番目の URL にリダイレクトされ、郵便番号を含む HTML 応答が実際に表示されます。

さらに悪いことに、ステップ 3 のページは、ステップ 1 で設定された Cookie を認識する必要があります。したがって、3 つの要求すべてに同じものを使用する必要があります (ただし、同じものを #2 と #3 のみ CookieContainerに送信するだけで十分な場合があります)。CookieContainer

さらに、Accept のように、これらのリクエストで追加の HTTP ヘッダーも送信する必要がある場合があります。問題が発生していると思われるのは、デフォルトで HttpWebRequest がリダイレクトを透過的に処理することですが、透過的にリダイレクトすると、ブラウザーになりすますために必要な適切な HTTP ヘッダーが追加されない場合があります。

解決策は、HttpWebRequestAllowAutoRedirectプロパティを false に設定し、リダイレクトを自分で処理することです。つまり、最初のリクエストでリダイレクトが返されたら、HttpWebResponseLocation:ヘッダーから URL を取り出す必要があります。HttpWebRequest次に、その URL に対して新しい (今回は POST ではなく通常の GET 要求)を作成する必要があります。同じクッキーを送信することを忘れないでください! (CookieContainerクラスはこれを非常に簡単にします)

セッション Cookie を設定するには、追加のリクエスト (上記のリストの 1) が必要になる場合もあります。私があなただったら、これは単に問題として排除するために必要であると想定し、後でそのステップを削除して、解決策がまだ機能するかどうかを確認します.

これらすべてを支援するために、Fiddler ( www.fiddlertool.com ) をダウンロードして使用することをお勧めします。Fiddler を使用すると、HTTP 要求がネットワーク上を通過するのを監視できます。また、(要求ビルダー機能を使用して) HTTP 要求を作成できるため、実際に必要なヘッダーを確認できます。

于 2009-09-22T17:45:48.747 に答える