Skip to content

Commit

Permalink
Render sauna links on webpage
Browse files Browse the repository at this point in the history
  • Loading branch information
jvaclavik committed Nov 19, 2019
1 parent 4b60f8a commit a77ef71
Show file tree
Hide file tree
Showing 8 changed files with 485 additions and 47 deletions.
Binary file added .DS_Store
Binary file not shown.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Airbnb is a bad service without the possibility of filtering by sauna. This script should fix it.

## Screenshot
<img src="screenshot.jpg" />

## Use

1. Install dependencies
Expand All @@ -10,14 +13,16 @@ Airbnb is a bad service without the possibility of filtering by sauna. This scri
yarn
```

2. Set `editableParams` (index.js)
3. Set `editableParams` (index.js)

3. Run script
4. Run server

```
yarn start
```

5. Run `http://localhost:8085/` in your browser

## Info

Open links in Terminal (Mac): `CMD + [double click]`
Expand Down
95 changes: 60 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
const fetch = require("node-fetch");

const numberOfItemsOnOnePage = 50;
const maximumOfRequests = 50;
const express = require("express");
const path = require("path");

const port = process.env.PORT || 8085;
const app = express();

const numberOfItemsOnOnePage = 10;
const maximumOfRequests = 20;
const currency = "CZK";
let saunaLinks = "";
let numberOfRequests = 0;

app.use(express.static(__dirname, { dotfiles: "allow" }));
app.engine("html", require("ejs").renderFile);
app.get("/", async function(req, res) {
saunaLinks = "";
numberOfRequests = 0;
if (req.query) {
console.log(req.query.query);
await fetchResultPage(req.query.query);
res.render(__dirname + "/public/index.html", { links: saunaLinks });
}
});
app.listen(port);

const editableParams = {
query: "Lofoten, Norway",
checkin: "2020-04-01",
checkout: "2020-04-17",
// query: "Helsinki, Finland",
// checkin: "2020-04-01",
// checkout: "2020-04-17",
price_min: 0,
price_max: 10000,
adults: 6
Expand Down Expand Up @@ -44,8 +65,6 @@ const getParams = (itemsOffset = 0) => ({
satori_version: "1.1.0"
});

let numberOfRequests = 0;

const filterAsync = (array, filter) =>
Promise.all(array.map(entry => filter(entry))).then(bits =>
array.filter(entry => bits.shift())
Expand All @@ -68,6 +87,7 @@ const fetchHome = id => {
.then(body => {
if (body) {
const descriptionObject = body.pdp_listing_detail.sectioned_description;
const photo = body.pdp_listing_detail.photos[0].large;
const searchedInFields = [
"space",
"summary",
Expand All @@ -85,11 +105,11 @@ const fetchHome = id => {
if (isSaunaAvailable) {
const params = getParams();
const homeUrl = `https://www.airbnb.cz/rooms/${id}`;
const homeUrlWithDateInterval = params.checkin
? `${homeUrl}?check_in=${params.checkin}&check_out=${
params.checkout
}`
: homeUrl;
const homeUrlWithDateInterval =
params.checkin && params.checkout
? `${homeUrl}?check_in=${params.checkin}&check_out=${params.checkout}`
: homeUrl;
saunaLinks = `${saunaLinks}<a href="${homeUrlWithDateInterval}" target="_blank"><div class="item" style="background-image: url('${photo}')"></div></a>`;
console.log(`- ${homeUrlWithDateInterval}`);
}
}
Expand All @@ -101,32 +121,37 @@ const fetchHome = id => {
return request;
};

const fetchResultPage = (offset = 0) => {
if (numberOfRequests > maximumOfRequests) return;
const fetchResultPage = async (query = null, offset = 0) => {
if (numberOfRequests > maximumOfRequests || query === null) return;
const params = getParams(offset);
const paramString = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join("&");
// console.log(`https://api.airbnb.com/v2/explore_tabs?${paramString}`);
fetch(`https://api.airbnb.com/v2/explore_tabs?${paramString}`, {
method: "get",
headers: headers
})
.then(body => body.json().catch(e => null))
.then(body => {
if (body) {
const ids = body.explore_tabs[0].home_tab_metadata.remarketing_ids;
console.log(`Query: ${query}`);
const paramString =
Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join("&") + `&query=${query}`;

const saunasUrls = filterAsync(ids, fetchHome);
if (body.explore_tabs[0].pagination_metadata.has_next_page) {
fetchResultPage(offset + numberOfItemsOnOnePage);
}
console.log(`https://api.airbnb.com/v2/explore_tabs?${paramString}`);
const request = await fetch(
`https://api.airbnb.com/v2/explore_tabs?${paramString}`,
{
method: "get",
headers: headers
}
);
try {
const body = await request.json();
if (body) {
const ids = body.explore_tabs[0].home_tab_metadata.remarketing_ids;
const homePromise = await filterAsync(ids, fetchHome);
if (body.explore_tabs[0].pagination_metadata.has_next_page) {
await fetchResultPage(query, offset + numberOfItemsOnOnePage);
}
})
.catch(e => {
console.log(e);
});
}
} catch (err) {
console.log("error");
}

numberOfRequests++;
};
console.log(`Saunas ${getParams().query}\n============================`);
console.log(`Saunas\n============================`);
fetchResultPage();
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"dependencies": {
"node-fetch": "^2.6.0"
"ejs": "^2.7.1",
"express": "^4.17.1",
"node-fetch": "^2.6.0",
"path": "^0.12.7"
},
"devDependencies": {
"nodemon": "^1.19.4"
},
"scripts": {
"start": "nodemon --exec node index.js"
"start": "nodemon --exec node index.js",
"build": "node index.js"
}
}
48 changes: 48 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<html>
<head>
<title>Airbnb sauna finder</title>
<style>
body {
font-family: sans-serif;
}
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.item{
width: 300px;
height: 200px;
margin-right: 10px;
margin-bottom: 10px;
background-size: cover;
border-radius: 10px;
}
a:hover {
opacity: 0.8;
transition: all 0.1s ease;
}
input {
border-radius: 5px;
line-height: 30px;
border: solid 1px #ccc;
padding: 0 10px;
}
input[type="submit"]{
background: #FF5A5F;
border: solid 1px #FF5A5F;
color: white;
padding: 0px 30px;
}
</style>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Airbnb sauna finder</h1>
<form method="GET">
Location: <input type="text" name="query" />
<input type="submit" value="Search" />
</form><br>
<div class="container"><%- links %></div>
</body>
</html>
Empty file added public/index.js
Empty file.
Binary file added screenshot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit a77ef71

Please sign in to comment.