Skip to content

Commit a77ef71

Browse files
committed
Render sauna links on webpage
1 parent 4b60f8a commit a77ef71

8 files changed

Lines changed: 485 additions & 47 deletions

File tree

.DS_Store

6 KB
Binary file not shown.

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

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

5+
## Screenshot
6+
<img src="screenshot.jpg" />
7+
58
## Use
69

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

13-
2. Set `editableParams` (index.js)
16+
3. Set `editableParams` (index.js)
1417

15-
3. Run script
18+
4. Run server
1619

1720
```
1821
yarn start
1922
```
2023

24+
5. Run `http://localhost:8085/` in your browser
25+
2126
## Info
2227

2328
Open links in Terminal (Mac): `CMD + [double click]`

index.js

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
const fetch = require("node-fetch");
22

3-
const numberOfItemsOnOnePage = 50;
4-
const maximumOfRequests = 50;
3+
const express = require("express");
4+
const path = require("path");
5+
6+
const port = process.env.PORT || 8085;
7+
const app = express();
8+
9+
const numberOfItemsOnOnePage = 10;
10+
const maximumOfRequests = 20;
511
const currency = "CZK";
12+
let saunaLinks = "";
13+
let numberOfRequests = 0;
14+
15+
app.use(express.static(__dirname, { dotfiles: "allow" }));
16+
app.engine("html", require("ejs").renderFile);
17+
app.get("/", async function(req, res) {
18+
saunaLinks = "";
19+
numberOfRequests = 0;
20+
if (req.query) {
21+
console.log(req.query.query);
22+
await fetchResultPage(req.query.query);
23+
res.render(__dirname + "/public/index.html", { links: saunaLinks });
24+
}
25+
});
26+
app.listen(port);
627

