planbuild_fetch.js 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. // /app/planbuild_fetch.js
  2. const { chromium } = require('playwright');
  3. (async () => {
  4. const browser = await chromium.launch({ headless: true });
  5. const ctx = await browser.newContext({
  6. userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ' +
  7. '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
  8. locale: 'en-AU'
  9. });
  10. const page = await ctx.newPage();
  11. let printed = false;
  12. page.on('response', async (res) => {
  13. // capture the app’s own search response if/when it fires
  14. const u = res.url();
  15. if (u.endsWith('/insecure/advertisementsearch/api/advertisements/search') &&
  16. res.request().method() === 'POST') {
  17. try {
  18. const json = await res.json();
  19. console.log(JSON.stringify({source: 'api', items: json}));
  20. printed = true;
  21. } catch (_) {}
  22. }
  23. });
  24. await page.goto('https://portal.planbuild.tas.gov.au/insecure/advertisementsearch', { waitUntil: 'networkidle' });
  25. // Accept modal if present
  26. try { await page.click('#contBtn', { timeout: 1500 }); } catch (_) {}
  27. // Give the app a moment to run its own search
  28. await page.waitForTimeout(2000);
  29. if (!printed) {
  30. // If the app didn’t auto-search, ask it to (uses CSRF + recaptcha internally)
  31. await page.evaluate(() => {
  32. const header = document.querySelector('meta[name="_csrf_header"]')?.content || 'X-CSRF-TOKEN';
  33. const token = document.querySelector('meta[name="_csrf"]')?.content || '';
  34. fetch('/insecure/advertisementsearch/api/advertisements/search', {
  35. method: 'POST',
  36. headers: { 'Content-Type': 'application/json', [header]: token },
  37. body: JSON.stringify({ page: 0, size: 200, lgas: [] })
  38. }).catch(()=>{});
  39. });
  40. await page.waitForTimeout(2500);
  41. }
  42. if (!printed) {
  43. // As a fallback, scrape the rendered rows (if the app injected them)
  44. const rows = await page.$$eval('.advertisement-result-row', nodes =>
  45. nodes.map(n => ({
  46. id: n.id || '',
  47. addressString: (n.querySelector('.col-xs-8')?.innerText || '').trim(),
  48. name: (n.querySelector('.col-xs-4')?.innerText || '').trim()
  49. })).filter(x => x.name && x.addressString)
  50. );
  51. console.log(JSON.stringify({source: 'dom', items: rows}));
  52. }
  53. await browser.close();
  54. })();