diff --git a/courses/RascalAmendmentProposals/RAP17/RAP17.md b/courses/RascalAmendmentProposals/RAP17/RAP17.md new file mode 100644 index 000000000..f2d44d812 --- /dev/null +++ b/courses/RascalAmendmentProposals/RAP17/RAP17.md @@ -0,0 +1,31 @@ +--- +title: RAP 17 - Drop symbol sequence syntax and semantics +sidebar_position: 17 +--- + +| RAP | 16 | +| :---- | :---- | +| Title | Drop sequence symbols | +| Author | Jurgen Vinju | +| Status | Draft | +| Type | Language Design | + +## Abstract + +The sequence symbol in syntax definitions, `(A B)` defines a nameless non-terminal which parses and `A` followed by a `B`. In context-free syntax contexts a lalayout node is added. An arbitrary amount of elements is allowed. Sequence is one of "regular" symbols next to lists, separated lists, alternatives and optionals. + +The proposal is to remove this feature entirely from Rascal. + +## Motivation + +* For technical reasons in the parser the sequence non-terminal requires extensive special cases in multiple places in the implementation of Rascal. It is therefore still not fully supported in concrete syntax fragments. It does not work as an outermost type and it does not work as a spliced variable in a concrete pattern or a concrete expression. It also does not work as the toplevel type to the `parse` function. +It can be made to work but at the cost of weird special cases in several places. While the other regular expressions have been dealt with in a natural way, this is not possible for the sequence symbol. +* Nested sequences in a grammar do not contribute to the readability of a grammar. +* Nested sequences in a grammar make downstream processing in Rascal harder. +* A simple production rule for the sequence: `syntax MySequence = A B;` solves the problem. + +## Compatibility + +* Sequence is not used by the bootstrap sequence because it does not occur in the Rascal grammar for Rascal +* None of our examples use sequence +* Removing sequence will produce a parse error in the grammars that use it. diff --git a/courses/Recipes/Languages/HTML/HTML.md b/courses/Recipes/Languages/HTML/HTML.md new file mode 100644 index 000000000..d84f68c84 --- /dev/null +++ b/courses/Recipes/Languages/HTML/HTML.md @@ -0,0 +1,14 @@ +--- +title: HTML +details: + - Scraping + +--- + +#### Synopsis + +Tools for HTML Processing + +#### Description + +(((TOC))) diff --git a/courses/Recipes/Languages/HTML/Scraping/Scraping.md b/courses/Recipes/Languages/HTML/Scraping/Scraping.md new file mode 100644 index 000000000..9a2ae98f3 --- /dev/null +++ b/courses/Recipes/Languages/HTML/Scraping/Scraping.md @@ -0,0 +1,171 @@ +--- +title: HTML Scraping +keywords: + - scraping + - "pattern matching" + - recursion + - html +--- + +#### Synopsis + +Scraping HTML is to recover raw data from HTML documents + +#### Description + +In this example we see HTML as just another language that may contain relevant information. In the case +of HTML it is smart to reuse existing parsers, so we use an ((AbstractSyntax)) format for HTML. It is +described in ((lang::html::AST)) and its IO interface is described in ((lang::html::IO)). + +In this demo we extract information from the website of the [Centraal Bureau voor Statistiek (CBS)](https://www.cbs.nl), the Dutch national centre for statistics. + +We found an interesting [page](https://longreads.cbs.nl/nederland-in-cijfers-2022/hoeveel-fietsen-we-gemiddeld-per-week/) that lists how much biking the Dutchies do on average weekly: + +```rascal-shell,errors +import lang::html::IO; +import IO; + +// we use a local copy instead of the live content for stability reasons +htmlExample = getResource("Languages/HTML/Scraping/fietsen.html"); +page = readHTMLFile(htmlExample); +``` + +As you can see the output is truncated with `...`, to see more we can use ((IO-iprintln)): + +```rascal-shell,continue +iprintln(page) +``` + +We used Chrome's "Inspect" feature to figure out that the div class `datatable-container` is of interest. +So let's select that using a deep match operator and bind that div to the `tab` variable: + +```rascal-shell,continue +if (/tab:div(_,class=/datatable-container/) := page) + iprintln(tab); +``` + +We used a deep match pattern and then a regular expression pattern to select all `class` attributes that have `datatable-container` somewhere in the string. + +Every row in the table contains data, except the header row. Let's convert this entire table +to a relation of type `rel[str persoonskenmerken, real fietskilometers]`. + +We create the match pattern by step-wise refinement. First let's just +list all the rows: + +```rascal-shell,continue +if (/tab:div(rows,class=/datatable-container/) := page) { // <1> + for (/r:tr(_) := rows) { // <2> + println(r); + } +} +``` + +* <1> binds the children of the div to `rows`; +* <2> uses deep match `/` to quickly jump to all the nested `tr` nodes; + +Now we refined the pattern to filter out the non-header rows: + +```rascal-shell,continue +if (/tab:div(rows,class=/datatable-container/) := page) { + for (/r:tr([text(_),th(_,scope="row"), text(_), td(_), text(_)]) := rows) { // <3> + println(r); + } +} +``` + +* <3> we matching only those `tr` that have two children, one `th` and one `td`. To be sure we also limit the first `th` to have the `scope` attribute equal to `"row"`. + +Now it's time to get the final data out. The category is in the first column and the numbers are in the second. +We could make the query deeper and more complex, but we choose to add another nesting level for the sake of clarity: + +```rascal-shell,continue +if (/tab:div(rows,class=/datatable-container/) := page) { + for (/r:tr([text(_),category:th(_,scope="row"), text(_), number:td(_), text(_)]) := rows) { + if (/text(str c) := category, /text(str n) := number) { + println(" --- "); + } + } +} +``` + +Now we have scraped the data out of the HTML syntax tree, we have to convert it to +raw data. But the Dutch use comma's as decimal separators: + +```rascal-shell,continue,error +import String; +import util::Math; +toReal("18,79"); +toReal("18.79"); +replaceAll("18,79", ",", ".") +``` + +```rascal-shell,continue +rel[str persoonskenmerken, real fietskilometers] myData = {}; +if (/tab:div(rows,class=/datatable-container/) := page) { + for (/r:tr([text(_),category:th(_,scope="row"), text(_), number:td(_), text(_)]) := rows) { + if (/text(str c) := category, /text(str n) := number) { + println(" --- "); + myData += ; + } + } +} +myData; +``` + +Now we have the data in a format that we can compute with: +```rascal-shell,continue +myData +import Set; +theSum = sum(myData); +relativeData = { | <- myData}; +``` + +To keep this analysis for the future, for example when new data is published on the site, we +can store the query in a function. It is also ready to be rewritten from structured programming +style into a functional comprehension. Let's do that first: + +```rascal-shell,continue +{ +| /tab:div(rows,class=/datatable-container/) := page +, /r:tr([text(_),category:th(_,scope="row"), text(_), number:td(_), text(_)]) := rows +, /text(str c) := category, /text(str n) := number +} +``` + +The patterns have _not_ changed, only they have been copied to the generator/filter side +of a ((Set-Comprehension)): +* <1> here we have the resulting tuple that uses `c` and `n` which have been selected by pattern matching +* <2> this is the first selector that finds the table in the page +* <3> here we iterate over the rows that are not the header +* <4> finally we project out the text from the two cells. + +Now we wrap it all up in a reusable function: +```rascal-shell,continue +rel[str persoonskenmerken, real fietskilometers] scrapeFietsKilometers(loc address=|https://longreads.cbs.nl/nederland-in-cijfers-2022/hoeveel-fietsen-we-gemiddeld-per-week/|) + = { + | /tab:div(rows,class=/datatable-container/) := readHTMLFile(address) + , /r:tr([text(_),category:th(_,scope="row"), text(_), number:td(_), text(_)]) := rows + , /text(str c) := category, /text(str n) := number + }; +scrapeFietsKilometers() +``` + +Every time the function is called, the HTML is retrieved again from the site. We coded the +URL in a default parameter, just in case a similar page exists that we might try our analysis +on. + +#### Benefits + +* Rascal has a lot of powerful ((PatternMatching)) operators to dissect a HTML page with; +* Skills used in the analysis of programming languages, like traversal and pattern matching, are equally useful for HTML scraping; +* Deep matching and ((Statements-Visit)) skip over all uninteresting content without depending on it. The more you use these "structure shy" primitives, the more robust the scraper will be against sudden changes in the HTML. + +#### Pitfalls + +* HTML scraping is a *brittle* business. If the page changes, then it's likely the query will not work +anymore. The function will start returning empty sets of tuples in that case, most likely. If we look at the +structural dependencies then the word `datatable-container` is very important. Also this query matches only +tables with two columns, and the first cell is always a `th` and the second cell is `td`. Finally the actual +data is stored in a single text cell under the `th` and `td`. If any of these properties change, this scraper +breaks. However, if _anything else_ changes, the scraper keeps working; +* The HTML parser skips SVG elements; \ No newline at end of file diff --git a/courses/Recipes/Languages/HTML/Scraping/fietsen.html b/courses/Recipes/Languages/HTML/Scraping/fietsen.html new file mode 100644 index 000000000..96eb05815 --- /dev/null +++ b/courses/Recipes/Languages/HTML/Scraping/fietsen.html @@ -0,0 +1,625 @@ + + + + + + + + Hoeveel fietsen we gemiddeld per week? - Nederland in cijfers 2022 | CBS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ +
+ Editie 2021Editie 2022
+ +

