Vi hentet ti kjeders tilbudsaviser — uten å åpne én eneste PDF
Avis-ID-en skifter hver uke. Det er den ene detaljen som avgjør om automatiseringen lever eller dør på mandag.
En PDF-katalog på tolv megabyte inn, nittitre produkter ut. Bildene koblet vi på til slutt — og løsningen lå i fontvekten, ikke i posisjonen.
Vi hadde nettopp fått en PDF-katalog på tolv megabyte gjennom kverna. Nittitre produkter ut i den andre enden, med navn, artikkelnummer, pris og kategori. Hundre og ti bilder fra samme fil.
Så koblet vi bildene til produktene, og oppdaget at alle produktene på samme side hadde fått det samme bildet.
Regelen vi hadde brukt var «ta det største bildet på siden». Rimelig når det er ett produkt per side, verdiløs når det er åtte.
Artikkelen handler om hvorfor det skjer, hva som løste det, og hva du gjør når to systemer beskriver samme produkt med hvert sitt navn.
Kort vei til svaret: har du en katalog eller prisliste som skal bli til data, send oss en prøvefil — vi sier hva som lar seg hente og hva som ikke gjør det. Vil du forstå mekanikken selv, les videre.
Denne artikkelen bygger på arbeid vi har gjort selv, i to spor: en pipeline som henter produktdata og bilder ut av leverandørkataloger, og uttrekksjobber som leser kampanjeaviser side for side. Tallene er lest av fra kjøringene, ikke anslått i etterkant.
Det er verdt å si hva dette ikke er:
Det vi kan stå inne for: hver mekanisme under er bygget og kjørt, og hvert stoppunkt er et vi faktisk traff.
Formater og verktøy endrer seg. Artikkelen gjennomgås derfor hver gang formatene eller verktøyene endrer seg. Sist gjennomgått 14. august 2026.
Skrevet av Svein Tore Olsen, daglig leder i Ebizapple.
Dette er den ene setningen hele artikkelen hviler på.
Når du åpner en pdf-katalog og ser en tabell, ser du ikke en tabell. Du ser tekstbokser plassert på hvert sitt punkt i et koordinatsystem, med hver sin skrifttype og punktstørrelse. At de danner rader og kolonner for øyet ditt, er et resultat av at noen plasserte dem pent — ikke av at filen vet at de hører sammen. Formatet beskriver utseende, ikke betydning.
Praktiske konsekvenser, i den rekkefølgen de pleier å overraske folk:
Leserekkefølgen er ikke logisk rekkefølge. Tekstbokser ligger i den rekkefølgen de ble skrevet inn i filen — kolonnevis, tilfeldig, eller hoppende mellom to spalter. Kopierer du en tabell inn i et regneark, får du ofte noe som ser ut som data, men der prisen tilhører feil produkt.
En rad er ikke en enhet. «Lager 6308 · 2RS · 349,00» kan være tre tekstbokser, eller én, eller sju. Det ser du bare på koordinatene.
Det som ser likt ut, kan være ulikt. To tall i samme kolonne kan ha ulik skrifttype fordi det ene er et listenummer og det andre noe annet. Øyet ditt filtrerer det bort. En maskin gjør ikke det — med mindre du ber den om det. Der lå løsningen på bildeproblemet vårt.
Noen PDF-er har ikke tekst i det hele tatt. En skannet katalog er bilder av sider. Da bytter du et strukturproblem mot et lesefeilproblem.
Det er samme skille vi beskriver i gjennomgangen av å hente produktdata fra en nettside: forskjellen mellom det et menneske ser og det en maskin får utlevert, er større enn folk tror. Der var det DOM-en mot HTML-en. Her er det layout mot data.
Det finnes fire, og vi har brukt alle. Oftest to sammen.
| Metode | Passer når | Bryter sammen når |
|---|---|---|
| Koordinater og font — les tekstlaget med posisjon og skrifttype | Dokumentet er maskingenerert, typografien konsekvent | Malen skifter midt i katalogen, eller filen er skannet |
| Regeluttrekk — flat teksten ut og kjør et mønster over den | Radene har fast form: «tekst, datointervall, pris, kr» | Ett produkt mangler pris, eller et navn ligner et datointervall |
| Språkmodell — send dokumentet inn, be om strukturerte felter | Layouten er uryddig og feltene varierer fra side til side | Du trenger garantert samme svar hver gang |
| Tekstgjenkjenning — les sidene som bilder | Dokumentet er skannet, uten tekstlag | Trykket er dårlig, eller 0/O og 1/l må skilles presist |
Den siste kolonnen er den viktigste. Alle fire virker på et godt dokument. Det er hvordan de svikter som avgjør valget — og særlig om de sier fra når de svikter.
Har filen et tekstlag, får du mer enn tegnene. Hver tekstboks ligger på et punkt i et koordinatsystem, med sin egen skrifttype og punktstørrelse — og alt det er data du kan sortere på. Det er slik en «rad» i det hele tatt lar seg gjenskape: ikke fordi filen vet at feltene hører sammen, men fordi de står på samme høyde.
Metoden er den mest presise av de fire når den først treffer, og det var den som til slutt løste bildeproblemet i denne jobben. Prisen er at den arver malen. Skifter katalogen oppsett midt i filen — et innstikk, en annonse, en temaside med annen typografi — slutter regelen å gjelde, og den sier ikke fra. Derfor bruker vi den på dokumenter vi har sett gjennom, ikke på et vedlegg vi mottar for første gang.
Kampanjeavisene er det reneste eksempelet. Jobben vår henter siden, stripper bort all markering, klipper ut strekket mellom to faste overskrifter og kjører ett mønster over resten som plukker ut varenavn, gyldighetsperiode og pris.
Det kjører på sekunder og koster nesten ingenting. Det er også helt avhengig av at avisen har akkurat de to overskriftene, og at hver linje har de tre feltene i den rekkefølgen. Fjernes den ene overskriften, står du igjen med tom tekst.
Vi bygde også et hjelpeledd: én jobb som finner avisens interne løpenummer i sidekoden, slik at ingen må klippe det ut manuelt for hver nye avis. Slike kjedelige mellomledd avgjør om en automatisering kjører av seg selv eller krever en person hver mandag.
For dokumenter uten fast mal sender vi hele PDF-en inn til en språkmodell og ber om strukturerte felter tilbake — der kategorien må være én av en lukket liste, ikke fritekst.
To grep skiller noe som virker fra noe som ser ut som det virker:
Be om struktur, ikke om tekst som ser strukturert ut. Ber du en modell skrive JSON som fritekst, holder det lenge — helt til ett uunnslupt anførselstegn ødelegger hele svaret.
Regn ut summene selv. Antall, prisintervall, kategoritelling — alt som kan utledes av dataene, skal utledes av koden etterpå. Modellen skal levere radene, ikke telle dem.
Et tredje grep handler ikke om teknikk: hvert uttrukket produkt får et konfidensnivå og lander i en liste merket «til gjennomgang».
De tre veiene over leser tekst som allerede står i filen. En skannet katalog er bilder av sider, og da står ikke den teksten der. Igjen står tekstgjenkjenning — OCR — som leser sidene som bilder og gjetter seg frem til tegnene.
Forskjellen på tekstlag og OCR er ikke en gradsforskjell. Et tekstlag gir deg tegnene slik de ble satt, med skrifttype og posisjon på kjøpet. OCR gir deg en kvalifisert gjetning per tegn, uten typografien som de andre metodene lener seg på — og gjetningen er svakest der det koster mest: 0 mot O og 1 mot l, i artikkelnumre som ikke betyr noe og derfor ikke kan avsløre en feil ved å se rart ut.
Vi sier ikke nei til skannede dokumenter, men vi regner alltid med korrektur på dem, og verifiserer numrene mot en kjent liste før de får lov til å bli data. Er det et valg mellom OCR og å be leverandøren om den samme katalogen med tekstlag, er det siste alltid billigere.
Har du en katalog som skal bli til data? Vi ser på en prøvefil og sier hva som er mulig — og hva som blir dyrt. Be om en vurdering.
Dette er rådet vi gir oftest, og det som selger dårligst.
En PDF er alltid avledet. Noen har en database et sted, og PDF-en er en utskrift av den. Å hente data ut av utskriften er å gjenskape noe som allerede finnes, med tap.
Så spør. En CSV-fil, et regneark, en ukentlig eksport. Mange leverandører har det liggende. Under står regelen vi bruker før vi sier ja til slikt arbeid.
| Situasjon | Vi ville | Hvorfor |
|---|---|---|
| Leverandøren kan sende CSV eller regneark | Ta eksporten | Ferdige felter slår enhver utgraving, og knekker ikke når malen endres. |
| Fast mal, skal leses hver uke | Bygg regeluttrekk | Billigst i drift. Legg inn en sjekk på at det faktisk kom noe ut. |
| Ett dokument, én gang, femti sider | Språkmodell + gjennomgang | Å bygge en regel lønner seg ikke for én kjøring. |
| Dokumentet er skannet | Regn med korrektur | Artikkelnumre ryker først. Verifiser mot en kjent liste. |
| Malen skifter mellom sidene | Del opp per malvariant | Ett mønster som skal dekke alt, dekker ingenting godt. |
| Tallene skal inn i et system som fakturerer | Stopp og be om eksport | Feil pris fra et uttrekk er ikke en skrivefeil, det er en faktura. |
| Du vet ikke hvilken rad som gjelder deg | Få dokumentet vurdert | Én prøvefil er nok — send den inn. |
Legg merke til at «be om eksport» går igjen. Det er ikke beskjedenhet. Det er at en avtalt datakanal er den eneste varianten som fortsatt virker neste år.
Tilbake til produktene som alle fikk samme bilde.
Bildene lå i filen som egne objekter, og vi hentet dem ut med posisjon. Vi filtrerte bort alt under en viss flatestørrelse, ellers drukner du i ikoner, logoer og QR-koder.
Da satt vi med drøyt hundre bilder som visste hvor de var, og nittitre produkter som ikke visste noe om hvor de var. Ingen felles nøkkel.
Så oppdaget vi at katalogen selv hadde løsningen trykt på seg.
Hvert produktbilde hadde et lite tall stemplet på seg. En badge. I prislisten under sto de samme tallene som listenumre. Koblingen fantes — designeren hadde laget den for leseren. Problemet var at tallet «4» på et bilde og tallet «4» i prislisten er samme tegn for en maskin.
Første forsøk var å skille dem på x-posisjon: badges inne i bildeflaten, listenumre i margen. Det virket på de fleste sidene, og feilet der en bildeflate strakte seg over margen. En terskel som er nesten riktig er den verste typen — den gir et resultat som ser fornuftig ut.
Det som virket var fontvekten. Badgene var satt i halvfet, litt større. Listetallene i normal vekt, litt mindre. Ikke fordi noen tenkte på maskinlesing, men fordi det ser bedre ut. Skrifttype og punktstørrelse ligger lagret sammen med hver tekstboks — en visuell detalj for leseren var et entydig skille for koden.
Resultatet: de fleste produktene ble koblet til bildet sitt gjennom en verifiserbar badge. Resten falt tilbake på «største ubrukte bilde på siden» — og ble merket i grensesnittet som nettopp det.
To ting er verdt å ta med videre:
Verifiser koblingen objektivt. Vi sammenlignet sjekksummen på hver bildefil mot koblingstabellen i stedet for å se på skjermen og synes at det så riktig ut. Å se på et resultat er ikke det samme som å måle det.
Ikke skjul de usikre. Et produkt med usikker bildekobling er fortsatt nyttig — hvis den som ser på det vet at det er usikkert. Samme grunn til at vi ikke gir ytelsesgaranti på noe en tredjepart bestemmer.
Produktbeskrivelsene lå heller ikke merket. Men de lå i sitt eget punktstørrelsessjikt: sidetittel størst, ingress under, produktbeskrivelse under der igjen, spesifikasjoner nederst. Bare mellomsjiktet var teksten vi var ute etter, og vi hentet det ut på punktstørrelse alene.
Ikke alle produktene fikk en beskrivelse. Det var riktig — sider som er rene prislister har ingen redaksjonell tekst. Et uttrekk som ga beskrivelse til alle, ville ha funnet på noe.
Nå til det problemet folk faktisk sitter fast i: leverandøren kaller produktet én ting, butikken en annen, og ingen kolonne er lik i begge.
Det første vi gjør er å slå fast hva som er den kanoniske nøkkelen, og det er aldri produktnavnet.
Navn kolliderer. To varianter av samme produkt kan hete nesten det samme og være ulike varer — ulik størrelse, ulik pris, ulikt artikkelnummer. Matcher du på navn, slår du dem sammen, og ingen oppdager det før noen bestiller feil størrelse.
Artikkelnummeret er nøkkelen. Alt annet er hjelpefelt.
Når det ikke finnes et felles artikkelnummer, må du sammenligne noe uskarpt — som regel hvor mye ordene i to produktnavn overlapper. Da må du velge en terskel, og det er en avveining du ikke kommer unna:
Legg merke til den midterste kolonnen i første rad. For løs matching gir et penere resultat enn riktig matching. Derfor er den farlig. En rapport uten «ikke funnet»-rader ser ferdig ut.
Ett enkelt grep demper problemet: sammenlign bare innenfor samme kategori. Et lager skal ikke kunne matche en reim, uansett hvor mange ord som er felles. Kategorien er et gjerde, og gjerder er billigere enn presisjon. Det andre grepet fortjener sitt eget avsnitt.
I sourcing-arbeidet for industrikomponenter starter vi med et delenummer og skal ende opp med å vite hvilket selskap som står bak. Kjeden:
Etter steg fem er matchingen ikke lenger uskarp. Organisasjonsnummeret er et nisifret tall som enten stemmer eller ikke.
Det er hele poenget: du bruker det uskarpe én gang, for å komme frem til noe skarpt — og deretter bruker du bare det skarpe.
Og det avgjørende: dommeren har lov til å svare «ikke funnet». Da settes ingenting, og raden står igjen synlig uten organisasjonsnummer. Et system som alltid velger noe, ville satt feil nummer på hver rad der merket var ukjent — og ingen ville sett det.
Vi har skrevet en egen artikkel om nettopp den mekanismen, og om hva som skjer når den mangler: når AI går i stå og mennesket legger den siste brikken.
Uttrukne produkter havner ikke i katalogen, men i en liste med tre automatisk satte felter:
Ingenting går videre uten at noen har sett på det.
Det er fristende å kalle dette en ufullstendig automatisering. Vi mener det motsatte: køen er produktet. Verdien ligger ikke i at maskinen gjetter det siste steget, men i at tusen sider er redusert til noen titalls rader en fagperson kan gå gjennom på en formiddag.
En innkjøper som ser hundre rader merket «høy» og seks merket «lav», bruker tiden på de seks. Den som får hundreogseks umerkede rader, må sjekke alle eller ingen — i praksis ingen. Samme arbeidsdeling som da vi kartla en produktkatalog på en nettside: maskinen tar volumet, mennesket tar resten.
Sitter dere med en leverandørkatalog dere ikke får ut av? Vi tar en titt på filen og sier hva som skal til. Ta kontakt — det koster ingenting å spørre.
Kampanjeaviser ser lettere ut enn de er. En avisside har som regel bare tre felter per linje: hva varen heter, hvor lenge tilbudet gjelder, og hva den koster. Lite data, og derfor lite å kjenne igjen på.
Varenavnet er markedsføring, ikke identifikator. Det samme produktet heter noe annet i hver avis. Derfor blir matchingen over viktig — og derfor bør et treff mot butikkens eget sortiment bekreftes av en person før prisen brukes til noe.
Tomt er ikke det samme som ingen tilbud. Bommer mønsteret, kommer det ut null rader. Null rader betyr «avisen hadde ingen tilbud» og «jobben virket ikke» på nøyaktig samme måte. Den fellen har vi gått i, og behandler den nå som en egen sjekk: en avisside som gir null treff skal varsles, ikke logges.
Dette ligner mest på vanlig datainnhenting fra nettsider: kilden er ikke laget for deg, den kan endres uten varsel, og det eneste du kontrollerer er hvor tydelig du oppdager at den har endret seg.
Står du foran samme problem, i denne rekkefølgen:
Vi oppgir ikke faste priser på slikt arbeid, fordi kostnaden avgjøres av dokumentet og ikke av oss. Fast mal med tekstlag kan være en dags jobb; samme sidetall skannet, med bilder som skal kobles, er noe helt annet — og forskjellen er ikke synlig fra utsiden. Derfor ber vi om en prøvefil først, og bruker nivåskalaen N1–N5 til vurderingen.
Er dataene først ute, skal de vises et sted: en dynamisk nettside med datakilde bak, eller en WordPress-nettside som tåler innhold utenfra. Å hente og å vise er to jobber.
Fordi tabellen ikke finnes i filen. En pdf-katalog lagrer tekstbokser med koordinater og skrifttype, ikke rader og kolonner. Rekkefølgen du får ved kopiering er den boksene ble lagt inn i — ofte kolonnevis eller tilfeldig. Derfor havner prisen på feil produkt.
Ja, og det er ofte den beste metoden når layouten er uryddig. To forbehold: be om strukturerte felter i stedet for tekst som ser strukturert ut, og regn ut summer og tellinger i egen kode etterpå. Uttrekket bør dessuten få et konfidensnivå per rad og gå til gjennomgang.
Du trenger noe som binder dem sammen. I katalogen vi jobbet med var det små tall trykt på bildene, som gikk igjen som listenumre i prislisten — og skillet mellom tallgruppene lå i fontvekten. Finnes ingen slik kobling, må du falle tilbake på posisjon, og merke resultatet usikkert.
Match aldri på navn alene. Sammenlign ordoverlapp innenfor samme kategori, sett en terskel, og la alt under den gå til manuell gjennomgang. Bruk der det er mulig det uskarpe treffet til å slå opp en hard identifikator — og bruk deretter bare den.
For streng matching gir færre treff, og alle ser det med en gang. For løs matching gir feil treff, og det ser ingen — resultatet er pent, men to ulike varer er slått sammen. Derfor legger vi grensetilfellene i kø framfor å justere terskelen til rapporten ser fin ut.
Det finnes verktøy som gjør det, og på et enkelt dokument med fast mal fungerer de. På en katalog med flere maler gir de et regneark som ser riktig ut og har feil i seg. Problemet er flyttet, ikke løst.
Har dere en katalog, prisliste eller kampanjeavis som skal bli til data? Send oss en prøvefil, så sier vi hva som lar seg gjøre — og hva som ikke gjør det. Ta kontakt, eller les om automatisering og dynamiske nettsider.
Avis-ID-en skifter hver uke. Det er den ene detaljen som avgjør om automatiseringen lever eller dør på mandag.
Det vanligste spørsmålet vi får om datainnhenting — web scraping, som det heter på engelsk — er «hva koster det?».
Vi kartla en norsk produktkatalog for å hente ut varedata. Fire av fem veier var blindveier. Her er hver eneste av dem — og de 19 % vi endte på.
Denne artikkelen hører til datainnhenting. Skal noen gjøre jobben for deg, er det dette vi tilbyr: hjelp til å hente ut data.