WebRequest: come trovare un codice postale utilizzando un WebRequest su questo contentType = “Application/XHTML+XML, TEXT/XML, TEXT/HTML; chatset = UTF-8 "?

StackOverflow https://stackoverflow.com/questions/1455567

Domanda

Ho pubblicato questo per la prima volta: HTTPWebRequest: come trovare un codice postale al Canada Post tramite una WebRequest con X-WWW-Form-Flosed?.

Seguendo i suggerimenti di Anthonywjones, ho cambiato il mio codice seguendo i suoi suggerimenti.

Alla continuazione della mia indagine, ho notato con il tempo che il tipo di contenuto del Canada Post è più probabile che lo sia "Applicazione/XHTML+XML, TEXT/XML, TEXT/HTML; chatSet = UTF-8".

Le mie domande sono:

  1. Come facciamo WebRequest contro un tale sito di tipo contenuto?
  2. Dobbiamo continuare ad andare avanti con l'oggetto NameValueCollection?
  3. Secondo Scott Lance che mi ha fornito generosamente informazioni preziose all'interno della mia domanda precedente, la WebRequest restituirà il tipo di informazioni qualunque sia il tipo di contenuto, mi manca qualcosa qui?
  4. Devo cambiare il mio codice a causa della modifica del tipo di contenuto?

Ecco il mio codice in modo che possa essere più facile capire i miei progressi.

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;
    }
}
}

Questo codice restituisce effettivamente il codice sorgente HTML del modulo che si deve compilare con le informazioni richieste per elaborare con la ricerca del codice postale. Quello che desidero è ottenere il codice sorgente HTML o qualunque cosa possa essere con il codice postale trovato.

MODIFICARE: Ecco WebException che ottengo ora: "Impossibile inviare un corpo di contenuto con questo tipo di verbo". (Questa è una traduzione dall'eccezione francese "Impossible D'Uvoyer Un Corps de Contenu AVEC CE Type de Verbe.")

Ecco il mio codice:

    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;
}

La linea che causa l'eccezione è puntata con "=>". Sembra che non posso usare come metodo, eppure questo è ciò che mi è stato detto di fare ...

Qualche idea di cosa mi manca qui? Cerco di fare ciò che Justin (vedi risposta) mi ha consigliato di fare.

Grazie in anticipo per qualsiasi aiuto! :-)

È stato utile?

Soluzione

Come introduzione al mondo dello scraping dello schermo, hai scelto una custodia molto dura! La pagina di ricerca del Canada Post funziona in questo modo:

  1. La prima pagina è un modulo che accetta i valori dell'indirizzo
  2. Questa pagina pubblica un secondo URL.
  3. Quel secondo URL a sua volta reindirizza (usando un reindirizzamento HTTP 302) a un terzo URL che mostra effettivamente la risposta HTML contenente il codice postale.

A peggiorare le cose, la pagina nel passaggio n. 3 deve conoscere il cookie impostato nel passaggio 1. Quindi devi usare lo stesso CookieContainer per tutte e tre le richieste (anche se potrebbe essere sufficiente inviare lo stesso CookieContainer solo a #2 e #3).

Inoltre, potrebbe essere necessario inviare ulteriori intestazioni HTTP anche in queste richieste, come accettare. Sospetto che dove stai affrontando problemi sia che HttpWebRequest per impostazione predefinita si reindirizza in modo trasparente per te ... ma quando reindirizza in modo trasparente potrebbe non aggiungere le intestazioni HTTP giuste necessarie per impersonare un browser.

La soluzione è impostare il HttpWebRequest'S AllowAutoRedirect Proprietà a False e gestire il reindirizzarsi da soli. In altre parole, una volta che la prima richiesta restituisce un reindirizzamento, dovrai estrarre l'URL nel HttpWebResponse'S Location: intestazione. Allora dovrai creare un nuovo HttpWebRequest (Questa volta una richiesta regolare, non un post) per quell'URL. Ricorda di inviare lo stesso biscotto! (il CookieContainer la classe lo rende molto semplice)

Potrebbe anche essere necessario effettuare una richiesta aggiuntiva (n. 1 nella mia lista sopra) per impostare il cookie della sessione. Se fossi in te, suppongo che ciò sia richiesto, semplicemente per eliminarlo come un problema e provare a rimuovere quel passaggio più tardi e vedere se la tua soluzione funziona ancora.

Ti consigliamo di scaricare e utilizzare Fiddler (www.fiddlertool.com) per aiutarti con tutto questo. Fiddler ti consente di guardare le richieste HTTP che passano oltre il filo e ti consente (tramite la funzione Builder di richiesta) ti consente di creare richieste HTTP in modo da poter vedere quali intestazioni sono effettivamente richieste.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top