Foto omschrijving: Senioren op de elektriche fiets, e-bike op een groene dijk met een blauwe lucht en veel witte wolken.

+ +
+ +
+
+
+
+ +
+ +
+
+ + + +
+ + +
+ + + + + +
+
+
+
+
+
+

Hoeveel fietsen we gemiddeld per week?

+

In 2020 zaten 12- tot 18‑jarigen het vaakst op de fiets of e-bike. Zij legden ook + de meeste kilometers af: gemiddeld 33 kilometer per persoon per week. 75‑plussers + stapten het minst vaak op de fiets. Zij fietsten in een gemiddelde week 14 kilometer, + evenveel als 25- tot 35‑jarigen.

+
+ + Hoeveel fietsen we gemiddeld per week?How much do we cycle per week on average? 6 tot 12 jaar6 to 11 yrs15,2 km12 tot 18 jaar12 to 17 yrs32,9 km18 tot 25 jaar18 to 24 yrs16,8 km25 tot 35 jaar25 to 34 yrs14,0 km35 tot 50 jaar35 to 49 yrs15,5 km50 tot 65 jaar50 to 64 yrs19,1 km65 tot 75 jaar65 to 74 yrs23,4 km75 yrs and over13,7 km
+

In 2020, een jaar waarin overheidsmaatregelen golden in verband met de coronapandemie, + fietsten mensen minder vaak en legden zij gemiddeld per week ook minder kilometers + af dan in 2019. De uitzondering hierop waren 75‑plussers, zij fietsten beide jaren + even vaak en legden dezelfde afstand af. +