728
const editableParams = {
8-
query: "Lofoten, Norway",
9-
checkin: "2020-04-01",
10-
checkout: "2020-04-17",
29+
// query: "Helsinki, Finland",
30+
// checkin: "2020-04-01",
31+
// checkout: "2020-04-17",
1132
price_min: 0,
1233
price_max: 10000,
1334
adults: 6
@@ -44,8 +65,6 @@ const getParams = (itemsOffset = 0) => ({
4465
satori_version: "1.1.0"
4566
});
4667

47-
let numberOfRequests = 0;
48-
4968
const filterAsync = (array, filter) =>
5069
Promise.all(array.map(entry => filter(entry))).then(bits =>
5170
array.filter(entry => bits.shift())
@@ -68,6 +87,7 @@ const fetchHome = id => {
6887
.then(body => {
6988
if (body) {
7089
const descriptionObject = body.pdp_listing_detail.sectioned_description;
90+
const photo = body.pdp_listing_detail.photos[0].large;
7191
const searchedInFields = [
7292
"space",
7393
"summary",
@@ -85,11 +105,11 @@ const fetchHome = id => {
85105
if (isSaunaAvailable) {
86106
const params = getParams();
87107
const homeUrl = `https://www.airbnb.cz/rooms/${id}`;
88-
const homeUrlWithDateInterval = params.checkin
89-
? `${homeUrl}?check_in=${params.checkin}&check_out=${
90-
params.checkout
91-
}`
92-
: homeUrl;
108+
const homeUrlWithDateInterval =
109+
params.checkin && params.checkout
110+
? `${homeUrl}?check_in=${params.checkin}&check_out=${params.checkout}`
111+
: homeUrl;
112+
saunaLinks = `${saunaLinks}<a href="${homeUrlWithDateInterval}" target="_blank"><div class="item" style="background-image: url('${photo}')"></div></a>`;
93113
console.log(`- ${homeUrlWithDateInterval}`);
94114
}
95115
}
@@ -101,32 +121,37 @@ const fetchHome = id => {
101121
return request;
102122
};
103123

104-
const fetchResultPage = (offset = 0) => {
105-
if (numberOfRequests > maximumOfRequests) return;
124+
const fetchResultPage = async (query = null, offset = 0) => {
125+
if (numberOfRequests > maximumOfRequests || query === null) return;
106126
const params = getParams(offset);
107-
const paramString = Object.keys(params)
108-
.map(key => `${key}=${encodeURIComponent(params[key])}`)
109-
.join("&");
110-
// console.log(`https://api.airbnb.com/v2/explore_tabs?${paramString}`);
111-
fetch(`https://api.airbnb.com/v2/explore_tabs?${paramString}`, {
112-
method: "get",
113-
headers: headers
114-
})
115-
.then(body => body.json().catch(e => null))
116-
.then(body => {
117-
if (body) {
118-
const ids = body.explore_tabs[0].home_tab_metadata.remarketing_ids;
127+
console.log(`Query: ${query}`);
128+
const paramString =
129+
Object.keys(params)
130+
.map(key => `${key}=${encodeURIComponent(params[key])}`)
131+
.join("&") + `&query=${query}`;
119132

120-
const saunasUrls = filterAsync(ids, fetchHome);
121-
if (body.explore_tabs[0].pagination_metadata.has_next_page) {
122-
fetchResultPage(offset + numberOfItemsOnOnePage);
123-
}
133+
console.log(`https://api.airbnb.com/v2/explore_tabs?${paramString}`);
134+
const request = await fetch(
135+
`https://api.airbnb.com/v2/explore_tabs?${paramString}`,
136+
{
137+
method: "get",
138+
headers: headers
139+
}
140+
);
141+
try {
142+
const body = await request.json();
143+
if (body) {
144+
const ids = body.explore_tabs[0].home_tab_metadata.remarketing_ids;
145+
const homePromise = await filterAsync(ids, fetchHome);
146+
if (body.explore_tabs[0].pagination_metadata.has_next_page) {
147+
await fetchResultPage(query, offset + numberOfItemsOnOnePage);
124148
}
125-
})
126-
.catch(e => {
127-
console.log(e);
128-
});
149+
}
150+
} catch (err) {
151+
console.log("error");
152+
}
153+
129154
numberOfRequests++;
130155
};
131-
console.log(`Saunas ${getParams().query}\n============================`);
156+
console.log(`Saunas\n============================`);
132157
fetchResultPage();

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
{
22
"dependencies": {
3-
"node-fetch": "^2.6.0"
3+
"ejs": "^2.7.1",
4+
"express": "^4.17.1",
5+
"node-fetch": "^2.6.0",
6+
"path": "^0.12.7"
47
},
58
"devDependencies": {
69
"nodemon": "^1.19.4"
710
},
811
"scripts": {
9-
"start": "nodemon --exec node index.js"
12+
"start": "nodemon --exec node index.js",
13+
"build": "node index.js"
1014
}
1115
}

public/index.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<html>
2+
<head>
3+
<title>Airbnb sauna finder</title>
4+
<style>
5+
body {
6+
font-family: sans-serif;
7+
}
8+
.container {
9+
display: flex;
10+
flex-direction: row;
11+
flex-wrap: wrap;
12+
}
13+
.item{
14+
width: 300px;
15+
height: 200px;
16+
margin-right: 10px;
17+
margin-bottom: 10px;
18+
background-size: cover;
19+
border-radius: 10px;
20+
}
21+
a:hover {
22+
opacity: 0.8;
23+
transition: all 0.1s ease;
24+
}
25+
input {
26+
border-radius: 5px;
27+
line-height: 30px;
28+
border: solid 1px #ccc;
29+
padding: 0 10px;
30+
}
31+
input[type="submit"]{
32+
background: #FF5A5F;
33+
border: solid 1px #FF5A5F;
34+
color: white;
35+
padding: 0px 30px;
36+
}
37+
</style>
38+
<meta name="viewport" content="width=device-width, initial-scale=1">
39+
</head>
40+
<body>
41+
<h1>Airbnb sauna finder</h1>
42+
<form method="GET">
43+
Location: <input type="text" name="query" />
44+
<input type="submit" value="Search" />
45+
</form><br>
46+
<div class="container"><%- links %></div>
47+
</body>
48+
</html>

public/index.js

Whitespace-only changes.

screenshot.jpg

378 KB
Loading

0 commit comments

Comments
 (0)