07 paź, 2013
1 star/gwiazdka2 stars/gwiazdek3 stars/gwiazdek4 stars/gwiazdek5 stars/gwiazdek (no rated/brak ocen)

GUS – wyszukiwarka podmiotów w C#

W tym wpisie pokażę w jaki sposób zaimplementować w języku C#, mechanizm wyszukiwania danych podmiotu gospodarczego w rejestrze GUS, na podstawie numeru NIP lub REGON.

Komunikacja

GUS nie udostępnia usługi typu WebService, dzięki któremu można by w łatwy sposób przeszukać rejestr podmiotów. Pozostaje więc zasymulowanie pracy przeglądarki aby uzyskać dostęp do tych danych. Należy przeanalizować co wysyła przeglądarka (np. za pomocą programu Fiddler) i odtworzyć dokładnie ten proces, poprzez ustawienie odpowiednich cookies oraz pól formularza.

Etapy

Parametry wyszukiwania przesyłane są za pomocą formularza, czyli żądaniem typu POST. Dodatkowym utrudnieniem jest weryfikacja każdego żądania poprzez wprowadzenie tzw. captchy.

W ogólności, proces wyszukiwania można podzielić na trzy etapy:

  1. Pobranie captchy oraz cookies
  2. Wysłanie kodu weryfikacyjnego oraz parametrów wyszukiwania
  3. Parsowanie odpowiedzi HTML

Implementacja

Nie będę przedstawiać pełnej implementacji klasy, lecz najistotniejsze szczegóły. Jeśli potrzebujesz kompleksowe i działające rozwiązanie – napisz do mnie.

Inicjalizacja

Do pobrania/wysłania danych wykorzystamy klasę HttpWebRequest. Na poczatku zdefiniujmy metodę zwracającą instancję tej klasy z prawidłowo przygotowanymi nagłówkami: 

private static string GUSSearchURL = "http://stat.gov.pl/regon/";
private CookieContainer cookies = new CookieContainer();
private HttpWebRequest createHttpRequest(string url)
{
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
    request.Referer = "http://www.stat.gov.pl/regon/";
    request.ServicePoint.Expect100Continue = false;
    request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20130224 Firefox/21.0";
    request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
    request.Headers.Add("Accept-Encoding", "gzip,deflate,sdch");
    request.Headers.Add("Accept-Language", "pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4");
    request.CookieContainer = this.cookies;
    request.Timeout = 5000;
    return request;
}

Instancja klasy CookieCointainer będzie przechowywać ciasteczka dla wszystkich realizowanych żądań.

Pobranie captchy

Captcha jest dostępna pod adresem http://www.stat.gov.pl/regon/Captcha.jpg?(1-999). Należy wylosować liczbę z tego przedziału i przesłać żądanie GET na ten adres. W odpowiedzi otrzymamy treść typu Image/Jpeg, którą można wyświetlić użytkownikowi (lub wykorzystać system łamania captcha).  

public System.Drawing.Image DownloadCaptcha()
{
    Random r = new Random();
    int captchaId = r.Next(1, 999);
    HttpWebRequest request = this.createHttpRequest(String.Format("http://www.stat.gov.pl/regon/Captcha.jpg?{0}", captchaId));
    request.Method = "GET";

    HttpWebResponse response = null;
    try
    {
        response = (HttpWebResponse)request.GetResponse();
    }
    catch (Exception ex)
    {
        return null;
    }

    System.Drawing.Image captcha = System.Drawing.Image.FromStream(response.GetResponseStream());
    return captcha;
}

Odesłanie captchy

Gdy już mamy kod weryfikacyjny pobranej captchy, można wysłać żądanie POST z parametrami wyszukiwania oraz kodem.

Na początku zdefiniujemy funkcję, która uzupełni dodatkowe ciasteczka, które są wysyłane w finalnym żądaniu.

private void addCookiesToSendRequest(string captcha, string criteria, string value)
{
    Dictionary<string, string> cookieHeaders = new Dictionary<string, string>()
    {
      {"lastCritIx", "1"}, {"isAmbiguousAnswer", "false"}, {"toGenerNewVerifCode", "false"}, {"cCodeHistory", ""}, 
      {"openingPageType", ""},  {"focusedObjIdGlobal", "verifCodeTF"}, {"focusedObjCursorPos", "5"}, {"browser", "FF-21.0"}, {"testCookie", ""}
    };

    cookieHeaders.ToList().ForEach(x => this.cookies.Add(new Cookie(x.Key, x.Value, "/regon/", "www.stat.gov.pl")));
    this.cookies.Add(new Cookie("lastCCode", captcha, "/regon/", "www.stat.gov.pl"));
    this.cookies.Add(new Cookie(criteria, value, "/regon/", "www.stat.gov.pl"));
}

oraz typ wyliczeniowy, do określenia rodzaju wyszukiwania (po numerze NIP bądź REGON):