+

Gemiddeld 4,2 keer per week op de fiets

+

In een gemiddelde week in 2020 maakten inwoners van Nederland van zes jaar of ouder + 4,4 fietsverplaatsingen, bijna 1 minder dan in 2019. Ook 12- tot 18‑jarigen fietsten in 2020 + minder vaak, maar met 8,4 keer nog altijd bijna twee maal zo vaak als de gemiddelde + Nederlander. Ze legden met 33 kilometer per persoon per week ook de meeste kilometers + af. Ook 6- tot 12‑jarigen zaten, ondanks dat ze minder vaak en minder ver fietsten + dan in 2019, bijna 7 keer per week op de fiets en reden ruim 15 kilometer per week. +

+

65- tot 75‑jarigen fietsten minder vaak dan jeugdigen, maar als zij op pad gingen + legden ze relatief veel kilometers af, rond 24 kilometer per week in 2020. Dat is + evenveel als in 2019. Mensen van 75 jaar of ouder fietsten met 14 kilometer per week + de minste kilometers. In 2020 fietsten ook 25- tot 35‑jarigen 14 kilometer per week. + In 2019 was dat nog bijna 18 kilometer per week. +

+

Studenten en scholieren fietsen het meest

+

Studenten en scholieren zaten het vaakst op de fiets en trapten ook de meeste kilometers. + In 2020 zaten ze per week bijna 7 keer op de fiets en fietsten in een week ruim 22 kilometer + per persoon. In 2019 was dat 9 keer en 29 kilometer. Gepensioneerden zaten minder + vaak op de fiets, maar als zij wel fietsten legden ze een grotere afstand af. In totaal + fietsten gepensioneerden gemiddeld 21 kilometer per persoon per week, zowel in 2020 + als het jaar ervoor. +

