Skoðanakannanir — RÚV + Vísir + Heimildin Opinion-Poll Aggregators
SkillDev toolsRÚV + Vísir + Heimildin opinion-poll aggregators — all-pollster party support (Alþingi + Reykjavík) and ESB; try maskina first.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Skoðanakannanir — RÚV + Vísir + Heimildin Opinion-Poll Aggregators skill
What this skill tells your AI
The instructions your AI receives, as published by jokull/icelandic-data in .agents/skills/skodanakannanir/SKILL.md and read by ahel’s review.
Three outlets' discovery mechanisms, each surfacing news coverage of opinion
polls from every major Icelandic pollster — not just one firm's own
dashboard. Use this skill for "what's the latest party support / fylgi
flokka" questions, at either national (Alþingi) or Reykjavík city
(borgarstjórn) level — and, via --topic esb, for EU-membership
support/oppose polling ahead of the 2026-08-29 referendum
(þjóðaratkvæðagreiðsla); see "Topic System" below.
Check the maskina skill first. This skill is the
heavy tool: it extracts numbers out of news articles (Playwright for RÚV,
prose/chart parsing, false-positive filtering). Maskína's own skill reads
structured data straight from the pollster — the Tableau fylgi dashboard and
the WordPress article API (fylgi, Borgarviti, ESB/aðild, trust series) — with
no browser and no article scraping. If the question is answerable from
Maskína's own data, answer it there. Reach for this skill for what maskina
cannot give: the other pollsters (Gallup, Prósent, Félagsvísindastofnun),
cross-pollster comparison, Reykjavík polling breadth, or polls that only ever
appeared as one-off news stories.
Use Vísir/Heimildin for discovery, RÚV for numbers. Verified: RÚV's tag
page holds only ~51 recent items with no working pagination (see Caveat 7),
while Vísir's is genuinely paginated back to at least September 2021 and
Heimildin's search goes back to 2019+ for this query. list --source visir --since 2025 --scope reykjavik alone found 40 Reykjavík polls against RÚV's
4 — including the entire Feb–May 2026 city-election polling season RÚV's own
tag had already dropped. RÚV and Vísir are both wired into fetch's
chart/prose number-extraction (see Vísir Extraction below) — Heimildin is
still discovery-only. Vísir is actually the simpler of the two to fetch:
its article bodies are server-rendered, so fetch_visir_article() is plain
httpx, no Playwright, no browser at all (RÚV needs one — see the RÚV
fetch section).
Two-Stage Pipeline
-
list— plain HTTP GET of the tag page, no browser needed. The page is server-rendered:https://www.ruv.is/frettir/tag/skodanakonnunembeds a full__NEXT_DATA__JSON blob containing every listed article (id, title, subtitle, url,first_published_at) as GraphQLArticleobjects. Walk the JSON tree for"__typename": "Article"nodes — do not regex the raw HTML, the JSON is already there and clean. -
fetch <id>— one article's party-support numbers, via Playwright. Article bodies are client-side rendered —httpx/curlsees only the page chrome (nav, footer, ~0 chars of article text); the real content only exists in the DOM after JS hydration. Confirmed by comparing a rawcurlfetch (empty<main>) against a Chrome DevTools MCP snapshot of the same URL (full prose + chart).
Where the Numbers Actually Live
Poll articles that include a chart render it as Highcharts, and each bar
is an SVG <path> with a real aria-label attribute:
<path aria-label="Samfylking, 22.2%." ...>
This is the extraction target — not OCR, not Highcharts internals, not color-matching against Iceland's standard party colors (which the chart also uses consistently, but the aria-labels are simpler and don't require a color lookup table). In Playwright:
bars = await page.eval_on_selector_all(
'path[aria-label*="%"]',
"els => els.map(e => e.getAttribute('aria-label'))",
)
# ["Samfylking, 22.2%.", "Sjálfstæðisflokkur, 19.3%.", ...]
Parse with r"^(.+?),\s*([\d.,]+)\s*%\.?$".
Prose Fallback — When There's No Chart
Verified: articles 451831 and 428434 (both real Reykjavík polls, ~5,900+
chars each) have zero chart <path> elements. The numbers only exist in
prose, and extracting them correctly needs three Icelandic-grammar signals,
not proximity alone — verified against two full real articles end-to-end
(hand-traced, then run through Playwright, then cross-checked against a
manual reading of the rendered page):
- Verb mood distinguishes a current poll figure from a historical
election result.
mælist/mælast/fengi/fengju/stæði/stæðu/stendur(conditional/present — "if the election were held now") mark a poll number;fékk/fengu(simple past) mark a result from an actual past election mentioned for comparison. These are genuinely distinct word forms in Icelandic, not a fuzzy heuristic. - "nú" (now) beats verb-cue proximity when both appear in one sentence.
Historical baselines get phrased too many ways to enumerate as a verb
list —
"fékk þá 19 prósent","tæp tólf í síðasta mánuði","hafa verið stöðugir í tæplega tólf prósent frá kosningum"— but the current number is consistently markednúwhenever a sentence states both. Whennúis present with 2+ percent numbers, it wins outright over the verb-proximity check. - Nearest-party-to-number pairing, not first-party-in-sentence — except
for strict enumerations, which need positional pairing instead.
Comparison sentences ("Sjálfstæðisflokkurinn er langstærstur austan
Elliðaáa með 39%, ... Samfylkingin mælist með 19%") name two parties —
pairing every number in the sentence with whichever party is named first
silently reattributes the second party's number to the first. Distance
must be measured edge-to-edge (
minover the four start/end combinations), not start-to-start — start-to-start systematically penalizes a long party name immediately before the number in favor of a short one further away. But nearest-gap itself breaks on a longer enumeration — verified live user-testing on a real 9-party sentence (visir-20262884529:"...mælist Sjálfstæðisflokkurinn með 31,3% fylgi, Samfylking með 21,5%, Vinstrið í 11,3%, Miðflokkur 10,9%, Viðreisn 9,8%, Sósíalistar með 4,8%, Framsókn 4,7%, Píratar 2,3% og Flokkur fólksins 2,3%...") — because the connector before a number ("... með ", 5 chars) is longer than the separator before the next party name (", ", 2 chars), nearest-gap silently attaches each number to the following party instead of its own: Samfylking's 21.5% landed on Vinstrið, Vinstrið's 11.3% on Miðflokkur, Sósíalistaflokkur's 4.8% on Framsóknarflokkur — and Samfylking + Sósíalistaflokkur vanished from the output entirely (their real numbers stolen by their neighbors). The same silent shift was independently confirmed on a 2-party case (visir-20262884571:"Framsókn mælist með 6,1 prósent og Sósíalistaflokkurinn 4,5 prósent fylgi."— Sósíalistaflokkur had been getting Framsókn's 6.1%, and Framsókn was dropped outright — this exact article was already in the regression set for three prior rounds without anyone noticing). Fix: when a sentence has exactly as many party matches as percent matches (and more than one of each), pair them by strict left-to-right position instead of nearest-gap — verified against both the original two-party motivating case (still pairs identically) and both enumeration bugs above (now pairs correctly). Nearest-gap is kept as the fallback for every case where the counts don't match. - First mention per party wins; later re-mentions are ignored, not merged. A party's topline citywide number is always stated once, early. Later re-mentions in the same article are sub-group breakdowns — verified example: article 451831 restates "Sjálfstæðisflokkurinn ... 39%" in a district-level paragraph ("east of Elliðaá") after already stating the citywide 29% earlier. Overwriting on second mention would silently replace the correct citywide topline with a geographic subset.
extract_prose_poll_figures() implements all four and returns
(results, skipped) — every sentence it declines to use is logged with a
reason (no poll cue, historical, no poll cue, <party> already recorded, later mention ignored, no party in context, unparsed number), printed
via fetch's prose_skipped count and saved in the raw {id}.json. Nothing
is silently dropped or guessed — a skip means "read this one by hand."
Fixed cue gaps (originally documented here as residual gaps, closed since):
- "úr X í Y" (trend framing) —
"Fylgi Sósíalistaflokksins er farið úr 7,8 í 10,4 prósent"("went from X to Y") names no poll-cue verb at all;_TREND_CUE_RErecognizes theúr … íconstruction as an independent cue class rather than requiring_POLL_CUE_REto also match. Verified against article 428434, the case that originally surfaced the gap. - "er með"/"eru með" (present "is/are with") —
"Sjálfstæðisflokkur er með 31,3 prósenta fylgi"doesn't contain any ofmælist/fengi/stenduretc. Found independently in two real articles (round 2 and round 3) before being added, per this skill's rule of never adding a cue on a single example. Added to_POLL_CUE_REas(?:er|eru)\s+með, with the tense-symmetric historical counterpart(?:var|voru)\s+meðadded to_HISTORICAL_CUE_REat the same time (a party's number stated with "var með" is a past/comparison figure, not the current poll result — same present/past distinction asmælistvsfékk). Verified:visir-20262884571went from 5 to 6 extracted parties (Sjálfstæðisflokkur 31.3% newly captured), with no change to any other previously-verified article.
Extending the cue-verb list further is still a real (if small) NLP-scope increase each time — each addition needs 2-3 independent real examples before being added, not a single occurrence.
A conditional-mood gap found and deliberately left unfixed (single
example): "Báðir flokkarnir slyppu naumlega inn á þing með um 5,5 prósenta fylgi" ("both parties would barely make it into parliament with
about 5.5% support") and "Píratar féllu af þingi með 3,4%" ("Pirates fell
out of parliament with 3.4%") — article 427650 — are genuine current-poll
figures (electoral-threshold framing: "would make it in" / "would fall out
of" parliament) that _POLL_CUE_RE doesn't recognize (slyppu/féllu
aren't in the verb list) and _TREND_CUE_RE doesn't cover either (no "úr X
í Y"). Correctly logged as [no poll cue] and skipped rather than guessed —
this is the second cue-verb gap found this way, and per the rule above it's
still only one article's worth of evidence, so it stays a documented gap,
not a fix.
Non-Party-Support Articles — RÚV's Tag Isn't Scoped to Fylgi
RÚV's skoðanakönnun tag catches every public-opinion poll, not just
party-support ones — leader-trust, minister job-approval, and policy
questions ("Hversu ánægð/ur ertu með X?") all get tagged the same way and
share the exact same verb vocabulary (mælist, stendur) as genuine
fylgi-flokka sentences. Found as three independent real false positives
in one round-5 sweep, each a different article entirely about something
other than party support:
- ruv-458088 ("traust" — trust in individual ministers):
"Þeim sem bera lítið traust til hennar fjölgar allnokkuð, úr 15 prósentum í 24 prósent."has a trend cue and no in-sentence party, so it fell back tocurrent_party— stale from an earlier sentence naming "Flokks fólksins" only as a possessive modifier ("Ráðherrar Flokks fólksins eru þeir sem flestir vantreysta", about that party's ministers, not its poll number). Produced a bogusFlokkur fólksins: 24%row. - ruv-453144 ("ánægja" — satisfaction with the taxi market, broken down
by which party each respondent voted for):
"Minnst mælist hún í röðum fylgismanna Vinstri grænna, 61 prósent."and"Mest mælist ánægjan ... meðal Pírata."both have a real poll-cue verb (mælist) and a party name, but the party is a voter-subgroup descriptor ("supporters of X"), not the sentence's own topic. Produced two bogus rows. - ruv-458497 ("staðið sig vel/illa" — job-approval ratings for party
leaders):
"Sigurður Ingi stendur sig litlu betur, 58 prósent segja hann hafa staðið sig illa."—stendur(here the idiom "stendur sig" = "performs", not "flokkurinn stendur í X prósentum") fired as a poll cue with no in-sentence party, inheritingcurrent_partyfrom a"formaður Framsóknarflokksins"mention two sentences earlier.
Two guards, added together, close all three without touching any verified real party-support sentence (checked against the full regression set: 451831, 428434, 467932, 468092, visir-20262884571, visir-20262904348 — none of them use this vocabulary):
_NON_SUPPORT_TOPIC_RE(ánægj\w*|óánægj\w*|traust\w*|vantreyst\w*| staðið\s+sig|fylgismann\w*|kjósend\w*|kusu|kaus) — a sentence whose own topic is satisfaction/trust/approval, or which frames its number as a voter-subgroup breakdown ("fylgismenn/kjósendur/kusu X"), is skipped entirely regardless of which cue verb it contains. Same discipline as_AGGREGATE_RE, different false-attribution shape.- The pre-existing
party = current_partypronoun fallback now also requires an actual poll-cue verb in the sentence (not just a trend cue) — a trend-cue-only sentence with no in-sentence party and no poll verb skips rather than guessing (ruv-458088's exact failure mode; both real verified trend-cue examples, article 428434's"Fylgi Framsóknarflokksins fór úr..."and"Stuðningur við Sósíalistaflokkinn eykst úr...", name their party in-sentence, so this costs nothing against evidence seen so far).
A related, deliberately unexplored generalization: whether an entire
article should be skipped up front (e.g. if its title/subtitle never
mentions fylgi at all) rather than filtering sentence-by-sentence. Not
attempted — the per-sentence guards above already resolve every real
false-positive case found, and an article-level topic classifier is a
bigger, unverified step past what evidence currently supports.
Two more instances of the same class, found live user-testing the skill
(article visir-20262884487, a Vísir "kosningaspá" — election-forecast
model by a named mathematician, Baldur Héðinsson, run on top of the
underlying Maskína/Gallup polls):
- "líkur" (probability/odds of winning a council seat) — a forecast's
native unit, phrased with the exact same cue verbs as a real fylgi
sentence:
"Stefán Pálsson, þriðji maður Vinstrisins er með 53 prósent líkur..."and"...Einar Þorsteinsson mælist með áttatíu prósent líkur..."both matched_POLL_CUE_REwith an in-sentence party name, producing two bogus rows (Vinstrið: 53%,Framsóknarflokkur: 80%) for two individual candidates' seat-probabilities, not their parties' fylgi. - "forskot" (lead/margin between two parties) —
"Flokkurinn er með rúmlega ellefu prósentustiga forskot á Samfylkinguna"("The party has just over an eleven-point lead over Samfylking") attributed the gap between two parties to whichever party happened to be named in the sentence (Samfylking, the trailing party — the leading party was only a pronoun), producing a bogusSamfylking: 11%row (real support that week was closer to 19-20%, per the same day's other Maskína/Gallup coverage). Verified as a recurring headline pattern, not this one article's phrasing — several other real Vísir article titles use the identical"mælist með N prósentustiga forskot á X"construction. Note:"prósentustig"(percentage point) alone was tried first and reverted — verified live to break a real regression article (428434's"Flokkurinn fengi 25,0 prósent, tæplega prósentustigi minna en..."is Samfylking's genuine 25% figure with a harmless comparison clause) —"forskot"is the specific, load-bearing word.
Both added to _NON_SUPPORT_TOPIC_RE alongside the round-5 terms. Checked
against the full regression set plus every other article in the local cache
that mentions "prósentustig" (428434, 429057, visir-20262859852,
visir-20262847801, visir-20262904348) — all unchanged after the fix.
A remaining gap, found the same session and deliberately left unfixed
(single example): "(?:er|eru)\s+með" is generic Icelandic for "to
have," not specific to polls — visir-20262884111 (an income-bracket
breakdown of majority preference, "hversu margir vilja vinstri/hægri
meirihluta eftir tekjum") contains "...þeirra sem eru með 800 til 999 þúsund krónur í tekjur á mánuði, 55 prósent vilja vinstrimeirihluta en 45 prósent hægri" — "eru með" here means "have [an income of]," grammatically
unconnected to the 55%/45% later in the same sentence, but it's still
enough to satisfy the poll-cue check, and with no in-sentence party the
55% fell back to a stale current_party ("Sósíalistaflokkur," set by an
earlier "kjósenda Sósíalistaflokksins" sentence — itself correctly
skipped by the kjósend\w* guard, since it has no percent to attach to).
One occurrence so far, not this session's 2-3-independent-examples bar, and
not needed to answer the request that surfaced it (a different article
covering the same underlying poll already has this poll's real numbers) —
documented rather than guarded.
Article JSON Shape (from __NEXT_DATA__)
{
"__typename": "Article",
"id": 479261,
"title": "Samfylkingin stærst en Sjálfstæðisflokkur vinnur á",
"subtitle": "Samfylkingin mælist með mest fylgi í könnun Maskínu en Sjálfstæðisflokkurinn er tveimur og hálfu prósentustigi á eftir. ...",
"url": "https://nyr.ruv.is/frettir/innlent/2026-06-24-samfylkingin-staerst-en-sjalfstaedisflokkur-vinnur-a-479261/",
"first_published_at": "2026-06-24T08:04:27.617332Z",
"topic": {"category": {"slug": "innlent", "title": "Innlendar fréttir"}}
}
urluses thenyr.ruv.isstaging host in the embedded JSON — swap forwww.ruv.is, both serve the same content but the public site is the documented one.topic.category.slugis alwaysinnlentfor both national and Reykjavík polls — it does not distinguish scope. Guess scope from title/subtitle keywords instead (reykjavík,borgarstjórn,í borginni).tagsisnullin the listing query (per-article tags likemaskína,Skoðanakönnun, party names only appear on the rendered article page, not in this JSON).
Pollster Detection — the "Prósent" Trap
"Prósent" is both a pollster's proper name and the ordinary Icelandic word
for "percent." A case-insensitive substring match on prósent false-fires
on nearly every poll subtitle, because generic phrases like
"...prósentustigi á eftir" contain the substring. The fix verified against
real data: match case-sensitively on the capitalized stem
(\bPrósent\w*\b), since the common noun is lowercase mid-sentence in
practice and the company name is not:
_POLLSTER_RE = re.compile(r"\b(Maskín\w*|Prósent\w*|Gallup\w*|Félagsvísindastofnun\w*)\b")
Maskína/Gallup/Félagsvísindastofnun have no ordinary-word collision and
would work case-insensitively too, but the shared regex is simpler to keep
one way.
Scope Detection
national vs reykjavik, guessed from title+subtitle:
r"reykjav[ií]k|borgarst[jó]órn|í borginni" (case-insensitive). This is a
geography classifier, not a topic classifier — a Reykjavík-scope poll about
airport siting (Reykjavíkurflugvöllur) will be tagged reykjavik even
though it isn't a party-support question. That's expected: the skill scopes
by where, not what.
Vísir Discovery
https://www.visir.is/t/2296/skodanakannanir/{page} — server-rendered HTML
(no JSON blob), <article class="article-item"> cards, genuinely paginated:
verified by fetching pages 1/5/10/20/40 and finding distinct,
chronologically-descending content from July 2026 (page 1) back through
September 2021 (page 20), with page 40 empty (end of history reached).
fetch_visir_article_list() walks pages until an empty page, or — with
--since <year> — until a whole page's articles are all older than the
cutoff (pages are date-descending, so that's a safe stopping point without
walking all ~35+ pages every time).
<article class="article-item ...">
<h2 class="article-item__title"><a href="/g/20262904348d/fylgi-...">Fylgi Sjálfstæðisflokks ekki meira í sex ár</a></h2>
<p class="article-item__text">Sjálfstæðisflokkurinn mælist með 24,9 prósenta fylgi í nýjum þjóðarpúlsi Gallups. ...</p>
<time class="article-item__time">1.7.2026 19:45</time>
</article>
- The listing subtitle (
article-item__text) often states the headline number directly — unlike RÚV's subtitles, which are usually pure prose summary. For a quick "what's the latest" answer,list's output alone may already be enough; no need to visit the article page. - Titles/subtitles carry HTML entities (
ö= ö) and literal soft hyphens (\xad, rendered as) mid-word from the source markup — both must be stripped/unescaped (html.unescape+.replace("\xad", "")) or matching against_PARTY_RE/_POLLSTER_REsilently fails on words that are visually identical but byte-different. - Dates are Icelandic
D.M.YYYY HH:MM("1.7.2026 19:45"), converted to ISO 8601 by_visir_date_to_iso(). - IDs are the numeric prefix of the
/g/<id>d/<slug>URL path, stored asvisir-<id>(RÚV ids are stored asruv-<id>for the same reason — disambiguating which source's numbering a bare id belongs to once both are combined in one cache file). - Recurring feature to know about: "Kosningaspá Vísis" (Vísir's own
election-forecast/projection series) — not a raw poll report, an
aggregated model. Shows up correctly under this skill's scope/pollster
guessing as
pollster: null(no known-pollster name in the text) since it isn't a single firm's poll. - The same underlying poll is frequently reported by both RÚV and
Vísir/Heimildin.
cross_reference_duplicates()(run automatically when--source all) flags this: a Vísir or Heimildin article getsduplicate_of: "<ruv-id>"and the matched RÚV article getsalso_reported_by: [{source, id, url}, ...]when — and only when — all three hold: same non-null pollster (exact string match), same scope, and published within 48 hours of each other. RÚV is always the anchor side of the match (it's the only source with number-extraction wired up — see Extraction Status below).list's printed view and its distinct-article count exclude anything withduplicate_ofset; the savedarticles.jsonkeeps every row either way, so nothing is lost, just marked. Verified match (2026-03-24): RÚV's "Samfylkingin dalar enn" and Vísir's "Fylgi Samfylkingar ekki verið minna í eitt ár", ~15 hours apart, both Maskína, both national — genuinely the same poll release covered two ways. When there's more than one same-pollster/same-scope candidate in the window, nothing is merged — logged asambiguous, N candidatesinstead. This is common and expected, not a bug: Vísir regularly runs a follow-up angle piece on the same poll a day or two after the first report (verified across RÚV+Vísir+Heimildin combined, 2024–2026: 7 ambiguous cases, each with 2-5 genuinely distinct stories about one poll release). Picking the "closest in time" candidate would be a guess dressed up as a match — left for manual reconciliation instead.
Vísir Extraction
fetch_visir_article(url) — plain httpx, no browser. Verified against a
real article (visir-20262904348): the full prose, including the methodology
paragraph, is present in a curl-only fetch. No Highcharts/aria-label
chart was found on that article either (the chart check still runs first,
in case some Vísir articles do embed one) — Vísir's house style leans on
thorough prose instead, and extract_prose_poll_figures() (the exact same
function RÚV's prose fallback uses — this is Icelandic-grammar logic, not
RÚV-specific) is the primary path here, not a fallback.
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 53
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
skodanakannanir- Source
- github.com/jokull/icelandic-data