OBS! Föreningen Dataskydd.net upplöstes i maj 2024. Sidorna kan innehålla brutna länkar och information som är inaktuell.

Kommunundersökning - teknisk dokumentation

Kommunundersökning - teknisk dokumentation

Koden till vår kommunundersökning finns på GitHub: https://github.com/andersju/municipality-privacy

Där finns även instruktioner på engelska om exakt hur vi gjorde. I vår ursprungliga projektplan lovade vi dock att ha dokumentation även på svenska. Senare insåg vi att det var föga meningsfullt, men för att hålla vad vi sagt är nedanstående en översättning av README:n så som den såg ut vid commit a91dff7  den 13 september 2016.


Det här är ett projekt från Dataskydd.net för att analysera vissa privatlivsrelaterade aspekter av webbplatserna för Sveriges 290 kommunner. Programmet använder data genererad av OpenWPM och skapar en statisk sajt med resultaten.

Koden skrevs inte med avsikt att kunna användas generellt för sajtundersökningar - den kan vara ganska rörig på sina håll. Den här dokumentationen är främst till för att visa exakt hur undersökningen genomfördes.

Resultatet finns på https://dataskydd.net/kommuner/ och du kan ladda ner SQLite-databasen: https://dataskydd.net/kommuner/crawl-data.sqlite.gz

Vi fick ekonomiskt stöd från Internetfonden / IIS.

Hur du kan göra samma sak

Få igång OpenWPM. För den här analysen använde vi revision ba0eb6c.
git clone https://github.com/citp/OpenWPM/
..
cd OpenWPM
...
git checkout ba0eb6.

OpenWPM kommer med ett installationsskript gjord för Ubuntu 14.04. Använd inte skriptet om du kör något annat, utan ta bara en titt i koden för att se vad du behöver installera på ditt system.

Försök köra demo.py (observera: Python 2, inte 3!). Om det verkar funka, fortsätt.

Klona det här repot (municipality-privacy). Kopiera två av dess filer till katalogen där du installerade OpenWPM (låt säga att det är ~/openwpm):

cp data/municipalities.txt openwpm_municipalities.py ~/openwpm

Sedan:

cd ~/openwpm

OpenWPM's browse-kommando laddar en specificerad URL och försöker redan också besöka ett visst antal interna länkar.  Just nu (v0.6.2) besöks dock inga interna länkar om den initiala URL:en är en redirect. Sålledes behöver vi först använda curl för att klura ut den slutgiltiga URL:en för varje kommunsajt och lägg in alla dem i en lista (som vi sedan använder som input till OpenWPM):

cat municipalities.txt|cut -d"|" -f1|xargs -I url_initial curl -Ls \
-o /dev/null -w '%{url_effective}\n' -A "Mozilla/5.0 (Windows NT 6.1; WOW64) \
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36" \
url_initial >> municipalities_final_urls.txt

(Det här kan ta några minuter.)

Verifiera att vi fortfarande har 290 URL:er:

wc -l municipalities_final_urls.txt
290 municipalities_final_urls.txt

Innan du börjar: När vi först körde testet kraschade många sajter OpenWPM:s browser-hanterare med ett StaleElementReferenceException, orsakat av koden som försöker extrahera (och använda) interna länkar. I sällsynta fall hände det också att externa länkar behandlades som interna (en bugg som fixades i en senare version av OpenWPM). Ett väldigt tillfälligt fix för det första problemet och ett hyggligt sätt att komma runt det andra är att ändra några saker i automation/Commands/utils/webdriver_extensions.py.

Först, installera Python-paketet publicsuffixlist (observera att du kan behöva använda pip2 beroende på ditt system):

sudo pip install publicsuffixlist

Ändra sedan automation/Commands/utils/webdriver_extensions.py och lägg till följande någonstans längst upp:

from publicsuffixlist import PublicSuffixList

Slutligen, kommentera ut följande rader i funktionen get_intra_links:

domain = urlparse(url).hostname
links = filter(lambda x: (x.get_attribute("href") and x.get_attribute("href").find(domain) > 0 and x.get_attribute("href").find("http") == 0), webdriver.find_elements_by_tag_name("a"))

...och lägg istället till följande (ovanför return links):

psl = PublicSuffixList()
domain = psl.privatesuffix(urlparse(url).hostname)
links = []
for x in webdriver.find_elements_by_tag_name("a"):
    try:
        if x.get_attribute("href") and psl.privatesuffix(urlparse(x.get_attribute("href")).hostname) == domain and x.get_attribute("href").find("http") == 0:
            links.append(x)
        except:
            pass

Redigera också automation/Commands/browser_commands.py. I
funktion `browse_website`, kommentera bort följande två rader:

