Weblog Tomasza Przechlewskiego [Zdjęcie T. Przechlewskiego]


scrum
random image [Photo gallery]
Zestawienie tagów
1-wire | 18b20 | 1wire | 2140 | 3rz | alsamixer | amazon | anniversary | antypis | apache | api | applebaum | arm | armenia | astronomy | asus | atom.xml | awk | aws | bachotek | bakłażan | balcerowicz | balta | bash | berlin | bibtex | bieszczady | biznes | blogger | blogging | blosxom | bono | borne-sulinowo | breugel | bt747 | budapeszt | bursztyn | canon | cedewu | chello | chiller | chillerpl | chown | chujowetaśmy | ciasto | cmentarz | contour | cron | css | csv | curl | cycling | d54250wykh | dbi | debian | dejavu | dhcp | dht22 | dia | docbook | dom | ds18b20 | dyndns | dynia | ebay | economy | ekonomia | elka | elm | emacs | emacs23 | english | ess | eu | excel | exif | exiftool | f11 | fc | fc11 | fc15 | fc29 | fc5 | fc8 | fedora | fedora21 | fenix | ffmpeg | finepix | firefox | flickr | fontforge | fontspec | fonty | food | fop | foto | france | francja | fripp | fuczki | fuji | fuse | gammu | garmin | gawk | gazwyb | gdańsk | gdynia | gender | geo | geocoding | georgia | gft | git | github | gmail | gmaps | gnokii | gnus | google | googlecl | googleearth | googlemaps | gotowanie | gphoto | gphoto2 | gps | gpsbabel | gpsphoto | gpx | gpx-viewer | greasemonkey | gruzja | grzyby | haldaemon | handbrake | historia | history | hitler | holocaust | holokaust | hpmini | humour | iblue747 | ical | iiyama | ikea | imap | inkscape | inne | internet | j10i2 | javascript | jhead | k800i | kajak | kamera | kleinertest | kml | kmobiletools | knuth | kociewie kołem | kod | kolibki | komorowski | konwersja | krutynia | kuchnia | kurski | latex | latex2rtf | latex3 | lcd | legend | lenny | lesund | lewactwo | liberation | linksys | linux | lisp | lisrel | litwa | lizbona | logika | ltr | lubowla | lwp | lwów | m2wś | malta | mapquest | mapsource | marvell | math | mathjax | mazury | mbank | mediolan | mencoder | mh17 | michalak | michlmayr | microsoft | monitor | mp4box | mplayer | ms | msc | mssql | msw | mswindows | mtkbabel | museum | muzyka | mymaps | mysql | nanopi | natbib | navin | nekrolog | neo | neopi | netbook | niemcy | niemieckie zbrodnie | nikon | nmea | nowazelandia | nuc | nxml | oauth | oauth2 | obituary | okular | olympus | ooffice | ooxml | opera | osm | otf | otftotfm | other | overclocking | ozbekiston | panoramio | pdf | pdfpages | pdftex | pdftk | perl | photo | photography | picasa | picasaweb | pim | pine | pis | pit | plotly | pls | plugin | po | podróże | politics | polityka | polsat | portugalia | postęp | powerpoint | prelink | problem | propaganda | pstoedit | putin | python | r | radio | random | raspberry pi | refugees | relaxng | ridley | router | rower | rowery | rpi | rsync | rtf | ruby | rugby | russia | rwc | rwc2007 | rwc2011 | rzym | samba | sem | sernik | sheevaplug | sienkiewicz | signature | sks | skype | skytraq | smoleńsk | sqlite | srtm | sshfs | ssl | staszek wawrykiewicz | statistics | stats | statystyka | stix | stretch | suwałki | svg | svn | swanetia | swornegacie | szwajcaria | słowacja | tbilisi | terrorism | tex | texgyre | texlive | thunderbird | tomato | totalnaopozycja | tourism | tramp | trang | truetype | ttf | turkey | turystyka | tusk | tv | tv5monde | twitter | typetools | ubuntu | uchodźcy | udev | ue | ukraina | umap | unix | upc | updmap | ups | utf8 | uzbekistan | varia | video | vienna | virb edit | vostro | wammu | wdc | wdfs | webcam | webdav | wh2080 | wiedeń | wikicommons | wilno | win10 | windows | windows8 | wine | wioślarstwo | word | wordpress | wrt54gl | ws1080 | wtyczka | ww2 | www | wybory | wybory2015 | włochy | węgry | xemex | xetex | xft | xhtml | xine | xml | xmllint | xsd | xslt | xvidtune | youtube | yum | zakopane | zakupy | zdf | zdrowie | łeba | świdnica | żywność
Archiwum
O stronie
wykorzystywany jest blosxom plus następujące wtyczki: tagging, flatarchives, rss10, lastbuilddatexhtmlmime. Niektóre musiałem dopasować nieco do swoich potrzeb. Więcej o blosxom jest tutaj
Subskrypcja
RSS 1.0
Trzecie ćwiczenie z Google Fusion Tables - sejm RP siódmej kadencji

