I want to get an JSON-output using Cheerio in nodejs that has the following layout:
[
{
teamName: 'M08-ARG',
time: '16:00 - 17:00',
veld: 1CD
}
...
{
teamName: 'J08-SPA',
time: '16:00 - 17:00',
veld: 1A
}
]
(part of) the html of my website is:
<div class="matches_container">
<section class="column matches screen5">
<ul class="jcarousel-skin-tango">
<li class="bg-title-list">
<h3 class="tite-list">Meisjes</h3>
</li>
<li class="position-list">
<div class="teamName">
M08-ARG
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1CD
</div>
</li>
<li class="position-list">
<div class="teamName">
M08-IND
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1B
</div>
</li>
<li class="position-list">
<div class="teamName">
MO8-BEL
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1CD
</div>
</li>
</ul>
</section>
<section class="column matches screen5">
<ul class="jcarousel-skin-tango">
<li class="bg-title-list"></li>
<h3 class="tite-list">Jongens</h3>
</li>
<li class="position-list">
<div class="teamName">
J08-ARG
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1A
</div>
</li>
<li class="position-list">
<div class="teamName">
J08-BEL
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1A
</div>
</li>
<li class="position-list">
<div class="teamName">
J08-SPA
</div>
<time>
16:00 - 17:00
</time>
<div class="text">
<span class="img"> </span>Veld 1A
</div>
</li>
</ul>
</section>
</div>
What I have so far:
console.log("1"); //debug
var result = [];
$("#Left > div > section:nth-child(1) > ul").each(function (el) {
console.log("2"); //debug
var $li = $(el).find("li");
var obj = {
teamName: $li.find("div.teamName").text(),
time: $li.find("time").text(),
veld: $li.find("div.text > span").text(),
};
console.log(obj);
result.push(obj);
});
console.log(result);
I do get logged in the console a '1'. So that far everything is working. However, I should see some numbers 2 as well but there aren't any. I think I go wrong with the selectors. If I use Chrome to inspect the element for eg. teamName, it comes up with #Left > div > section:nth-child(1) > ul > li:nth-child(2) > div.teamName, time comes up with #Left > div > section:nth-child(1) > ul > li:nth-child(2) > time and veld comes up with #Left > div > section:nth-child(1) > ul > li:nth-child(2) > div.text > span
Console output:
Opening the browser...... 1 []
I have used the answer to this question as a reference for my code since the structure looked the same.
It looks like you want to loop over the <li> elements, not the <ul> elements.
const cheerio = require("cheerio"); // ^1.0.0-rc.12
const html = `<HTML as above>`;
const $ = cheerio.load(html);
const result = [...$(".matches_container .position-list")].map(e => ({
teamName: $(e).find(".teamName").text().trim(),
time: $(e).find("time").text().trim(),
veld: $(e).find(".text").text().trim().split(/ +/).pop(),
}));
console.log(result);
If you want to maintain the <ul> groupings in your output, add an extra loop/map over the <ul> elements and loop over <li>s in an inner loop:
const result = [...$(".matches_container .jcarousel-skin-tango")].map(e =>
[...$(e).find(".position-list")].map(e => ({
teamName: $(e).find(".teamName").text().trim(),
time: $(e).find("time").text().trim(),
veld: $(e).find(".text").text().trim().split(/ +/).pop(),
}))
);
I don't see #Left in your shared markup but I assume that's in the actual HTML.
If this fails on the live site, it's probably because JS is injecting the elements dynamically, you're being blocked as a bot, or there's a naming clash on some of these selectors. In that case, feel free to share your URL and I can take a look at it.
Related
I want to be able to click on an item and clone it immediately after that item, At moment it always Clones to the END, id adds it after the last LI in list.
I created a jsFiddle here jsfiddle test
JS
const curId = 20;
function cloneIt(){
var newId = Math.floor((Math.random() * 1000) + 1);
const newCloned = $('#d'+curId).clone(true).prop('id', "d"+newId );
newCloned.html(newCloned.html().replace(
new RegExp(curId, 'g'),
newId
));
$("#ulContainer").append(newCloned);
}
$('#ulContainer').on('click', '.tog', function(e) {
cloneIt();
alert('item cloned');
e.preventDefault();
});
HTML
<ul id='ulContainer'>
<li id="d20" class="cards__item">
<div class="card">
<div class="card__content cellb">
<a id="dup20" class="tog" href="http://test/pgdup/20">
<div class="dup20">clone me</div>
</a>
</div>
<div class="card__content nick">
<p class="card__text nick">Test Tres (20)</p>
</div>
</div>
</li>
<li id="d21" class="cards__item">
<div class="card">
<div class="card__content anchor">
<a id="dup21" class="tog" href="http://test/pgdup/21">
<div class="dup21">clone me</div>
</a>
</div>
<div class="card__content nick">
<p class="card__text nick">Test Tres (21)</p>
</div>
</div>
</li>
</ul>
You can try using .after() by passing the event to the function:
Insert content, specified by the parameter, after each element in the set of matched elements.
Change
$("#ulContainer").append(newCloned);
To
$(e.target.closest('li')).after(newCloned);
const curId = 20;
function cloneIt(e){
var newId = Math.floor((Math.random() * 1000) + 1);
const newCloned = $('#d'+curId).clone(true).prop('id', "d"+newId );
newCloned.html(newCloned.html().replace(
new RegExp(curId, 'g'),newId));
$(e.target.closest('li')).after(newCloned);
}
$('#ulContainer').on('click', '.tog', function(e) {
cloneIt(e);
alert('item cloned');
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul id='ulContainer'>
<li id="d20" class="cards__item">
<div class="card">
<div class="card__content cellb">
<a id="dup20" class="tog" href="http://test/pgdup/20">
<div class="dup20">clone me</div>
</a>
</div>
<div class="card__content nick">
<p class="card__text nick">Test Tres (20)</p>
</div>
</div>
</li>
<li id="d21" class="cards__item">
<div class="card">
<div class="card__content anchor">
<a id="dup21" class="tog" href="http://test/pgdup/21">
<div class="dup21">clone me</div>
</a>
</div>
<div class="card__content nick">
<p class="card__text nick">Test Tres (21)</p>
</div>
</div>
</li>
</ul>
I tra to filter the uk-filter from a button or a checkbox.
I understand that I can create a filter, in the that contains .js-filter. but I want to create a checkbox outside the div. so how can a call or set a posible selecction.
This is what I have.
<div uk-filter=".js-filter" data-id="page#4" id="page#4" class="uk-margin">
<ul class="el-nav uk-margin uk-subnav uk-subnav-pill">
<li class="uk-active" uk-filter-control="">
All
</li>
<li uk-filter-control="[data-tag~='Anual']">
Anual
</li>
<li uk-filter-control="[data-tag~='Temporada']">
Temporada
</li>
</ul>
<div class="js-filter uk-child-width-1-1 uk-child-width-1-2#m uk-grid-match uk-grid" uk-grid="">
<div data-tag="Anual Temporada" class="uk-first-column">
<div class="el-item uk-card uk-card-primary uk-card-small uk-card-body uk-margin-remove-first-child">
<h3 class="el-title uk-card-title uk-margin-remove-top uk-margin-remove-bottom">
Triplex MI 47
</h3>
<a href="">
</a>
<div class="el-content uk-panel uk-margin-top"><p style="column-count: 2; font-family: Montserrat; font-size: 14px;">info</p></div>
</div></div>
<div>
<div class="el-item uk-card uk-card-primary uk-card-small uk-card-body uk-margin-remove-first-child">
<h3 class="el-title uk-card-title uk-margin-remove-top uk-margin-remove-bottom">
Botafoch MI35 </h3>
<a href="">
</a>
<div class="el-content uk-panel uk-margin-top"><p style="column-count: 2; font-family: Montserrat; font-size: 14px;">info</p>
<p style="text-align: justify;">info</p></div>
</div></div>
</div>
</div>
This is the function i call from a onclick
function myFunction() {
var filter = document.querySelector('.js-filter');
var filterBar= document.querySelector('#page#4');
var activeFilter= document.querySelector('div [data-tag:'Anual']');
UIkit.filter( filterBar, {
target: filter,
selActive: activeFilter,
});
}
in other post i see they set a selection of the filter when the page is load and recive a hash, i like to do the same but from a button.
This is the other post UIkit 3's Filter component: Set active filter with URL hash - unable to change active filter with JS
I am working on a html code as shown below. The screen-shot beneath the code belong to the class schedule-action-bar from the code below. (removed ct, at nt from the code at this moment in order to minimize my question)
<div class="schedule-wrapper" id="js-schedule-wrapper" data-timezone="nt"> <!-- It changes to pt, mt, and et -->
<div class="schedule-action-bar" onclick="timezoneCheck()">
<div class="schedule-timezone-filter">
Select your timezone:
<ul id="js-timezone-picker">
<li>
<button id="js-time-pt" class="" data-timezone="pt">PT</button>
</li>
<li>
<button id="js-time-mt" class="" data-timezone="mt">MT</button>
</li>
<li>
<button id="js-time-et" class="" data-timezone="et">ET</button>
</li>
</ul>
</div>
</div>
<!-- want to add this class -->
<!--
<div class="schedule-date-bar">
April 12
</div>
-->
<div class="schedule-show">
<div class="schedule-show-time">
<time datetime="21:00 11-04-2019" data-timezone="pt">21:00</time>
<time datetime="22:00 11-04-2019" data-timezone="mt">22:00</time>
<time datetime="00:00 12-04-2019" data-timezone="et">00:00</time>
</div>
</div>
<!-- .schedule-show -->
</div>
Screenshot:
Problem Statement:
When different timezone is selected from the UI (in the screenshot), the value
of data-timezone changes to pt, mt and et at Line#A. What I want to do now is to add a <div class="schedule-date-bar"> <!-- whatever date belong to the respective timezone --> </div> before the schedule-show class
<script>
function timezoneCheck() {
jQuery(function ($) {
$("#js-schedule-wrapper").click(function () {
if ($('#js-schedule-wrapper').attr('data-timezone') === 'et') {
} else if ($('#js-schedule-wrapper').attr('data-timezone') === 'pt') {
} else if ($('#js-schedule-wrapper').attr('data-timezone') === 'mt') {
}
})
})
}
</script>
This is what I have tried above but more need to be done.
<div class="view-content">
<div class="views-row views-row-1">
<div class="views-field">
<span class="field-content">
<a href="link1">Name for link1
<img src="image1">
</a>
</span>
</div>
<div class="views-field-title">
<span class="field-content">
<a href="link1">
</a>
</span>
</div>
</div>
<div class="views-row views-row-2">
<div class="views-field">
<span class="field-content">
<a href="link2">Name for Link2
<img src="image2">
</a>
</span>
</div>
<div class="views-field-title">
<span class="field-content">
<a href="link2">
</a>
</span>
</div>
</div>
I am using node with request, and cheerio to request the data and scrape accordingly.
I am seeking the href from link1 and link2, I got it to work for one link, but it does not scale out when I try to loop it.
const data ={
link:"div.views-field > span > a"
},
pageData = {};
Object.keys(data).forEach(k => {
pageData[k] = $(data[k]).attr("href");});
console.log(pageData);
Your approach with $(data[k]).attr("href"); is the right idea, but there's no loop here. There should be 2 elements matching this selector but your code only grabs the first.
Changing this to a [...$(data[k])].map(e => $(e).attr("href")) lets you get href attributes from all matching elements.
I'm not crazy about pageData being global and using a forEach when a map seems more appropriate, so here's my suggestion:
const $ = cheerio.load(html);
const data = {
link: "div.views-field > span > a",
};
const pageData = Object.fromEntries(
Object.entries(data).map(([k, v]) =>
[k, [...$(v)].map(e => $(e).attr("href"))]
)
);
console.log(pageData);
I have the following HTML present:
<div id="restautantsdiv">
<ul id="restListings">
<li>
<div class="leftlable">
<h4>Imax Vendor 01</h4>
<p>Madhapur Area new</p>
<p><b>Timings:</b> 09:40 AM - 06:40 PM</p>
<label class="label-green label-wrap" style="display:none;">Close</label>
</div>
<div class="rightlable"><a class="addrest-btn icon-ok-3 addrest-btn-active"></a></div>
</li>
<li>
<div class="leftlable">
<h4>Imax Vendor 02</h4>
<p>Miyapur Area</p>
<p><b>Timings:</b> 01:34 PM - 07:34 PM</p>
<label class="label-green label-wrap" style="display:none;">Close</label>
</div>
<div class="rightlable"><a class="addrest-btn icon-ok-3 addrest-btn-active"></a></div>
</li>
<li>
<div class="leftlable">
<h4>Imax Vendor 04</h4>
<p>Madhapur Area </p>
<p><b>Timings:</b> 09:40 AM - 06:40 PM</p>
<label class="label-green label-wrap" style="display:none;">Close</label>
</div>
<div class="rightlable"><a class="addrest-btn icon-ok-3"></a></div>
</li>
</ul>
</div>
How can i fetch the h4 text which has got the class as addrest-btn-active of the ul tag present under restautantsdiv
Could you please let me know how to fetch this data ??
$('.restListings').find.each(function () {
var labelname = $(this).find("h4").text();
alert(labelname);
});
Please see this is my fiddle
http://jsfiddle.net/hptd3070/
You could use the following:
Example Here
$('#restautantsdiv .addrest-btn-active').closest('#restListings > li').each(function () {
var labelname = $(this).find("h4").text();
alert(labelname);
});
It returns:
"Imax Vendor 01", "Imax Vendor 02"
Start by selecting all .addrest-btn-active elements within #restautantsdiv:
$('#restautantsdiv .addrest-btn-active')
Then find the closest li parent element that is a direct child of #restListings:
.closest('#restListings > li')
From there, it will iterate over each parent li element that contains .addrest-btn-active, and retreieve the h4 text: $(this).find("h4").text().
If you're looking for each of the <h4> tags that are in the same section as a addrest-btn-active, then you can do that like this:
$("#restListings .addrest-btn-active").each(function() {
var text = $(this).closest("li").find("h4").text();
// do something with the text here
});
Demo: http://jsfiddle.net/jfriend00/j8qb124y/