Public
Edited
Sep 24, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
listings
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Insert cell
Insert cell
prices = _.map(listingData, 'price')
Insert cell
areaPrices = _.map(listingData, 'areaPrice')
Insert cell
areas = _.map(listingData, 'area')
Insert cell
averagePrice = avg(prices)
Insert cell
averageArea = avg(areas)
Insert cell
averageAreaPrice = avg(areaPrices)
Insert cell
listings = listingData.map((listing) => {
return {
...listing,
avgPriceDiff: (listing.price - averagePrice) / listing.price,
avgAreaPriceDiff: (listing.areaPrice - averageAreaPrice) / listing.areaPrice,
avgAreaDiff: (listing.area - averageArea) / listing.area
};
})
Insert cell
Insert cell
listingData = await Promise.all(
normalizeStrings(urls).map(async (urlInput) => {
// Page fetching happens here
const qi = urlInput.indexOf("?");
const url = qi < 0 ? urlInput : urlInput.substring(0, qi);
const html = await fetchPage(url);
const page = parseHtml(html);

// Basic details
const id = url.replace("https://www.etuovi.com/kohde/", "");

const title = getRawText(page.querySelector("h1"));

// Parsing
const basicDetailsNodes = page.querySelector(
"#infos > div > div > div > :nth-child(2)"
).childNodes[0].childNodes;

const details = samplePage
.querySelectorAll(
"#showings > div:nth-child(2) > div:nth-child(2) > div > div > div > div > div > div > div + div"
)
.map(getRawText);

const [city, buildingType, shareType, description, type, floorString] = [
details[1],
details[2],
details[3],
details[4],
details[5],
details[8]
];

const [floor, floorMax] = floorString
? floorString.split("/").map(parseFloat).map(round)
: [1, 1];

const basicDetails = basicDetailsNodes.map(({ childNodes }) => {
return getRawText(childNodes[1].childNodes[0]);
});

const [priceString, areaString, yearString] = basicDetails;
const price = parseCurrencyString(priceString);
const area = parseAreaString(areaString);
const year = parseInt(yearString);

const taxPercentage = 0.015;
// const taxPercentage = _.includes(["Kerrostalo", "Rivitalo"], buildingType)
// ? 0.02
// : 0.04;
const tax = price * taxPercentage;
const areaPrice = price / area;

return {
// html,
// page,
id,
url,
title,

price,
areaPrice,
tax,
taxPercentage,

area,
year,
city,
buildingType,
shareType,
description,
type,
floor,
floorMax
};
})
)
Insert cell
Insert cell
Insert cell
samplePage = parseHtml(await fetchPage('https://www.etuovi.com/kohde/u66694?haku=M2011592486'))
Insert cell
floor = samplePage.querySelectorAll(
"#showings > div:nth-child(2) > div:nth-child(2) > div > div > div > div > div > div > div + div"
).map(getRawText)[8]
Insert cell
floor.split('/').map(parseFloat)
Insert cell
samplePage.querySelector(
"#showings > div:nth-child(2) > div:nth-child(2) > div > div > div > div > :nth-child(2) > div > a > img"
)
Insert cell
getRawText(samplePage.querySelector(
"#showings > div:nth-child(3) > div:nth-child(2)"
))
Insert cell
getRawText(samplePage.querySelector(
"#showings > div:nth-child(4) > div:nth-child(2)"
))
Insert cell
Insert cell
Insert cell
Insert cell
function parseCurrencyString(string) {
return parseFloat(
string.replace(/\s/g, "").replaceAll(",", ".").replaceAll("€", "")
);
}
Insert cell
Insert cell
Insert cell
Insert cell
function normalizeStrings (...items) {
return _.flatten(_.flattenDeep(items).map(splitString))
}
Insert cell
function splitString (string) {
const list = string.split(',')

return _.map(list, (s) => {
return s.trim()
})
}
Insert cell
function corsifyUrl(url) {
return `https://corsproxy.io/?${encodeURIComponent(url)}`;
}
Insert cell
async function fetchPage(url, noCors) {
const response = await fetch(noCors ? url : corsifyUrl(url));

return response.text()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more