Wprawdzie Sejm RP szybkimi krokami zmierza w stronę wybranego w 1936 r. Reichstagu określanego jako ,,najlepiej opłacany męski chór ma świecie'' (The best paid male chorus in the world -- określenie użyte podobno w amerykańskim czasopiśmie The Literary Digest) i być może już w kadencji ósmej ten stan ideału zostanie osiągnięty. Ale zanim to nastąpi zbierzmy dane dotyczące posłów wybranych do Sejmu 7 kadencji.

Zaczynam od ściągnięcia listy stron bibliograficznych posłów:

## Najpierw zestawienie posłów:
wget -U XX http://www.sejm.gov.pl/sejm7.nsf/poslowie.xsp?type=A -O lista-poslow.html
## Potem dla każdego posła z zestawiena:
cat lista-poslow.html | perl -e ' 
undef $/; 
$_ = <>; 
s/\n/ /g;

while ($_ =~ m@href="/sejm7.nsf/posel.xsp\?id=([0-9]+)\&amp;type=A@g ) {
    $id = $1; ## id posła
    $pos = "http://www.sejm.gov.pl//sejm7.nsf/posel.xsp?id=$id&amp;type=A";
    print STDERR $pos, "\n";
    system ('wget', '-U', 'XXX', "-O", "7_$id.html", $pos);
    sleep 3; ## --let's be a little polite--
}

Powyższe pobiera 460 stron, każda zawierająca biografię jakiegoś posła.

Z kolei poniższy skrypt wydłubuje co trzeba (data scraping) ze strony zawierających biografię pojedynczego posła (np. tego).

#!/usr/bin/perl
#
use Storable;
use Google::GeoCoder::Smart;
$geo = Google::GeoCoder::Smart->new();

# Domyślny kraj
my $GeoCodeCacheName = 'geocode.cache';
my $NewCoordinatesFetched=0; # global flag
my $kraj = 'Polska';
my $SLEEP_TIME = 3 ;
# Rok wyborów
my $baseyr = 2011; 
my ($data_day, $data_mc, $data_yr);

undef $/;
$_ = <>;
s/\n/ /g;

# Retrieve geocode cash (if exists)
# http://curiousprogrammer.wordpress.com/2011/05/11/faking-image-based-programming-in-perl/
my %hash = %{ retrieve("$GeoCodeCacheName") } if ( -f "$GeoCodeCacheName" ) ;

if (m@<h2>([^<>]+)</h2>@) { $posel = recode($1); }

# zgadnij płeć patrząc na imię (jeżeli kończy się na a->kobieta):
my @tmp_posel = split " ",  $posel;
if ( $tmp_posel[0] =~ m/a$/ ) { $sex='K' } else { $sex='M' }

