Valla apan i en fyrkant

· 1371 ord · 7 minut(er) att läsa

Möjlighet att lämna betyg om olika typer av företag har funnits ett tag. Dels i branschspecifika register, om t.ex. hantverkare eller i Ted Valentins kartsajter om sushi, caféer, kyrkor och annat, och dels i mer allmänna förteckningar som Reco.se och Eniros avknoppning av Gula sidorna, Rejta.se (att två av de första bokstäverna är samma ser onekligen ut som en tanke). Men trots den potentiella nyttan som finns i dessa webbplatser har det liksom aldrig tagit ordentlig fart i Sverige, trots hög IT-mognad.

Samtidigt kan man konstatera att positionsbaserade tjänster som Gowalla och Foursquare som använder sig av en spelform (likt t.ex. ESP game som Googles Image labeler baseras på) för att locka till interaktivitet har lyckats få en hel del användare även i Sverige. Användare som checkar in på olika platser kan man använda för att på något sätt mäta olika ställens popularitet.

Jag som är väldigt intresserad av att integrera olika typer av data kunde förstås inte låta bli att tänka på hur man skulle kunna använda denna data på någon av webbplatserna som finns för att betygsätta företag. Eftersom jag inte längre jobbar på Eniro eller Reco.se har jag inte möjlighet att förändra någon av tjänsterna i sig utan är hänvisad till att göra det på annat sätt.

In kommer user scripts, skript som liksom vanliga skript körs lokalt på webbläsaren men med skillnaden att skripten inte laddas ned på order av webbsidan man besöker utan av webbläsaren själv efter att man installerat användarskriptet. User scripts är den allmänna formen men mest känd är nog insticksprogrammet Greasemonkey, vilket jag skrivit om några gånger tidigare. Greasemonkey tilllåter alltså användaren att installera Javascript som körs när besöker webbsidor och som på något sätt förändrar hur webbsidan visas eller fungerar.

I mitt fall vill jag alltså att information från Gowalla och Foursquare visas när jag tittar på information om ett visst företag. Såväl Gowalla som Foursquare har API-er som tillåter andra program eller tjänster att komma åt information som finns i deras databaser. Något sådant API finns dock inte för någon av de svenska rekommendationswebbplatserna. Här gäller det alltså att utnyttja information som redan finns på webbsidan.

Oavsett hur man får ut informationen så återstår det stora integrationsproblemet: hur man kopplar ihop ställen på rekommendationswebbplatsen med de på Gowalla och Foursquare. De har alla förstås sina olika ID i de olika databaserna och de kan inte användas för att koppla ihop dem med mindre än att någon bygger upp en databas med sådana kopplingar. Istället får man använda sig av annan information som finns tillgänglig.

Den information vi har tillgänglig på båda sidor är följande:

  • Namn
  • Kategori
  • Latitud
  • Longitud

Saker som finns under samma namn, i samma kategori och på samma plats är med största sannolikhet samma ställe. Problemet är att man inte kan förvänta sig någon perfekt träff.

Namnen på Gowalla och Foursquare är inmatade manuellt av användarna, på de andra sajterna kommer de antingen från register, säljare eller användare. Subway i Uppsala kan på ena stället ha namnet " Subway" medan det på andra sajten finns i form av två ställen " Subway Uppsala" och " Subway Vaksalagatan".

Därför gäller det att kunna se om två olika namn är tillräckligt lika för att de skulle kunna vara samma. Vad som är tillräckligt lika är inte helt enkelt att definiera. Utöver att det kan förekomma extra ord kan man även tänka sig att det smugit sig in stavfel (Subvvay) eller alternativa namn (Subwayaffären). Javascript innehåller inga inbyggda funktioner för strängjämförelse. Det finns möjlighet att söka efter förekomst av en sträng i en annan och möjligheten till reguljära uttryck men övriga former av likhet är svårare att testa.

Jag tänkte först använda mig av edit distance, antalet förändringar som krävs för att den andra strängen ska bli som den andra. Detta visade sig dock bli alltför beroende på längden av strängen och rätt matchning kunde missas eftersom skillnaden mellan “Subway” och “Subway Vaksalagatan” var för stor.

