Related
I am trying to get gas prices for a web app I am making. I found a web scraper from Apify that gets the data I need. The problem is I'm not sure how to extract just the gas prices from the returned object from the API. There is a lot of information.
Here is the code I run in node:
import { ApifyClient } from 'apify-client';
// Initialize the ApifyClient with API token
const client = new ApifyClient({
token: 'apify_api_********',
});
// Prepare actor input
const input = {
"location": "Indiana",
"maxCrawledPlacesPerSearch": 1
};
(async () => {
// Run the actor and wait for it to finish
const run = await client.actor("natasha.lekh/gas-prices-scraper").call(input);
// Fetch and print actor results from the run's dataset (if any)
console.log('Results from dataset');
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
console.log(item);
})();
Which returns:
Results from dataset
{
title: 'Luke',
subTitle: null,
description: null,
price: null,
menu: null,
categoryName: 'Gas station',
address: '1051 Indianapolis Blvd, Hammond, IN 46320',
locatedIn: null,
neighborhood: '1051 Indianapolis Blvd',
street: '1051 Indianapolis Blvd',
city: 'Hammond',
postalCode: '46320',
state: 'Indiana',
countryCode: 'US',
plusCode: 'MFVP+R3 Hammond, Indiana',
website: 'http://uluke.com/',
phone: '(219) 473-1425',
temporarilyClosed: false,
claimThisBusiness: false,
location: { lat: 41.6945038, lng: -87.5147858 },
permanentlyClosed: false,
totalScore: 3.7,
isAdvertisement: false,
rank: 1,
placeId: 'ChIJm_BHBfbYEYgRZee8gFZeG7o',
categories: [ 'Gas station', 'Convenience store' ],
cid: '13410416041045845861',
url: 'https://www.google.com/maps/place/Luke/#41.6945038,-87.5147858,17z/data=!3m1!4b1!4m6!3m5!1s0x8811d8f60547f09b:0xba1b5e5680bce765!8m2!3d41.6945038!4d-87.5147858!16s%2Fg%2F1ptvtxy1r?hl=en',
searchPageUrl: 'https://www.google.com/maps/search/gas%20station%20in%20Indiana?hl=en',
searchPageLoadedUrl: 'https://www.google.com/maps/search/gas+station+in+Indiana/#40.7310665,-86.5930181,8z?hl=en',
searchString: 'gas station in Indiana',
scrapedAt: '2023-02-11T04:17:35.902Z',
popularTimesLiveText: null,
popularTimesLivePercent: null,
popularTimesHistogram: {
Su: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
Mo: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
Tu: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
We: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
Th: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
Fr: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
],
Sa: [
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object],
[Object], [Object], [Object]
]
},
openingHours: [
{ day: 'Monday', hours: '5 AM to 10 PM' },
{ day: 'Tuesday', hours: '5 AM to 10 PM' },
{ day: 'Wednesday', hours: '5 AM to 10 PM' },
{ day: 'Thursday', hours: '5 AM to 10 PM' },
{ day: 'Friday', hours: '5 AM to 10 PM' },
{ day: 'Saturday', hours: '5 AM to 10 PM' },
{ day: 'Sunday', hours: '5 AM to 10 PM' }
],
peopleAlsoSearch: [
{
category: 'People also search for',
title: 'BP Luke Gas Station',
reviewsCount: 441,
totalScore: 3.3
},
{
category: 'People also search for',
title: 'Luke Gas Station Car Wash',
reviewsCount: 9,
totalScore: 2.2
},
{
category: 'People also search for',
title: 'GoLo',
reviewsCount: 331,
totalScore: 3.8
},
{
category: 'People also search for',
title: 'Shell',
reviewsCount: 104,
totalScore: 3.5
},
{
category: 'People also search for',
title: "Luke's Gas Station",
reviewsCount: 0,
totalScore: 0
}
],
additionalInfo: {
Accessibility: [ [Object], [Object] ],
Offerings: [ [Object] ],
Amenities: [ [Object] ],
Payments: [ [Object] ]
},
reviewsCount: 283,
reviewsDistribution: {
oneStar: 41,
twoStar: 10,
threeStar: 51,
fourStar: 67,
fiveStar: 114
},
imagesCount: 8,
reviews: [],
reviewsTags: [
{ title: 'prices', count: 21 },
{ title: 'pumps', count: 15 },
{ title: 'clean', count: 8 },
{ title: 'pay', count: 6 },
{ title: 'credit cards', count: 5 },
{ title: 'convenient', count: 3 }
],
orderBy: [],
gasPrices: [
{
priceTag: '$3.30',
updatedAt: '2023-02-10T14:46:30.000Z',
unit: 'gallon',
currency: 'USD',
price: 3.3,
gasType: 'Regular'
},
{
priceTag: '$3.70',
updatedAt: '2023-02-10T05:37:40.000Z',
unit: 'gallon',
currency: 'USD',
price: 3.7,
gasType: 'Midgrade'
},
{
priceTag: '$4.10',
updatedAt: '2023-02-10T05:37:40.000Z',
unit: 'gallon',
currency: 'USD',
price: 4.1,
gasType: 'Premium'
},
{
priceTag: '$5.00',
updatedAt: '2023-02-10T06:52:21.000Z',
unit: 'gallon',
currency: 'USD',
price: 5,
gasType: 'Diesel'
}
]
}
All I want is the last bit, the priceTag and gasType for each type of gas. How would I get that data out of the returned object?
I thought it was a JSON file so I tried a few things I found online, but I'm not really sure where to start.
I'm receiving an array and performing a treatment to extract some information.
When I get the matrix like this, everything goes well!
[
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object]
]
}
]
But when the array comes like this:
[
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{ averages: 'No data in period.' }
]
I get this error:
ccs.medias.forEach is not a function
Could this be the error?
{ averages: 'No data in period.' }
The code mentioned to filter the information I want is this:
function test(arr) {
let results = [];
arr.forEach(ccs => {
ccs.medias.forEach(element => {
if (element["TYPE"] === 'AAA') {
results.push(element["STATUS"])
}
})
})
return results;
}
As I mentioned above, this code works for the first array example, but not for the following one.
I appreciate if anyone can help me analyze it!
Yep, you guessed right, problem is here:
{ averages: 'No data in period.' }
Before using .forEach(), you need to make sure property value (ccs.averages) is array, so just add simple if-condition like:
if (Array.isArray(ccs.averages)) {
// Do what you need with array and only
}
Full code:
const arr = [
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{
averages: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
]
},
{ averages: 'No data in period.' }
]
function test(arr) {
let results = [];
arr.forEach(ccs => {
// Make sure .avarages is array
if (Array.isArray(ccs.averages)) {
ccs.averages.forEach(element => {
if (element["TYPE"] === 'AAA') {
results.push(element["STATUS"])
}
})
}
})
return results;
}
console.log(test(arr));
Refs: Array.isArray().
As this question is tagged as "mongo", to do it in a query and retrieve results directly without the fields which are not an array you can use one of these queries:
For a find query you can add:
"averages": { "$type": "array }
Like this:
db.collection.find({
"averages": {
"$type": "array"
}
})
Example here
Also if you are using an aggregation stage you can add a $match stage like this:
db.collection.aggregate([
{
"$match": {
"averages": {
"$type": "array"
}
}
}
])
Example here
Note that the queries use $type
In this way you can avoid the loop.
I'm developing with the LocalBitcoins API using nodejs. Heres the code
const adsList = async (action, options = {}, page) => {
let prefix = action + '-'
let countryCode = (options.countryCode) ? options.countryCode : false
let countryName = (options.countryName) ? options.countryName : false
let paymentMethod = (options.paymentMethod) ? options.paymentMethod : false
let currency = (options.currency) ? options.currency : false
let basePath = prefix + 'bitcoins-online'
let suffix = (page > 1) ? `.json?page=2` : '.json'
let path
if (currency) {
path = (paymentMethod) ? `${basePath}/${currency}/${paymentMethod}/${suffix}` : `${basePath}/${currency}/${suffix}`
} else if (countryCode && countryName) {
path = (paymentMethod) ? `${basePath}/${countryCode}/${countryName}/${paymentMethod}/${suffix}` : `${basePath}/${countryCode}/${countryName}/${suffix}`
} else if (!currency && !countryCode && !countryName) {
path = (paymentMethod) ? `${basePath}/${paymentMethod}/${suffix}` : `${basePath}/${suffix}`
}
let response = await get(path, true)
return response
}
Now when I make a call with no page given, everything works fine.
adsList('sell', {
countryCode: 'co',
countryName: 'colombia'
}).then(response => {
console.log(response)
})
The output:
{
pagination: {
next: 'https://localbitcoins.com/sell-bitcoins-online/co/colombia/.json?page=2'
},
data: {
ad_list: [
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object], [Object], [Object],
[Object], [Object]
],
ad_count: 50
}
}
Ah, but when I add a page:
adsList('sell', {
countryCode: 'co',
countryName: 'colombia'
}, 2).then(response => {
console.log(response)
})
This is the output:
{
error: {
message: 'HMAC authentication key and signature was given, but they are invalid.',
error_code: 41
}
}
I have been working on this for hours but and searching info but there is nothing, so if you have any solution I will be grateful.
I am using nodejs and the node code is fairly simple (using some of folktale here, but a task is similar to a promise):
const connect = (vcenter) => {
return new Task( (reject, resolve) => {
const Vsphere = require('vsphere');
const vc = new Vsphere.Client(vcenter, 'me', 'myPass', false);
vc.once('ready', () => resolve(vc));
vc.once('error', reject);
})
}
const getVirtualMachines = (vc) => {
return new Task( (reject, resolve) => {
const rootFolder = vc.serviceContent.rootFolder;
const vms = vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine');
vms.once('result', resolve)
vms.once('error', reject)
})
}
connect(vcenterIp).
chain(getVirtualMachines).
// SNIP (this isn't significant)
fork(e2,f2)
and results in sending these requests
CreateContainerView
{
"_this": {
"attributes": {
"type": "ViewManager"
},
"$value": "ViewManager"
},
"container": {
"attributes": {
"type": "Folder"
},
"$value": "group-d1"
},
"type": "VirtualMachine",
"recursive": true
}
RetrievePropertiesEx
{
"_this": {
"attributes": {
"type": "PropertyCollector"
},
"$value": "propertyCollector"
},
"specSet": [
{
"attributes": {
"xsi:type": "PropertyFilterSpec"
},
"propSet": [
{
"attributes": {
"xsi:type": "PropertySpec"
},
"type": "VirtualMachine",
"all": true
}
],
"objectSet": [
{
"attributes": {
"xsi:type": "ObjectSpec"
},
"obj": {
"attributes": {
"type": "ContainerView"
},
"$value": "session[520e031b-3c15-9c1d-408a-45ab98bde1dc]52dfe626-a128-c94f-8c4c-df52a68d97c0"
},
"skip": true,
"selectSet": [
{
"attributes": {
"xsi:type": "TraversalSpec"
},
"type": "ContainerView",
"path": "view",
"skip": false
}
]
}
]
}
],
"options": {}
}
Which returns
{ returnval:
{ token: '0',
objects:
[ [Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object] ] } }
The problem i am seeing is that the API is only returning 100 items, while there are more than 100VMs in my environment.
I really want the query to return all of my VMs
update 1:
To invoke a single property collection operation, call the
RetrievePropertiesEx method. The example application passes the
populated PropertyFilterSpec and an empty options structure to the
method. The default for the RetrieveOptions.maxObjects specifies that
no maximum for the number of objects that can be returned is set. The
PropertyCollector can impose a maximum. If the number of collected
objects is greater than the maximum, the PropertyCollector returns a
token value in the RetrieveResult data object and this token is used
to retrieve the remaining properties using the
ContinueRetrievePropertiesEx API method
https://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.wssdk.pg.doc_50%2FPG_Ch5_PropertyCollector.7.5.html
update 2
OK looking at the returned results vSPehere is returning me a token code. and the node-vsphere library isn't to retrieve all of the results. I need to figure out a way to get all the results in one step.
I need to follow up with this request
ContinueRetrievePropertiesEx
{
"_this": {
"attributes": {
"type": "PropertyCollector"
},
"$value": "propertyCollector"
},
"token": "0"
}
HEre is how I accomplished this with the library:
const connect = (vcenter) => {
return new Task( (reject, resolve) => {
const Vsphere = require('vsphere');
const vc = new Vsphere.Client(vcenter, 'xyz\\tbrown', 'ijhi', false);
vc.once('ready', () => resolve(vc));
vc.once('error', reject);
})
}
const getVirtualMachines = (vc) => {
return new Task( (reject, resolve) => {
const rootFolder = vc.serviceContent.rootFolder;
const vms = vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine');
vms.once('result', (initial) =>{
if(initial.returnval.token == undefined) {
resolve(initial)
return
}else {
const thisReceiveAll = receiveAll(reject, resolve)
thisReceiveAll(vc, initial)
}
})
vms.once('error', reject)
})
}
const receiveAll = (reject, resolve) => (vc, initial) => {
const executeContinueReceive = function executeContinueReceive(previous) {
const args = {
_this: {"attributes":{"type":"PropertyCollector"},"$value":"propertyCollector"},
token: previous.returnval.token
}
vc.vc.runCommand('ContinueRetrievePropertiesEx', args).once('result', function(current){
const previousObjects = previous.returnval.objects
const currentObjects = current.returnval.objects
const allObjects = previousObjects.concat(currentObjects)
current.returnval.objects = allObjects
if(current.returnval.token == undefined) {
resolve(current);
return
}
return executeContinueReceive(current)
}).once('error', reject);
}
executeContinueReceive(initial)
}
Essentially when I retreive the intial result set, and check for a token. If the token is there I enter a recursive "receiveAll" function which calls runCommand('ContinueRetrievePropertiesEx', args) and appends its results. Again checking the token at the end, and either returning the result OR make another recursive call... perhaps this should move back into the library
You can simply use RetrieveProperties and you will not need to do any kind of traversing.
Just cannot get my head around this after 10 hours of trying:
if I
users.findById(req.body.user_id,function(e,doc){});
and console.log the doc returned, all looks good:
{ _id: 54dcad6de4b01007caacb0cd,
username: 'realizertest',
password: '******************************',
first_name: 'Realizer',
second_name: 'Test',
display_name: 'Realizer Test',
email: 'system#realizerlabs.com' }
However, when trying to access the included fields, e.g. by:
user = users.findById(req.body.user_id,function(e,doc){});
var user_email = user.email;
I just get undefined. The user object looks like this:
{ col:
{ manager:
{ driver: [Object],
helper: [Object],
collections: [Object],
options: [Object],
_events: {} },
driver:
{ _construct_args: [],
_native: [Object],
_emitter: [Object],
_state: 2,
_connect_args: [Object] },
helper: { toObjectID: [Function], id: [Ob
name: 'users',
col:
{ _construct_args: [],
_native: [Object],
_emitter: [Object],
_state: 2,
_skin_db: [Object],
_collection_args: [Object],
id: [Object],
emitter: [Object] },
options: {} },
type: 'findOne',
opts: { fields: {}, safe: true },
domain: null,
_events:
{ error: [ [Function], [Function] ],
success: [ [Function], [Function] ] },
_maxListeners: 10,
emitted: {},
ended: false,
success: [Function],
error: [Function],
complete: [Function],
resolve: [Function],
fulfill: [Function],
reject: [Function],
query: { _id: 54dcad6de4b01007caacb0cd } }
I've also tried user.query.email but get the same result.
The findById obviously doesn't return a JSON object that I can use in this way.
How can I get at these fields?
It's an async call, so you need to use the callback, you can't assign that function to a variable:
users.findById(req.body.user_id,function(e,doc){
var user = doc;
console.log(user); //should see the object now, and access the props
});