3

我首先发布了这个:HttpWebRequest: How to find a postal code at Canada Post through a WebRequest with x-www-form-enclosed? .

根据 AnthonyWJones 的建议,我按照他的建议更改了我的代码。

在继续查询时,我注意到加拿大邮政的内容类型更可能是"application/xhtml+xml, text/xml, text/html; charset=utf-8"

我的问题是:

  1. 我们如何针对这样一个内容类型的网站进行 webrequest?
  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 作为方法,但这是我告诉我要做的......

知道我在这里缺少什么吗?我尝试做贾斯汀(见答案)建议我做的事情。

提前感谢您的帮助!:-)

4

1 回答 1

2

作为对屏幕抓取世界的介绍,您选择了一个非常困难的案例!加拿大邮政的查找页面是这样的:

  1. 第一页是一个接受地址值的表单
  2. 此页面发布到第二个 URL。
  3. 第二个 URL 依次重定向(使用 HTTP 302 重定向)到第三个 URL,该 URL 实际上向您显示包含邮政编码的 HTML 响应。

更糟糕的是,第 3 步中的页面需要知道第 1 步中设置的 cookie。因此,您需要对所有三个请求使用相同的请求(尽管仅将相同的请求发送到 #2 和 #3 CookieContainer可能就足够了)。CookieContainer

此外,您可能还需要在这些请求中发送额外的 HTTP 标头,例如 Accept。我怀疑您遇到问题的地方是 HttpWebRequest 默认情况下会为您透明地处理重定向 - 但是当它透明地重定向时,它可能不会添加模拟浏览器所需的正确 HTTP 标头。

解决方案是将HttpWebRequest'AllowAutoRedirect属性设置为 false,并自己处理重定向。换句话说,一旦第一个请求返回一个重定向,您就需要在HttpWebResponse'sLocation:标头中提取 URL。然后您需要HttpWebRequest为该 URL 创建一个新的(这次是常规的 GET 请求,而不是 POST)。记得发送相同的cookie!(该CookieContainer课程使这非常容易)

您可能还需要发出额外的请求(我上面列表中的#1)才能设置会话 cookie。如果我是你,我会假设这是必需的,只是为了消除它作为一个问题,然后尝试删除该步骤,看看你的解决方案是否仍然有效。

您需要下载并使用 Fiddler ( www.fiddlertool.com ) 来帮助您完成这一切。Fiddler 允许您查看通过网络传输的 HTTP 请求,并允许您(通过请求构建器功能)允许您创建 HTTP 请求,以便查看实际需要哪些标头。

于 2009-09-22T17:45:48.747 回答