Comandi e accessi — come sono stati raccolti i materiali

Registro operativo aggiornato al 1 agosto 2026. Serve a rifare gli stessi passaggi fra un mese senza ricominciare da capo, e soprattutto a ricordare dove l’accesso si rompe e come si aggira.

Le tre barriere incontrate, e cosa funziona

Fonte Cosa succede con curl Cosa funziona
www.senato.it HTTP 202 con challenge JavaScript («we need to verify that you’re not a robot») browser vero: agent-browser --headed con DISPLAY verso X410
www.camera.it, documenti.camera.it risponde normalmente curl con user-agent da browser
www.garanteprivacy.it risponde normalmente curl, oppure Brave Search API per trovare il doc-web giusto

La CLI italianparliament

Il repertorio dei dati parlamentari: https://github.com/ondata/italianparliament-mcp

Commissioni della Camera, per ricavare l’URI dell’organo:

italianparliament committees list --chamber camera --legislature 19 | grep -iE "affari costituzionali|giustizia"
# I  Affari costituzionali → http://dati.camera.it/ocd/organo.rdf/o19_3501
# II Giustizia            → http://dati.camera.it/ocd/organo.rdf/o19_3502

Componenti di una commissione, con ruolo e date:

italianparliament committee-members list \
  --committee-uri http://dati.camera.it/ocd/organo.rdf/o19_3501 \
  --chamber camera --legislature 19 --limit 200 --format csv > camera-I-membri.csv

Gruppi di appartenenza e anagrafica con circoscrizione di elezione:

italianparliament group-members list --legislature 19 --limit 900 --format csv > camera-gruppi.csv
italianparliament deputies list      --legislature 19 --limit 900 --format csv > camera-deputati.csv

Atti del Governo al Senato:

italianparliament documents list --legislature 19 --type 'Atto del Governo' --limit 200

Sedute di commissione. L’endpoint SPARQL del Senato ha restituito un 403 intorno alle 18 del 1 agosto e ha ripreso a rispondere poco dopo, senza che cambiasse nulla nella richiesta: è un errore transitorio, la reazione giusta è riprovare, non cercare un’altra strada. Verificato funzionante alle 19:30 dello stesso giorno, con le sedute aggiornate al 30 luglio.

italianparliament committee-sessions list --committee-uri http://dati.senato.it/commissione/0-1 \
  --chamber senato --date-from 2026-07-25 --date-to 2026-08-01

Limiti noti del LOD, verificati: il relatore di un Atto del Governo non c’è, né alla Camera né al Senato — va letto dal resoconto o dalla scheda dell’atto. E le email dei deputati non esistono: la Camera non le pubblica né nel LOD né nella scheda personale (verificato anche aprendo la scheda con un browser). Il Senato invece pubblica nome.cognome@senato.it nella scheda del senatore.

Il join con DuckDB

L’elenco destinatari-camera-ag418.csv nasce così. Nota all_varchar=true: senza, le colonne di date vuote fanno fallire la conversione a INT64.

duckdb -c "
COPY (
WITH m AS (
  SELECT member_uri, member_name, role, 'I Affari costituzionali' AS commissione
  FROM read_csv_auto('camera-I-membri.csv', header=true, all_varchar=true) WHERE end_date IS NULL
  UNION ALL
  SELECT member_uri, member_name, role, 'II Giustizia'
  FROM read_csv_auto('camera-II-membri.csv', header=true, all_varchar=true) WHERE end_date IS NULL
),
g AS (SELECT deputy_uri, group_label,
             row_number() OVER (PARTITION BY deputy_uri ORDER BY start_date DESC) rn
      FROM read_csv_auto('camera-gruppi.csv', header=true, all_varchar=true) WHERE end_date IS NULL),
d AS (SELECT uri, election_label, html_url
      FROM read_csv_auto('camera-deputati.csv', header=true, all_varchar=true))
SELECT m.commissione, m.member_name AS deputato, m.role AS ruolo_commissione,
       g.group_label AS gruppo,
       regexp_extract(d.election_label, 'circoscrizione ([A-ZÀ-Ù0-9 ''\-]+?) nelle', 1) AS circoscrizione,
       d.html_url AS scheda
FROM m LEFT JOIN g ON g.deputy_uri = m.member_uri AND g.rn = 1
       LEFT JOIN d ON d.uri = m.member_uri
) TO 'destinatari-camera-ag418.csv' (HEADER, DELIMITER ',');"