+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Gemiddelde fietsafstand per persoon per week, 2020 (kilometer)
PersoonskenmerkenFietskilometers
Student of scholier22,45
Gepensioneerd of VUT20,69
Werkzaam, 12 tot 30 uur per week18,79
Werkzaam, 30 uur per week of meer16,69
Werkloos14,64
Arbeidsongeschikt11,77
Anders13,14
+
+
+
+

Vooral recreatieve fietskilometers

+

In 2020 werden de meeste fietsverplaatsingen gemaakt om boodschappen te doen of te winkelen. + De meeste fietskilometers werden echter voor recreatieve doeleinden afgelegd, zoals + voor toertochten, wandelen en voor uitgaan, sport en hobby. Het gemiddelde van 4,7 kilometer + per persoon per week voor het motief toeren en wandelen was bijna een derde hoger + dan in 2019. Voor uitgaan, sport en hobby werden in 2020 bijna een vijfde minder kilometers + gefietst dan in 2019 en om van en naar het werk te gaan een derde minder. +

+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Gemiddelde fietsafstand per week naar reismotief, 2020 (kilometer)
ReismotiefAfstand
Toeren, wandelen4,67
Uitgaan, sport, hobby3,60
Van en naar het werk3,07
Winkelen,
boodschappen doen
2,18
Onderwijs,
cursus,
kinderopvang
1,85
Visite, logeren1,33
Overige reismotieven0,99
Diensten,
persoonlijke
verzorging
0,33
Zakelijk, beroepsmatig0,23
+
+
+
+
+ + + + +

De vragen

+ + + +
+
+
+
+
+
+ + + + +
+
+
+ + +
+
+
+

Colofon

+

Deze website is ontwikkeld door het CBS in samenwerking met Textcetera Den Haag.
+ Heb je een vraag of opmerking over deze website, neem dan contact op met het CBS. +

+

Disclaimer en copyright

+

Cookies

+

CBS maakt op deze website gebruik van functionele cookies om de site goed te laten + werken. Deze cookies bevatten geen persoonsgegevens en hebben nauwelijks gevolgen + voor de privacy. Daarnaast gebruiken wij ook analytische cookies om bezoekersstatistieken + bij te houden. Bijvoorbeeld hoe vaak pagina's worden bezocht, welke onderwerpen gebruikers + naar op zoek zijn en hoe bezoekers op onze site komen. Het doel hiervan is om inzicht + te krijgen in het functioneren van de website om zo de gebruikerservaring voor u te + kunnen verbeteren. De herleidbaarheid van bezoekers aan onze website beperken wij + zo veel mogelijk door de laatste cijfergroep (octet) van ieder IP-adres te anonimiseren. + Deze gegevens worden niet gedeeld met andere partijen. CBS gebruikt geen trackingcookies. + Trackingcookies zijn cookies die bezoekers tijdens het surfen over andere websites + kunnen volgen. +

