Reputation: 21
Cheerio does not like html without proper tags (who does, really?). I'm trying to scrape some menus and the content i want is between elements in the html. Is there a way to parse each of these and save them as chunks using cheerio? The menu is a 5 day menu with different courses below each day. The element (the days) is at the same level (siblings) to the menu items.
Here's the tricky part: The menu items does not have any attributes. It's a "raw" text node.
I'm not able to change the html.
Here is the html structure, note that the raw text nodes are siblings to the 'headers'.
<div class="meny" style="clear:left;line-height:1.6em;padding-bottom:2em;">
<strong>Måndag</strong>
<br>Klassisk wallenbergare på kalvfärs serveras med gräddsås, lingonsylt, och potatismos
<br>Dragonbrässerad fiskfilé serveras med basilika och ruccolacrème samt kokt potatis
<br>Pasta med strimlad ryggbiff, champinjoner och lök i krämig grönpepparsås
<br>Pasta vegetale med rostad paprika, lök och purjolök i krämig örtsås
<br>Grillad högrevsburgare serveras med ost, bacon, briochebröd och country fries
<br>
<br><strong>Tisdag</strong>
<br>Stekt fläsk med löksås, bruna bönor eller raggmunk och lingon
<br>Thailändsk biffgryta med citrongräs, kokosgrädde, limeblad, wokgrönsaker och rödcurry
<br>Hollandaisebakad torskfilé på purjolöksbädd serveras med vitvinssås och kokt potatis
<br>Pasta penne med bacon, ädelost, champinjoner, blomkål och grädde
<br>Vegetarisk Thaigryta serveras med jasminris
<br>Grillad högrevsburgare serveras med ost, bacon, briochebröd och country fries
<br>
<br><strong>Onsdag</strong>
<br>Kycklingschnitzel serveras med barbequesås och rostad kulpotatis
<br>Honung och enbärsbakad laxfilé serveras med citruscrème och örtslungad potatis
<br>Pasta med strimlad kycklingfilé i rosmarin och citronsås
<br>Karibisk falafelrulle med salsa och vitlöksdressing
<br>Grillad högrevsburgare serveras med ost, bacon, briochebröd och country fries
<br>
<br><strong>Torsdag</strong>
<br>Grillad karréskiva serveras med bearnaisesås och klyftpotatis
<br>Stekt dubbelpanerad fiskfilé serveras med dill och rödlöksröra
<br>Pasta med strimlad fläskfilé, paprika och lök i krämig gorgonzolasås
<br>Grillad högrevsburgare serveras med ost, bacon, briochebröd och country fries
<br>
<br><strong>Fredag</strong>
<br>LÅNGFREDAG STÄNGT</div>
desired json output, the "day" is for i18n...
weekMenu = {
name: "menuname",
weekDayMenus: {
monday: {
day: "",
dayMenu: "",
},
tuesday: {
day: "",
dayMenu: "",
},
wednesday: {
day: "",
dayMenu: "",
},
thursday: {
day: "",
dayMenu: "",
},
friday: {
day: "",
dayMenu: "",
},
}
};
Here's what i tried so far, needless to say, it does not work since it only outputs the headers. It doesn't include the text-node children with contents() since they're siblings and not children.
cheeriojs
var a = $('div.meny')
.clone()
.remove().eq(0) //remove second menu div
.find('strong').eq(0) //point to first 'day'
.siblings() //select the other days
.remove() //remove the other days
.end() //move pointer back to the start
.contents() //get the text nodes associated with the selected 'header'
.filter(function (idx, elem) {
console.log(elem.data)
return elem.type === 'text';
})
.end()
.text()
Upvotes: 1
Views: 845
Reputation: 21
The solution:
///function
let menu = $('div.meny').slice(0, 1).children('strong').map((i, strong) => {
var nodez = getNextSiblings.getAll($(strong)[0])
const dayMenuToReturn = []
for (var i = 0; i < nodez.length; i++) {
if (nodez[i].type === "text") {
dayMenuToReturn.push(nodez[i].data)
}
if (nodez[i].name === "strong") {
break
}
}
return {
day: $(strong).text(),
dayMenu: dayMenuToReturn
}
}).get()
///GetNextSiblings.js
exports.getAll = function (el) {
var siblings = [];
while (el = el.nextSibling) {
siblings.push(el)
}
return siblings;
}
output:
[
{
"day": "Måndag",
"dayMenu": [
"ANNANDAG PÅSKSTÄNGT!!"
]
},
{
"day": "Tisdag",
"dayMenu": [
"Köttfärslimpa serveras med gräddsås, potatismos, lingonsylt och pressgurka",
"Soltorkad tomatbakad sejfilé serveras med basilikasås",
"Pasta chicken thai med kycklingfilé och grönsaker i krämig thaisås",
"Pasta Pesto vegetale med zucchini, röd paprika och rödlök i krämig pestosås",
"Kebabrulle serveras med fefferoni, sriracha och vitlöksdressing"
]
},
{
"day": "Onsdag",
"dayMenu": [
"Italiensk kalvfärsbiff serveras med röd pestosås och basilikarostad kulpotatis",
"Sprödbakad torskfilé serveras med skirat smör och gröna ärtor",
"Pasta carbonara serveras med äggula, riven grand padano",
"Indisk lins och böngryta serveras med basmatiris och mynta yoghurt",
"Kebabrulle serveras med fefferoni, sriracha och vitlöksdressing"
]
},
{
"day": "Torsdag",
"dayMenu": [
"Säsongens sista Ärtsoppa eller krämig hummersoppa serveras med pannkakor, sylt och grädde",
"Kreolsk kycklinggryta serveras med cajunkokt ris och chiliaioli",
"Pocherad torskfilé serveras med purjolöksås och handskalade räkor",
"Pasta penne med strimlad kycklingfilé och grönsaker i mild gorgonzolasås",
"Vegetarisk ärtsoppa serveras med pannkakor, sylt och grädde",
"Kebabrulle serveras med fefferoni, sriracha och vitlöksdressing"
]
},
{
"day": "Fredag",
"dayMenu": [
"Grilltallrik med karréskiva, choritzo, drumstick, bearnaisesås, barbequesås och klyftpotatis",
"Stekt dubbelpanerad fiskfilé serveras med remouladsås och pommes frites",
"Pasta med räkor, kräftstjärtar, fänkål och blekselleri i krämig purjolöksås",
"Ris och quornfärsfylld paprika serveras med yoghurt och tomatsås",
"Kebabrulle serveras med fefferoni, sriracha och vitlöksdressing"
]
}
]
Thx for the help, map is the shit ☻
Upvotes: 1
Reputation: 54984
Here's what I would do:
let menu = $('strong').map((i, strong) => {
return {
name: $(strong).text(),
weekDayMenus: {
monday: {
day: "",
dayMenu: $(strong).next()[0].nextSibling.data.trim(),
},
tuesday: {
day: "",
dayMenu: $(strong).next().next()[0].nextSibling.data.trim(),
},
}
}
}).get()
So the explanation is that next()
gives a cheerio node, so you want next()[0]
which is a javascript node and those have nextSibling (which can be text nodes)
It looks like the last one doesn't have a full menu so you will need to add exception handling.
Upvotes: 0