Adding dynamically to a JSON file - javascript

I have a json file that is generated dynamically by taking the values of a page using a crawler, json is created as follows:
{
"temperatura":"31°C",
"sensacao":"RealFeel® 36°",
"chuva":"0 mm",
"vento":"NNO11km/h",
"momentoAtualizacao":"Dia",
"Cidade":"carazinho",
"Site":"Accuweather"
}
{
"temperatura":"29 º",
"sensacao":"29º ST",
"vento":"11 Km/h",
"umidade":"51% UR",
"pressao":"1013 hPa",
"Cidade":"carazinho",
"Site":"Tempo Agora"
}
The problem with this generated file is missing [] to join all the files inside an array, and commas to separate the files.
The final json should look like this:
[{
"temperatura":"31°C",
"sensacao":"RealFeel® 36°",
"chuva":"0 mm",
"vento":"NNO11km/h",
"momentoAtualizacao":"Dia",
"Cidade":"carazinho",
"Site":"Accuweather"
},
{
"temperatura":"29 º",
"sensacao":"29º ST",
"vento":"11 Km/h",
"umidade":"51% UR",
"pressao":"1013 hPa",
"Cidade":"carazinho",
"Site":"Tempo Agora"
}]
I am currently using this code to generate json.
const climatempo = async (config) => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
const override = Object.assign(page.viewport(), {width: 1920, heigth:1024});
await page.setViewport(override);
await page.goto(config.cidades[cidade],{waitUntil: 'load',timeout:'60000'})
if(siteEscolhido == "accu"){
const elementTemp = await page.$(config.regras.elementTemp)
const temperatura = await page.evaluate(elementTemp => elementTemp.textContent, elementTemp)
const sensacaoElement= await page.$(config.regras.sensacaoElement)
const sensacao = await page.evaluate(sensacaoElement => sensacaoElement.textContent, sensacaoElement)
const chuvaElement = await page.$(config.regras.chuvaElement)
const chuva = await page.evaluate(chuvaElement => chuvaElement.textContent, chuvaElement)
const ventoElement = await page.$(config.regras.ventoElement)
const vento = await page.evaluate(ventoElement => ventoElement.textContent, ventoElement)
const atualizadoA = await page.$(config.regras.atualizadoA)
const momentoAtualizacao = await page.evaluate(atualizadoA => atualizadoA.textContent, atualizadoA)
var dado = {
temperatura:temperatura,
sensacao:sensacao,
chuva:chuva,
vento:vento,
momentoAtualizacao:momentoAtualizacao,
Cidade:cidade,
Site:"Accuweather"
}
//dados.push(dado)
var x = JSON.stringify(dado)
fs.appendFile('climatempo.json',x,function(err){
if(err) throw err
})
console.log("Temperatura:" + temperatura)
console.log(sensacao)
console.log("Vento:" + vento)
console.log("chuva:" + chuva)
console.log(momentoAtualizacao)
await browser.close()
If anyone has any idea how to solve my problem, please let me know!
Grateful, Carlos

I would suggest doing it a little differently
I will try to explain in pseudo code since i dont understand your variable names
read json file
array = JSON.parse(fileContents)
array.push(newItem)
newContents = JSON.stringify(array)
file WRITE (not append) newContents

I recommend reading the file, pushing onto an array captured from that file, and then writing the file back to disk.
Assuming the file has content already in the form of an array:
let fileDado = JSON.parse(fs.readFileSync('climatempo.json'));
fileDado.push(dado);
fs.writeFileSync('climatempo.json', JSON.stringify(fileDado));

Related

How to set cookies in a variable and use it again: PUPPETEER

i'm trying to insert ALL browser cookies in a variable and then use it again later.
My attempt:
const puppeteer = require('puppeteer')
const fs = require('fs').promises;
(async () => {
const browser = await puppeteer.launch({
headless: false,
executablePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
const page = await browser.newPage();
await page.goto('https://www.google.com/');
Below, it's my code to get cookies, and it work, but now it print a error message.
const client = await page.target().createCDPSession();
const all_browser_cookies = (await client.send('Network.getAllCookies')).cookies;
const current_url_cookies = await page.cookies();
var third_party_cookies = all_browser_cookies.filter(cookie => cookie.domain !== current_url_cookies[0].domain);
and below it's the seccond page (that will use cookies)
(async () => {
const browser2 = await puppeteer.launch({
});
const url = 'https://www.google.com/';
const page2 = await browser2.newPage();
try{
await page2.setCookie(...third_party_cookies);
await page2.goto(url);
}catch(e){
console.log(e);
}
await browser2.close()
})();
})();
until yesterday it works, but today it's appearing this message error:
Error: Protocol error (Network.setCookies): Invalid parameters Failed to deserialize params.cookies.expires - BINDINGS: double value expected at position 662891
Anyone know what is it?
Ok, the solution is here: https://github.com/puppeteer/puppeteer/issues/7029
You are saving an array of cookies and page.setCookie only works with one cookie. Yo have to iterate trought your array like that:
for (let i = 0; i < third_party_cookies.length; i++) {
await page2.setCookie(third_party_cookies[i]);
}
#julio-codesal's suggestion is ok, but according to the puppeteer documentaion it is ok to use page.setCookie with an array, as long as you destructure it, e.g.:
await page.setCookie(...arr)
source: https://devdocs.io/puppeteer/index#pagesetcookiecookies
Not exactly sure what it is, but the error the OP has is something else.

get data from another add-on on Excel javascript api

I saved data on workbook on following code
export const storeSettingsToWorkbook = async (settingsType: Settings, storeData:
WorkbookModel) => {
return Excel.run(async (context) => {
const originalXml = createXmlObject(storeData);
const customXmlPart = context.workbook.customXmlParts.add(originalXml);
customXmlPart.load("id");
await context.sync();
// Store the XML part's ID in a setting
const settings = context.workbook.settings;
settings.add(settingsTitles[settingsType], customXmlPart.id);
await context.sync();
})
}
when i get data -it works normally.But when i want to get this data form another "add-in" on Excel- I cannot get this data
const {settings} = context.workbook;
const sheet = context.workbook.worksheets.getActiveWorksheet().load("items");
const xmlPartIDSetting = settings.getItemOrNullObject(settingsTitles[settingsType]).load("value");
await context.sync();
if (xmlPartIDSetting.value) {
const customXmlPart = context.workbook.customXmlParts.getItem(xmlPartIDSetting.value);
const xmlBlob = customXmlPart.getXml();
await context.sync()
const parsedObject = parseFromXmlString(xmlBlob.value);
const normalizedData = normalizeParsedData(parsedObject);
Any ideas?
Thanks for reaching us.
This is by design. Each addin has its own setting and cannot share with each other.
You can use 'context.workbook.properties.custom' as a workaround.
You can also use 'context.workbook.worksheets.getActiveWorksheet().customProperties', but the two add-ins are required to be on the same worksheet.

Node.js PDFjs fetching specific page using pdfjs-dist

I'm trying to fetch the contents of page 4 of a PDF file using 'pdfjs-dist'.
I've tried to replace the 'pdfjs-dist' module with 'const pdfjs = require("pdfjs/es5/build/pdf")' but with no success.
What could be problem?
Thanks in advance!
const pdfjs = require('pdfjs-dist'); // Fetch PDF
async function getContent(src) {
const doc = await pdfjs.getDocument(src).promise // note the use of the property promise
const page = await doc.getPage(4)
return await page.getTextContent()
}
console.log(getContent('pdfs/Quantum.pdf'))
It's too late to reply to this questions but let me add the solution that worked for me for those who will wish to implement the some issue.
This is the code that will work fine
// Install the latest version of pdf.js via npm i pdfjs-dist
const pdfjsLib = require("pdfjs-dist/legacy/build/pdf.js");
let pdf_path = "{RELATIVE_PATH_TO_YOUR_FILE}/sample.pdf";
async function getContent(src: any){
const doc = await pdfjsLib.getDocument(src).promise;
const page = await doc.getPage(1);
const strings: any = await page.getTextContent();
let ITEMS_STRINGS = strings.items.map((item: any) => item.str);
let PDF_STRINGS: string = ITEMS_STRINGS.join(" ");
return PDF_STRINGS;
}
console.log(await getContent(`${pdf_path}`));
This will work fine

How can I let a script parse content from next pages?

I'm trying to scrape three fields from different companies listed in a webpage. The idea here is to make use of this webpage, parse the inner page links of different companies from the landing page and finally scrape title,phone and email from the detail pages. The script that I've created can perform this without any issue.
However, I wish to scrape content from next pages (Next) as well. As I'm very new to node, I would appreciate if somebody could help me implement the logic of grabbing next pages within the script below.
const puppeteer = require("puppeteer");
const base = "https://www.timesbusinessdirectory.com";
const url = "https://www.timesbusinessdirectory.com/company-listings";
(async () => {
const browser = await puppeteer.launch({headless:false});
const [page] = await browser.pages();
await page.goto(url,{waitUntil: 'networkidle2'});
page.waitForSelector(".company-listing");
const sections = await page.$$(".company-listing");
let data = [];
for (const section of sections) {
const itemName = await section.$eval("h3 > a", el => el.getAttribute("href"));
data.push(itemName);
}
let itmdata = [];
for (const link of data) {
const newlink = base.concat(link);
await page.goto(newlink,{waitUntil: 'networkidle2'});
page.waitForSelector(".company-details");
const result = await page.$$(".company-details");
for (const itmres of result) {
const company = await itmres.$eval("h3", el => el.textContent);
const phone = await itmres.$eval("#valuephone a[href]", el => el.textContent);
const email = await itmres.$eval("a[onclick^='showCompanyEmail']", el => el.getAttribute("onclick"));
console.log({
title: company,
tel : phone.trim(),
emailId : email.split("('")[1].split("',")[0]
});
}
}
await browser.close();
})();
How can I implement the logic of traversing next pages within the script?
EDIT:
I was trying to use the next page link within the script iteratively defining a while loop. This time the script however doesn't seem to respond at all. Perhaps I've done something wrong along the lines.
In your current code, you declare new url variable in each iteration. Instead of this, you need to declare the outer url variable as let and redefine it:
let url = "https://www.timesbusinessdirectory.com/company-listings";
// ...
url = base.concat(nextPageLink);

Take screenshots of different elements with specific names in Puppeteer

I am trying to take screenshots of each section in a landing page which may container multiple sections. I was able to do that effectively in "Round1" which I commented out.
My goal is to learn how to write leaner/cleaner code so I made another attempt, "Round2".
In this section it does take a screenshot. But, it takes screenshot of section 3 with file name JSHandle#node.png. Definitely, I am doing this wrong.
Round1 (works perfectly)
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.somelandingpage.com');
// const elOne = await page.$('.section-one');
// await elOne.screenshot({path: './public/SectionOne.png'})
// takes a screenshot SectionOne.png
// const elTwo = await page.$('.section-two')
// await elTwo.screenshot({path: './public/SectionTwo.png'})
// takes a screenshot SectionTwo.png
// const elThree = await page.$('.section-three')
// await elThree.screenshot({path: './public/SectionThree.png'})
// takes a screenshot SectionThree.png
Round2
I created an array that holds all the variables and tried to loop through them.
const elOne = await page.$('.section-one');
const elTwo = await page.$('.section-two')
const elThree = await page.$('.section-three')
let lpElements = [elOne, elTwo, elThree];
for(var i=0; i<lpElements.length; i++){
await lpElements[i].screenshot({path: './public/'+lpElements[i] + '.png'})
}
await browser.close();
})();
This takes a screenshot of section-three only, but with wrong file name (JSHandle#node.png). There are no error messages on the console.
How can I reproduce Round1 by modifying the Round2 code?
Your array is only of Puppeteer element handle objects which are getting .toString() called on them.
A clean way to do this is to use an array of objects, each of which has a selector and its name. Then, when you run your loop, you have access to both name and selector.
const puppeteer = require('puppeteer');
const content = `
<div class="section-one">foo</div>
<div class="section-two">bar</div>
<div class="section-three">baz</div>
`;
const elementsToScreenshot = [
{selector: '.section-one', name: 'SectionOne'},
{selector: '.section-two', name: 'SectionTwo'},
{selector: '.section-three', name: 'SectionThree'},
];
const getPath = name => `./public/${name}.png`;
let browser;
(async () => {
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.setContent(content);
for (const {selector, name} of elementsToScreenshot) {
const el = await page.$(selector);
await el.screenshot({path: getPath(name)});
}
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;

Categories