links = get_intra_links(webdriver, url)
links = filter(lambda x: x.is_displayed() == True, links)

Lägg istället till följande:

links_initial = get_intra_links(webdriver, url)
links = []
for x in links_initial:
    try:
        if x.is_displayed() == True:
          links.append(x)
    except:
        pass

Kör OpenWPM med vår fil:

python2 openwpm_municipalities.py

Det här kommer att ta många timmar. Crawl-datan hamnar i data/crawl-data.sqlite, och OpenWPM loggar till data/openwpm.log. Några sajter kommer förmodligen inte fungera (time out). Kör om OpenWPM med enbart dessa sajter (den existerande databasen skrivs inte över, utan uppdateras bara). Du vill först förmodligen ta bort de felande sajterna från databasen, eftersom sajtgeneratorn för närvarande inte hanterar multipla crawls av en sajt. Om till exempel sajterna med visit_id 283 och 290 misslyckades:

DELETE FROM http_response_cookies WHERE header_id IN (SELECT id FROM http_responses WHERE visit_id IN (283, 290));
DELETE FROM http_request_cookies WHERE header_id IN (SELECT id FROM http_requests WHERE visit_id IN (283, 290));
DELETE FROM http_responses WHERE visit_id IN (283, 290);
DELETE FROM http_requests WHERE visit_id IN (283, 290);
DELETE FROM profile_cookies WHERE visit_id IN (283, 290);
DELETE FROM site_visits WHERE visit_id IN (283, 290);

Sedan har vi ett Elixir-program med två huvudmoduler: en för att berika crawl-data.sqlite, och en för att generera en statisk sajt med resultaten.

Från din OpenWPM-katalog, kopiera data/crawl-data.sqlite till där-du-klonade-municipality-privacy/data.

Se till att du har Erlang (minst R18) och Elixir >= 1.2 installerade.

Gå till katalogen municipality-privacy. Installera dependencies:

mix deps.get

Modulen SiteGenerator.AddStuff gör diverse saker:

  • Lägger till kolumnen base_domain till tabellen http_requests och uppdaterar varje post (om t.ex. kolumn url har värde http://foo.bar.com/blah sätts base_domain till bar.com).
  • Lägger till diverse kolumner till tabellen site_visits (och sätter dem):
  • municipality_name: Kommunnamn, laddat från municipalities.txt
  • scheme: http eller https beroende på vad den initiala URL:en använder
  • hsts: Värde för Strict-Transport-Security om det är satt, 0 om det inte är satt (eller om det är satt till värde max-age=0)
  • referrer_policy: Värde för HTML-elementet meta referrer om satt, annars 0
  • first_party_cookies: Antal förstapartskakor
  • third_party_cookies: Antal tredjepartskakor
  • third_party_requests: Antal förfrågningar gjorde till domän annan än base_domain eller en av dess underdomäner

Modulen SiteGenerator.Process genererar en sida för varje kommun, en indexsida med en översikt, och en del statiska sidor. Dessa hamnar i web/kommuner, där du också hittar diverse annat (CSS, JS, bilder).

Kör dem:

mix run -e "SiteGenerator.AddStuff.start()"
mix run -e "SiteGenerator.Process.start()"

Eller kör i interaktivt Elixir-skal genom att sätta "iex -S" före, t.ex.:

iex -S mix run -e "SiteGenerator.AddStuff.start()"

Generera GeoJSON för kartan

D3.js används för en enkel visualisering av kommunerna, färgkodade enligt betyg.

För att skapa GeoJSON-filen:

Först, installera QGIS (fri mjukvara).

Ladda ner en shape-fil med Sveriges kommuner. Packa upp den någonstans.

Med sqlitebrowser, öppna data/crawl-data.sqlite, kör SELECT municipality_name, score, site_url FROM site_visits;, och exportera till CSV.

I QGIS:

Layer -> Add Layer -> Add Vector Layer. Välj .shp-filen från paketet du nyss laddade ner. Notera att encoding för just den filen bör vara ISO-8859-1.

Layer -> Add Layer -> Add Delimited Text Layer. Välj csv-filen vi genererade i sqlitebrowser. Välj "No geometry (attribute only table". Encoding gör bara UTF-8.

I Layers Panel, högerklicka "Kommungränser SCB 07" och välj Properties och sedan Joins. Klicka på plus-ikonen för att lägga till en join: Join layer `kommuner_betyg`, join field `municipality_name`, target field `KNNAMN`.

Slutligen: Layer -> Save as. Välj format GeoJSON, encoding UTF-8, CRS "Project CRS (EPSG:4326 - WGS 84)".

(Den här svenska guiden var mycket hjälpsam.)