Sedan kompletterade jag med specialfall där den ena strängen förekom i början eller slutet av den andra och viktade upp det. Det fungerade betydligt bättre. Tyvärr tappade jag bort denna del av koden så jag fick ändå göra om det från scratch och valde då istället att använda mig av ett mer avancerat mått på strängavstånd. Valet föll till slut på Jaro-Winkler som ger en siffra mellan 0 och 1 på hur lika två strängar är. Jag hittade ingen implementation i Javascript så jag tog en PHP-version och översatte till Javascript. Jag märkte att resultatet inte blev som jag ville ha det ifall strängarna liknande varandra mer på slutet än början av strängen (“Uppsalas Subway” och “Subway”) och därför adderade jag även likheten mellan de två strängarna baklänges.

Vad gäller kategorierna har jag ännu inte gjort någon matchning på dem. En snygg lösning skulle innebära att jag mappade kategorierna i de olika systemen mot varandra. En enkel lösning skulle vara att börja med att plocka bort alla privata lägenheter och hus som ändå inte skulle vara intressanta i denna tillämpning.

Koordinaterna finns enkelt tillgängliga i Gowallas och Foursquares respektive API. Man kan skicka koordinaten och få tillbaka ställen som finns i närheten. Problemet blir istället att plocka ut koordinaten från rekommendationssajten. Eftersom inte jag heller har tillgång till internt data så får jag utnyttja att koordinaterna finns tillgängliga på webbsidan i anropet till Google Maps API för att ladda in kartan. På objektsidorna på Reco.se är det två variabler vid namn vCLat och vCLon (namnet hämtas på samma sätt).

Greasemonkey-skriptet fungerar enligt följande:

  1. Skapa en ruta vi kan visa informationen i senare. Fyll denna med laddningsinfo så länge
  2. Plocka ut namn och koordinater i webbsidan med en XPath-fråga
  3. Skicka ett AJAX-anrop till API-et med latitud och longitud som parametrar.
  4. När AJAX-anrop ankommer räkna ut likheten mellan namnet på sidan och respektive ställe.
  5. Sortera ställena efter stränglikhet
  6. Plocka ut värdet för stränglikhet för stället på plats 5
  7. Spring igenom listan igen och skriv ut en länk till stället om det har en likhet större eller lika med tröskelvärdet från föregående punkt.
  8. Uppdatera rutan med listan

Tyvärr saknar Javascript ett smidigt sätt för att sortera associativa arrayer så jag sorterar inte träffarna efter likhet än.

Träffarna med G inom hakparentes är från Gowalla och de med 4 är från Foursquare.

Detta är mest ett proof-of-concept eftersom användandet av API-erna inte känns helt sjyst även om man håller sig inom tjänsternas riktlinjer. Jag har inte riktigt lyckats luska ut ifall webbläsaren cachar AJAX-requesterna (även om jag tycker att den borde göra det eftersom de görs med GET).

Helst borde förstås rekommendationswebbplatsen själv (i detta fall Reco.se) koppla sig till dessa API-er, plocka ut rätt ställe och presentera dessa. I annat fall skulle man kunna tillhandahålla denna information hos en tredje part från vilken man laddar ner informationen i ett userscript men som sparar informationen en viss tid i sin egen databas (och som kan ta nytta av att hålla koll på platser som ligger nära varandra och inte belasta API-erna i onödan) och där man kan göra handpåläggning för att sortera bort irrelevanta platser (och bidra till Foursquare och Gowalla genom att identifiera dubbletter).

Vill man testa själv kan man prova följande skript i Firefox (fungerar inte i Chrome pga. något strul med AJAX-anropen i användarskript). Man behöver en API-nyckel från Gowalla vilken man får mata in första gången skriptet körs och som sedan sparas i webbläsaren.

Koden till Greasemonkey-skriptet hos Pastie

Vissa saker kunde förbättras i webbläsarnas hantering av userscripts. En sak är parsning av JSON-objekt. I min kod så gör jag en eval på JSON-objektet vilket innebär att koden kör kod som kommer från Gowalla respektive Foursquare som Javascript-kod när det egentligen bara är data. Detta är ett säkerhetshål som bara väntar på att utnyttjas och som dessutom gör hanteringen onödigt långsam. En annan sak som vore bra är om man som användare enkelt kunde inspektera vilka värden Greasemonkey-skriptet lagrat (i detta fall API-nyckeln för Gowalla) och enkelt ta bort eller ändra dessa värden i webbläsarens användargränssnitt.

En hel rad bekvämlighetsfunktioner för att komma runt saknade funktioner i Javascript vore också trevligt. Saker som jag behövt programmera mig runt är bl.a. möjligheten att finna minsta värde i en array, kunna spegelvända strängar och hantera defaultparametrar i funktioner.