← back to Sublease Agentabrams
crawl/firms/premierworkspaces.js
73 lines
'use strict';
// Premier Workspaces — flexible office / executive-suite operator (WordPress + JetEngine).
// Public location pages carry a LocalBusiness JSON-LD with a full PostalAddress; we parse
// that, geocode via the free Census API, and store each center as a flexible-space listing.
const LOCATIONS = 'https://premierworkspaces.com/locations/';
const titleCase = (s) => (s || '').replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
function parseJsonLd(html) {
const out = [];
for (const m of html.matchAll(/<script[^>]+application\/ld\+json[^>]*>([\s\S]*?)<\/script>/g)) {
try { const j = JSON.parse(m[1]); (j['@graph'] || [j]).forEach(n => out.push(n)); } catch { /* skip */ }
}
return out;
}
module.exports = {
meta: { firm: 'Premier Workspaces', space_type: 'flex' },
async crawl({ base, sponsor, upsert }) {
const home = await base.httpGet(LOCATIONS, { timeout: 20000 });
if (!home.ok) throw new Error('locations page ' + home.status);
// Individual center pages: /locations/<state>/<city>/<center>/
const links = [...new Set(
[...home.text.matchAll(/href="(https:\/\/premierworkspaces\.com\/locations\/[a-z0-9\-]+\/[a-z0-9\-]+\/[a-z0-9\-]+\/)"/g)]
.map(m => m[1]))].filter(u => !/\/view-all\//.test(u));
// Focus on California first (the LA market); env override to widen.
const onlyState = process.env.PW_STATE || 'california';
const scoped = links.filter(u => u.includes(`/locations/${onlyState}/`));
const cap = Number(process.env.PW_CAP || 30);
const targets = (scoped.length ? scoped : links).slice(0, cap);
let geocoded = 0;
for (const url of targets) {
const parts = url.split('/').filter(Boolean); // .. locations, state, city, center
const state = parts[parts.length - 3], city = titleCase(parts[parts.length - 2]);
const centerName = titleCase(parts[parts.length - 1]);
const page = await base.httpGet(url, { timeout: 18000 });
let address = null, zip = null, image = null, lat = null, lng = null;
if (page.ok) {
for (const node of parseJsonLd(page.text)) {
const addr = node.address;
if (addr && (addr.streetAddress || addr['@type'] === 'PostalAddress')) {
address = [addr.streetAddress, addr.addressLocality].filter(Boolean).join(', ');
zip = addr.postalCode || null;
}
if (node.geo) { lat = Number(node.geo.latitude) || null; lng = Number(node.geo.longitude) || null; }
if (!image && node.image) image = typeof node.image === 'string' ? node.image : node.image.url;
}
}
const full = [address, `${titleCase(state)}`, zip].filter(Boolean).join(', ');
if ((lat == null || lng == null) && (address || city)) {
const g = await base.geocodeCensus(full || `${city}, ${titleCase(state)}`);
if (g) { lat = g.lat; lng = g.lng; geocoded++; await base.sleep(300); }
}
await upsert({
external_id: url.replace('https://premierworkspaces.com', '').replace(/\/$/, ''),
title: `${centerName} — flexible office space`,
address: address || null, city, state: state === 'california' ? 'CA' : titleCase(state), zip,
lat, lng, space_type: 'flex', is_sublease: true,
size_sf: null, price_amount: null, price_unit: 'call for terms',
term: 'flexible (month-to-month & short-term)',
source_url: url, image_url: image,
description: `Premier Workspaces flexible workspace: private offices, coworking & meeting rooms at ${centerName}, ${city}.`,
raw: { center: centerName, city, state },
});
await base.sleep(700); // polite
}
return { cost: 0, notes: `${targets.length} centers scanned, ${geocoded} geocoded (Census, $0)` };
},
};