Il Senato dietro il WAF: browser headed via X410

Playbook completo del display: /mnt/c/varie/msi/wsl/display-x410.md. In sintesi, in rete NAT DISPLAY deve puntare al gateway Windows, non a 127.0.0.1.

GW=$(ip route | grep default | awk '{print $3; exit}')
for ip in 127.0.0.1 "$GW"; do
  timeout 2 bash -c "echo > /dev/tcp/$ip/6000" 2>/dev/null && { export DISPLAY=$ip:0.0; break; }
done
echo "$DISPLAY"   # es. 172.19.0.1:0.0

agent-browser close --all
agent-browser open "https://www.senato.it/leggi-e-documenti/attivita-non-legislative/procedure?documentoId=56098" \
  --headed --profile ~/.agent-browser-profiles/senato \
  --args "--enable-unsafe-swiftshader,--ignore-gpu-blocklist"
agent-browser read > pagina.txt

close --all va sempre per primo: se il daemon è già vivo, --headed e --profile vengono ignorati e riparte headless.

Per estrarre i link dei resoconti dalla pagina di trattazione:

agent-browser eval "Array.from(document.querySelectorAll('a')) \
  .filter(a => /Resoconto/i.test(a.textContent)) \
  .map(a => a.textContent.trim() + ' || ' + a.href).join('\n')"

I resoconti hanno una struttura a parti: la pagina base mostra solo l’indice, il testo sta in &part=doc_dc e gli allegati — dove si trova il testo del parere approvato — in &part=doc_dc-allegato_a.

https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0                        # indice
https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0&part=doc_dc            # resoconto
https://www.senato.it/show-doc?leg=19&tipodoc=SommComm&id=1518018&idoggetto=0&part=doc_dc-allegato_a # parere

La Camera con curl

La scheda di un Atto del Governo alla Camera ha lo stesso numero del Senato e contiene assegnazioni, termini, relatori, sedute, audizioni e memorie depositate:

curl -sL -A "Mozilla/5.0" "https://www.camera.it/leg19/682?atto=418&idLegislatura=19&tab=&tipoAtto=atto" -o cam418.html

Estrazione dei link ai PDF delle memorie:

python3 -c "
import re, html
t = open('cam418.html', encoding='utf-8', errors='ignore').read()
for m in re.finditer(r'<a[^>]*href=\"([^\"]+)\"[^>]*>(.*?)</a>', t, re.S):
    txt = html.unescape(re.sub(r'<[^>]+>', '', m.group(2))).strip()
    if 'Memoria' in txt: print(txt, '||', m.group(1))
"

Estrazione del testo dai PDF

liteparse parse memoria.pdf --format markdown --image-mode off -o memoria.md

In alternativa kreuzberg extract --content-format markdown <file>, e markitdown come ripiego.

Ricerca web

Brave, con la chiave già in ambiente. È servita per trovare il numero corretto del provvedimento del Garante, che sul sito non è indicizzato in modo evidente:

curl -s -H "X-Subscription-Token: $BRAVE_API_KEY" -H "Accept: application/json" \
  "https://api.search.brave.com/res/v1/web/search?q=<query urlencoded>&count=10&country=it&search_lang=it" \
  | python3 -c "import sys,json;[print('-',r['title'],'\n ',r['url']) for r in json.load(sys.stdin)['web']['results']]"

Exa via MCP funziona ma restituisce risposte molto grandi, che vanno lette da file invece che a schermo.

NotebookLM da riga di comando

Il notebook di lavoro è «Protezione dei dati personali in ambito penale: Direttiva 2016/680», id 2d0fadcc-63ba-4ae4-ba16-cb6dca965dda.

export DISPLAY=172.19.0.1:0.0        # il login apre un browser: senza X410 fallisce
notebooklm login                     # va lanciato in un terminale interattivo: chiede INVIO
notebooklm auth check --test --json  # deve dare status ok E token_fetch true

notebooklm list --json
notebooklm source list -n <notebook-id> --json
notebooklm source add ./file.pdf -n <notebook-id> --json
notebooklm source delete <source-id> -n <notebook-id> -y
notebooklm ask "domanda" -n <notebook-id> -s <source-id> -s <source-id>

Tre cose imparate a spese nostre:

Alert di calendario

Le scadenze del procedimento — parere della Camera, adozione definitiva entro il 10 ottobre 2026 — si annotano con gwsb sul calendario gmail.com, evento a orario, fuso Europe/Rome.