# id posła:
if (m@http://www.sejm.gov.pl/sejm7.nsf/posel.xsp\?id=([0-9]+)@) {$id = $1; }

# liczba oddanych na posła głosów:
if (m@<p class="left">Liczba g\&#322;os\&oacute;w:</p>[ \n\t]*<p class="right">([^<>]+)</p>@) {
   $glosy = $1 ; }

# Data i miejsce urodzenia:</p><p class="right">29-06-1960<span>,&nbsp;</span>Gda&#324;sk</p>
if (m@Data i miejsce urodzenia:[ \n\t]*</p>[ \n\t]*<p class="right">([0-9\-]+)<span>,[ \n\t]*\&nbsp;[ \n\t]*</span>([^<>]+)</p>@ ) {
  $data = recode ($1); 
  ($data_day, $data_mc, $data_yr) = split "-", $data;
  ##print STDERR "R/M/D: $data_day, $data_mc, $data_yr\n";
  $mce = recode ($2);
}

# Zawód:
if (m@<p class="left">Zaw\&oacute;d:</p>[ \nt]*<p class="right">([^<>]+)</p>@ ) { $zawod = recode($1) ; }

if (m@klub.xsp\?klub=([^"<>]+)@ ) { $klub = recode($1); }

# Klub poselski:
if (m@Okr\&#281;g wyborczy:</p>[ \t\n]*<p class="right">([^<>]+)</p>@) {
  $okr = recode($1);
  ## Pierwszy wyraz to numer okręgu, reszta nazwa (może być wielowyrazowa)
  ($okrnr, $okrnz) =  $okr =~ m/([^ \n\t]+)[ \n\t]+(.+)/; ## -- sprawdzić czy działa --
}

# Poprawienie błędów
if ($mce =~ /Ostrowiec Świetokrzyski/) {$mce = 'Ostrowiec Świętokrzyski' }
elsif ($mce =~ /Stargard Szaczeciński/) {$mce = 'Stargard Szczeciński' ; }
elsif ($mce =~ /Białograd/ ) {$mce = 'Białogard'; }
elsif ($mce=~ /Szwajcaria/) {$mce = 'Szwajcaria,Suwałki' }
elsif ($mce=~ /Kocierz Rydzwałdzki/) { $mce= "Kocierz Rychwałdzki" }
elsif ($mce=~ /Stąporów/) { $mce= "Stąporków" }
elsif ($mce=~ /Szmotuły/) { $mce= "Szamotuły" }

# Wyjątki:
if ($posel=~ /Arkady Fiedler/ ) {$kraj = 'Wielka Brytania' }
elsif ($posel=~ /Vincent-Rostowski/) {$kraj = 'Wielka Brytania' }
elsif ($mce=~ /Umuahia/) {$kraj = 'Nigeria' }
elsif ($posel=~ /Munyama/) {$kraj ='Zambia'; }
## Imię kończy się na `A' ale to facet:
elsif ($posel=~ /Kosma Złotowski/ ) {$sex = "M"}

# Sprawdź czy data jest zawsze w formacie dd-mm-yyyy:
if ($data_yr < 1900) { print STDERR "*** $data_yr: $data for $posel ($id)\n"; }

# geokodowanie (uwaga na limit) 
my $coords = addr2coords($mce);
my $wiek =  $baseyr - $data_yr;
my $coords_okr = addr2coords($okrnz, 'r'); ($tmp_lat, $tmp_lng) = split " ", $coords;

my $kml_line = "<LineString><coordinates>$tmp_lng,$tmp_lat $coords_okr</coordinates></LineString>";

print STDERR "$id : $sex : $posel : $wiek\n";
print "$id;$sex;$posel;$data;$wiek;$mce,$kraj;$coords;$klub;$glosy;$zawod;$okrnr;$okrnz;$kml_line\n";

## Retrieve geocode cash
if ($NewCoordinatesFetched) { store(\%hash, "$GeoCodeCacheName"); }

## ## ## ## ## ##
sub recode {
  my $s = shift;

  $s =~ s/\&nbsp;/ /g; $s =~ s/\&#322;/ł/g; $s =~ s/\&oacute;/ó/g;
  $s =~ s/\&#324;/ń/g; $s =~ s/\&#321;/Ł/g; $s =~ s/\&#378;/ź/g;
  $s =~ s/\&#380;/ż/g; $s =~ s/\&#346;/Ś/g; $s =~ s/\&#379;/Ż/g;
  $s =~ s/\&#281;/ę/g; $s =~ s/\&#261;/ą/g; $s =~ s/\&#347;/ś/g;
  $s =~ s/\&#263;/ć/g; $s =~ s/[ \t\n]+/ /g;

  return $s;
}

## ## ## ## ## ##
sub addr2coords {
 my $a = shift ;
 my $r = shift || 'n';
 my ($lat, $lng) ;

 ##consult cache first
 if (exists $GeoCodeCache{"$a"} ) {
   ($lat,$lng) = split (" ", $GeoCodeCache{"$a"} );
 }
 else {
   my ($resultnum, $error, @results, $returncontent) = $geo->geocode("address" => "$a");
   $resultnum--; 
   $resultNo=$resultnum ;

   if (resultNo > 0) { print STDERR "** Location $a occured more than once! **" }
   if ($error eq 'OK') {
     $NewCoordinatesFetched=1;
     for $num(0 .. $resultnum) {
       $lat = $results[$num]{geometry}{location}{lat};
       $lng = $results[$num]{geometry}{location}{lng};
       ##print "*** LAT/LNG:$lat $lng ERROR: $error RES: $resultNo ***\n";
     }
   } else { print STDERR "** Location $a not found! due to $error **"  }
 }

 $GeoCodeCache{"$a"} = "$lat $lng"; ## store in cache
 sleep $SLEEP_TIME;

 if ($r eq 'r' ) { return "$lng,$lat"; } # w formacie KML
 else { return "$lat $lng"; }
}

Skrypt wydłubuje id posła (id), imię i nazwisko (imnz), datę urodzenia (data_ur), miejsce urodzenia (mce_ur), skrót nazwy partii do której należy (partia), liczbę oddanych na niego głosów (log), zawód (zawod), numer okręgu wyborczego (nrokregu) i nazwę okręgu (nzokregu). Do tego zgadywana jest płeć posłanki/posła oraz obliczany jest wiek w chwili wyboru jako $baseyr - $data_yr, gdzie $data_yr to rok urodzenia.

Miejsce urodzenia oraz nazwa okręgu są geokodowane. Współrzędne miejsca urodzenia są zapisywane jako zmienna wsp. Jako zmienna odleglosc zapisywana jest linia definiowana (w formacie KML) jako: współrzędne miejsce urodzenia--współrzędne okręgu wyborczego.

<LineString><coordinates>lng1,lat1 lng2,lat2</coordinates></LineString>

Zamiana kupy śmiecia (tj. wszystkich 460 plików HTML) na plik CSV zawierający wyżej opisane dane sprowadza się teraz do wykonania:

for i in 7_*html ; do perl extract.pl $i >> lista_poslow_7_kadencji.csv ; done

Jako ciekawostkę można zauważyć, że strony sejmowe zawierają 6 oczywistych błędów: Ostrowiec Świetokrzyski, Stargard Szaczeciński, Białograd, Kocierz Rydzwałdzki, Stąporów oraz Szmotuły. Ponadto wieś Szwajcaria k. Suwałk wymagała doprecyzowania. Czterech posłów urodziło się za granicą a pan Kosma jest mężczyzną i prosta reguła: jeżeli pierwsze imię kończy się na ,,a'' to poseł jest kobietą zawiodła.

Ostatecznie wynik konwersji wygląda jakoś tak (cały plik jest tutaj):

id;plec;imnz;dat_ur;wiek;mce_ur;wsp;partia;log;zawod;nrokregu;nzokregu;odleglosc
001;M;Adam Abramowicz;10-03-1961;50;Biała Podlaska,Polska;52.0324265 23.1164689;PiS;\
  12708;przedsiębiorca;7;Chełm;<LineString><coordinates>23.1164689,52.0324265 23.4711986,51.1431232</coordinates></LineString>
 ...

Teraz importuję plik jako arkusz kalkulacyjny do GoogleDocs, a następnie, na podstawie tego arkusza tworzę dokument typu FusionTable. Po wizualizacji na mapie widać parę problemów -- ktoś się urodził niespodziewanie w USA a ktoś inny za Moskwą. Na szczęście takich omyłek jest tylko kilka...

Geocoder nie poradził sobie z miejscowościami: Model/prem. Pawlak, Jarosław/posłowie: Kulesza/Golba/Kasprzak, Orla/Eugeniusz Czykwin, Koło/Roman Kotliński, Lipnica/J. Borkowski, Tarnów/A. Grad. We wszystkich przypadkach odnajdywane były miejsca poza Polską -- przykładowo Orly/k Paryża zamiast Orli. Ponadto pani poseł Józefa Hrynkiewicz została prawidłowo odnaleziona w Daniuszewie ale się okazało, że ta miejscowość leży na Białorusi a nie w Polsce. Też ręcznie poprawiłem...

Ewidentnie moduł Google::GeoCoder::Smart ustawia geocoder w trybie partial match, w którym to trybie Google intelligently handles incomplete input aka stara się być mądrzejszym od pytającego. Może w trybie full match byłoby lepiej, ale to by wymagało doczytania. Na dziś, po prostu poprawiłem ręcznie te kilka ewidentnie błędnych przypadków.

Po poprawkach wszystko -- przynajmniej na pierwszy rzut -- oka wygląda OK

Link do tabeli jest zaś tutaj.

Nawiasem mówiąc kodowanie dokumentów na stronach www.sejm.gov.pl jest co najmniej dziwaczne.

url | Sun, 29/01/2012 21:31 | tagi: , , , , , ,