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.
Aktualizacja 2015-03-24
Informacje na temat nowej wyszukiwarki GUSKomunikacja
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:
- Pobranie captchy oraz cookies
- Wysłanie kodu weryfikacyjnego oraz parametrów wyszukiwania
- 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}00={2}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.
Strona Internetowa
Potrzebujesz ładnej strony internetowej? Zobacz demo na: tej stronie
Piękne dzięki, zaoszczędziłem sporo czasu.
Pozdrawiam
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!
od kliku dni nie działa ten mechanizm. Albo awaria albo coś zmienili u siebie na stronie. Przychodzą w odpowiedzi jakieś krzaki.
Dzięki za info. Postaram się sprawdzić to.
Dzięki Tomku!
Dekompresja gzip + usunięcie prefixu rozwiązuje problem w 100%
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.
Tak, mechanizm dziaÅ‚a – przed chwilÄ… sprawdziÅ‚em. Sprawdź programem Fiddler, jakie ciasteczka sÄ… ustawiane i porównaj z tym co masz w programie.
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…
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/)
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 :)
Tak – w koÅ„cu szukamy albo po NIP albo REGON
Super, dzięki.
Wszystko działa.
NIP działa super a REGON nie. Nie rozumiem dlaczego
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.
Hej :) natknąłem się na ten sam problem!
W miejscu, gdzie w funkcji wysyłającej żądanie POSTem ustawiasz postFields musisz zmienić na:
queryTypeRBSet={0}00={2}11={2}&verifCodeTF={1}
regon jest zczytywany po pierwszym parametrze ({1}00={2}), który w przypadku oryginalnego kodu jest pusty!
Testowane, działa, pozdrawiam
Dzięki za zwrócenie uwagi :)
Dziwne, mam ten sam problem co Bartek. Pokazuje mi błąd: “Twoja przeglÄ…darka ma wyłączonÄ… obsÅ‚ugÄ™ mechanizmu cookies”. PorównywaÅ‚em cookies Fiddlerem i niestety nie wiem co może być nie tak.
Już wszystko w porzÄ…dku. MusiaÅ‚em dodać cookie, które otrzymaÅ‚em z captcha’y do requestu który pobiera już odpowiednie dane na podstawie nip/regon i captcha
Cześć,
Od niedawna na stronie http://stat.gov.pl/regon/ można zauważyć komunikat, że powstała nowa wyszukiwarka podmiotów, a stara nie jest już aktualizowana (przestali aktualizować dane w dniu 2014-11-07). W nowej wersji zapewne pozmieniały się główne mechanizmy pobierania ciasteczek, captcha etc. Z różnic widać gołym okiem, że wystarczy raz na początku sesji wpisać kod, a później wyszukiwać dowolną ilość podmiotów, jest też multiwyszukiwanie czego nie było w wersji poprzedniej. Czy ktoś z Was próbował się do nowej wersji jakoś dobrać z poziomu .net?
W wolnej chwili spróbuję się tym zająć
Cześć,
Czy jest szansa żeby coś z tym tematem zrobić?
Zawsze można odezwać się na priv i ustalić szczegóły.
No właśnie ostatnio zmienili interfejs. Mam nadzieję, ze uda się Tobie zająć tematem. Z tego co można z komunikatu wyczytać dane są nie aktualne w starej wyszukiwarce ;(
Teraz używają JSONa
opracowaÅ‚em porzÄ…dny parser na podstawie tego co mi zwraca ten “cudowny” web service.
Też tak macie że jak szukacie po NIPie to wszystko jest ok ale przy szukaniu po REGONie nagle odpowiedź nie zawiera NIPa a za to powtarza REGON ?