2009-09-21 11 views
3

먼저 게시 : HttpWebRequest: How to find a postal code at Canada Post through a WebRequest with x-www-form-enclosed?.WebRequest :이 ContentType = "application/xhtml + xml, text/xml, text/html; charset = utf-8"에 대해 WebRequest를 사용하여 우편 번호를 찾는 방법?

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이있다 : ". 동사의 이러한 유형의 콘텐츠 몸을 보낼 수 없습니다" (이것은 프랑스 예외를 번역 한 것입니다 "불가능 디부 envoyer 유엔 봉사단 드 contenu AVEC CE 형 드 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 (대답을 보시오)이 나를 추천 한 것을하려고 노력한다.

미리 도움을 청하십시오! :-)

+0

@Will : 우리를 계속 게시하십시오; 이 질문에 매우 관심이 있습니다! 블로그가 있습니까? –

+0

@pcambell : 관심을 가져 주셔서 감사합니다. 나는 당신이 그것을 활용할 수 있도록 여기에 조사 결과를 추가하여 당신에게 알려줄 것입니다. –

+0

전체 솔루션을 보유하고 있습니까? 작동 되나요? 나는 여기서도 똑같이하려고 노력하고있다. 감사! – VinnyG

답변

2

스크린 스크래핑의 세계에 대한 소개로서, 당신은 매우 어려운 경우를 골랐습니다! 캐나다 포스트의 조회 페이지는 다음과 같이 작동

  1. 첫 페이지는 주소가 두 번째 URL에
  2. 이 페이지에 게시하는 가치를 받아들이는 형태입니다.
  3. 두 번째 URL은 실제로 HTTP 302 리디렉션을 사용하여 우편 번호가 포함 된 HTML 응답을 보여주는 세 번째 URL로 리디렉션합니다.

3 단계의 페이지는 1 단계에서 설정된 쿠키를 알아야합니다. 따라서 동일한 CookieContainer을 # 3 및 # 3에만 보내면 충분하지만 3 가지 요청 모두에 동일한 CookieContainer을 사용해야합니다.

또한 이러한 요청에 Accept와 같은 추가 HTTP 헤더를 보내야 할 수도 있습니다. 나는 당신이 문제를 겪고있는 것으로 의심된다. ​​HttpWebRequest는 기본적으로 당신을 위해 투명하게 리디렉션을 처리한다. 그러나 투명하게 리다이렉트 할 때 브라우저를 가장하는 데 필요한 올바른 HTTP 헤더를 추가하지 않을 수있다.

해결 방법은 HttpWebRequestAllowAutoRedirect 속성을 false로 설정하고 리디렉션을 직접 처리하는 것입니다. 즉, 첫 번째 요청에서 리디렉션이 반환되면 의 Location: 헤더에있는 URL을 추출해야합니다. 그런 다음 해당 URL에 대해 HttpWebRequest (이번에는 POST가 아닌 일반 GET 요청)을 새로 만들어야합니다. 동일한 쿠키를 보내기위한 기억 장치! (CookieContainer 클래스를 사용하면 매우 편리합니다.)

세션 쿠키를 설정하려면 추가로 요청해야합니다 (위 목록에서 # 1). 내가 너라면,이 문제는 문제 해결을 위해 필요하다고 가정하고 나중에이 단계를 제거하고 솔루션이 여전히 작동하는지 확인하십시오.

모든 것을 돕기 위해 Fiddler (www.fiddlertool.com)를 다운로드하여 사용해야합니다. Fiddler를 사용하면 HTTP 요청을 유선으로 볼 수 있으며 요청 작성자 기능을 통해 HTTP 요청을 만들어 실제로 필요한 헤더를 볼 수 있습니다.

+0

@Justin Grant :이 정보를 제공해 주셔서 감사합니다. AnthonyWJones와 EricLaw-MSFT가 제안한 Fiddler를 이미 다운로드하여 설치했습니다. 나는이 모든 것을 헤더와 모든 것으로 이해하지 못합니다. 리디렉션에 대해 알고 있었지만 특정 사례를 처리하는 방법을 알지 못했습니다. 당신의 단서는 내가 나 자신의 재발견을 더 잘 처리해야한다는 것을 알게 해줄 것입니다. 귀하의 답변은 끝났습니다. 나는 그것으로 나의 해결책에 곧바로 도달 할 수 있기를 바랍니다. 괜찮 으면 다시 돌아와서 다른 질문이나 의견을 게시했는지 확인하십시오. –

+0

@Justin Grant : 두 번째 및 세 번째 요청에 대해서는 응답 URL에서 다른 요청을 시작하면됩니다. 내가 코딩 한 ServicesWebClient의 동일한 인스턴스? 두 번째 요청을 수행하기 위해이 주소로 데이터를 다시 업로드 할 수 있도록이 응답 URL을 어떻게 얻습니까? –

+1

안녕하세요 - 두 가지 가능성이 있습니다. URL이 항상 동일한 경우 (캐나다 우편 사이트의 경우), 코드의 각 단계에 대한 URL을 하드 코딩 할 수 있습니다. URL이 매번 다를 경우 HTML을 화면 스크래핑하여 URL을 찾아야합니다. 동일한 WebClient 인스턴스 또는 다른 인스턴스를 사용할 수 있습니다. 각 단계마다 쿠키와 헤더를 올바르게 설정해야합니다. –