public enum SearchType { NIP, REGON };

Poniżej funkcja wysyłająca finalne żądanie do systemu GUS. Należy spreparować pola formularza, ciasteczka oraz wysłać żądanie typu POST.

public void ConfirmCaptcha(string captcha, SearchType searchType, string value)
{
    string searchField = null;
    string criteriaType = null;
    if (searchType == SearchType.NIP)
    {
        searchField = "1nip";
        criteriaType = "criterion1TF";
    }
    else
    {
        searchField = "0regon";
        criteriaType = "criterion0TF";
    }

    this.addCookiesToSendRequest(captcha, criteriaType, value);

    HttpWebRequest request = this.createHttpRequest(GUSSearchURL);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    string postFields = String.Format("queryTypeRBSet={0}&{1}00=&{1}11={2}&verifCodeTF={1}",
    searchField, captcha, value);
    byte[] postData = ASCIIEncoding.ASCII.GetBytes(postFields);
    request.ContentLength = postData.Length;
    Stream stream = request.GetRequestStream();
    stream.Write(postData, 0, postData.Length);
    stream.Close();

    HttpWebResponse response = null;
    try
    {
        response = (HttpWebResponse)request.GetResponse();
    }
    catch (Exception ex)
    {
        return;
    }
    string htmlContent = null;
    using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
    {
        htmlContent = streamReader.ReadToEnd();
    }
}

Parsowanie danych

Po odesłaniu captchy, odpowiedź należy ręcznie przeparsować.

Możliwe są następujące rodzaje odpowiedzi:

  • Na danym numerze NIP jest kilka podmiotów i należy doprecyzować podmiot po REGON
  • Numer NIP/REGON jest nieprawidłowy
  • Numer nie został odnaleziony
  • Niepoprawny kod weryfikacyjny
  • Wpis został odnaleziony (sukces)

Parsowanie danych jest dosyć proste. Kod HTML można załadować do np. HTMLDocument i wyszukać w DOM odpowiednie tabelki i wiersze z danymi.

Aktualizacja 2014-04-27

Od teraz odpowiedź serwera jest kompresowana GZipem. Wystarczy w instancji klasy HttpWebRequest ustawić właściwość AutomaticDecompression na DecompressionMethods.GZip. Koniecznie należy też podać adres bez WWW czyli http://stat.gov.pl/regon jako URL serwera.


Komentarze

14 odpowiedzi na „“GUS – wyszukiwarka podmiotów w C#””

  1. Andrzej pisze:

    Piękne dzięki, zaoszczędziłem sporo czasu.
    Pozdrawiam

  2. Michał pisze:

    Ekstra! Wszystko działa jak należy po przeparsowaniu. Wielkie dzięki za udostępnienie tego artykułu, bo sam to bym chyba miliard lat to robił :)
    Pozdrawiam!

  3. Piotr pisze:

    od kliku dni nie działa ten mechanizm. Albo awaria albo coś zmienili u siebie na stronie. Przychodzą w odpowiedzi jakieś krzaki.

  4. Bartek pisze:

    Cześć, czy ten mechanizm nadal działa? Walczyłem dzisiaj cały dzień z tym problemem ale niestety bezskutecznie. Za każdym razem otrzymuję komunikat "Twoja przeglądarka ma wyłączoną obsługę mechanizmu cookies.". Ciasteczka są dodane do CookieContainer, w debugu je widać ale po wysłaniu requesta confirmCaptcha za każdym razem pojawia się ten błąd. Będę wdzięczny za jakiekolwiek wskazówki.

    • admin admin pisze:

      Tak, mechanizm działa – przed chwilą sprawdziłem. Sprawdź programem Fiddler, jakie ciasteczka są ustawiane i porównaj z tym co masz w programie.

  5. PO pisze:

    Wydaje mi się, że może być problem z query stringiem który jest przekazywany w POST. W przeglądarce budowany qs wygląda trochę inaczej…

  6. admin admin pisze:

    Jeśli zrobicie wszystko tak jak jest napisane w artykule to musi działać. Sprawdziłem ten kod i jest OK. Nie zapomnijcie o kompresji oraz adresie bez WWW (http://stat.gov.pl/regon/)

  7. PO pisze:

    Mam jeszcze pytanie odnośnie parametrów:

    string captcha, SearchType searchType, string value

    captcha – tekst z obrazka

    value – nip/regon który sprawdzamy?

     

    Dzięki wielkie za pomoc :)

  8. Zenek pisze:

    Super, dzięki.
    Wszystko działa.

  9. Piotr pisze:

    NIP działa super a REGON nie. Nie rozumiem dlaczego

    • admin admin pisze:

      Należy porównać za pomocą programu Fiddler, jakie ciasteczka ustawia apka a jakie standardowy interfejs na WWW. W razie dalszych problemów pisz na PW.

Dodaj komentarz

*