+

De geplaatste functionele en analytische cookies maken geen of weinig inbreuk op uw + privacy. Volgens de regels mogen deze zonder toestemming geplaatst worden. +

+

Meer informatie: https://www.rijksoverheid.nl/onderwerpen/telecommunicatie/vraag-en-antwoord/mag-een-website-ongevraagd-cookies-plaatsen

+
+

Leeswijzer

+

Verklaring van tekens

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
niets (blanco)een cijfer kan op logische gronden niet voorkomen
.het cijfer is onbekend, onvoldoende betrouwbaar of geheim
0 (0,0)het cijfer is kleiner dan de helft van de gekozen eenheid
* voorlopige cijfers
** nader voorlopige cijfers
-(indien voorkomend tussen twee getallen) tot en met
2016–20172016 tot en met 2017
2016/2017het gemiddelde over de jaren 2016 tot en met 2017
2016/’17oogstjaar, boekjaar, schooljaar, enz. beginnend in 2016 en eindigend in 2017
2004/’05-2016/’17oogstjaar enz., 2004/’05 tot en met 2016/’17
+

In geval van afronding kan het voorkomen dat het weergegeven totaal niet overeenstemt + met de som van de getallen. +

+
+

Over het CBS

+

De wettelijke taak van het Centraal Bureau voor de Statistiek (CBS) is om officiële + statistieken te maken en de uitkomsten daarvan openbaar te maken. Het CBS publiceert + betrouwbare en samenhangende statistische informatie, die het deelt met andere overheden, + burgers, politiek, wetenschap, media en bedrijfsleven. Zo zorgt het CBS ervoor dat + maatschappelijke debatten gevoerd kunnen worden op basis van betrouwbare statistische + informatie. +

+

Het CBS maakt inzichtelijk wat er feitelijk gebeurt. De informatie die het CBS publiceert, + gaat daarom over onderwerpen die de mensen in Nederland raken. Bijvoorbeeld economische + groei en consumentenprijzen, maar ook criminaliteit en vrije tijd. +

+

Naast de verantwoordelijkheid voor de nationale (officiële) statistieken is het CBS + ook belast met de productie van Europese (communautaire) statistieken. Dit betreft + het grootste deel van het werkprogramma. +

+

Voor meer informatie over de taken, organisatie en publicaties van het CBS, zie cbs.nl. +

+
+

Contact

+

Met vragen kunt u contact opnemen met het CBS. +

+
+

Medewerkers

+

Concept & beeldredactie

+

Irene van Kuik

+

Infographics

+

Hendrik Zuidhoek

+

Janneke Hendriks

+

Richard Jollie

+

Redactie

+

Gert Jan Wijma

+

Karolien van Wijk

+

Michel van Kooten

+

Paul de Winden

+

Ronald van der Bie

+

Sidney Vergouw

+

Vertaling

+

Frans Dinnissen

+

Gabriëlle de Vet

+

Eindredactie

+

Elma Wobma

+

Veel dank aan alle andere CBS’ers die hebben bijgedragen aan deze editie.

+
+

Erratum

+

Ondanks de zorgvuldigheid waarmee deze publicatie is samengesteld, zijn er achteraf + enkele onvolkomenheden geconstateerd. Onze excuses hiervoor. +

+

Datum: 29 september 2022

+

In het figuur ‘Opgesteld vermogen en elektricititeitsproductie van windmolens’ waren de eenheden van het bovenste + en onderste deel van de figuur verwisseld (kwH en megawatt). Dit is nu gecorrigeerd. +

+

Datum: 21 oktober 2022

+

In het figuur ‘Gemiddelde fietsafstand naar reismotief’ waren de getallen verwisseld. Dit is nu gecorrigeerd. + De cijfers in de tekst waren juist. +

+
+ +
+
+ + + + + + + + + + + + + + + + \ No newline at end of file