Major updates: - December 2025 crisis documentation and separation agreement - Daily check system v2 with multiple card categories - Xiaozhu rental search tools and results - Exit plan documentation - Message drafts for family communication - Confluent moved to CONSTANT - Updated profiles and promises 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
155 lines
5.6 KiB
JavaScript
155 lines
5.6 KiB
JavaScript
const puppeteer = require('puppeteer');
|
||
|
||
const url = 'https://minsu.xiaozhu.com/detail?luId=354701406371854&startDate=2025-12-21&endDate=2025-12-22';
|
||
|
||
console.log('📋 Extracting conditions/rules from listing...\n');
|
||
|
||
(async () => {
|
||
const browser = await puppeteer.launch({
|
||
headless: "new",
|
||
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
||
});
|
||
|
||
const page = await browser.newPage();
|
||
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15');
|
||
|
||
try {
|
||
console.log('🌐 Loading page...');
|
||
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||
|
||
// Click on "须知" (rules) tab if it exists
|
||
await page.evaluate(() => {
|
||
const buttons = Array.from(document.querySelectorAll('button, div, span'));
|
||
const rulesButton = buttons.find(b => b.textContent.includes('须知'));
|
||
if (rulesButton) rulesButton.click();
|
||
});
|
||
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
|
||
const info = await page.evaluate(() => {
|
||
const result = {
|
||
houseRules: [],
|
||
checkInOut: '',
|
||
deposit: '',
|
||
restrictions: [],
|
||
specialNotes: [],
|
||
description: '',
|
||
fullText: document.body.textContent
|
||
};
|
||
|
||
const text = document.body.textContent;
|
||
|
||
// Check-in/out times
|
||
const checkInMatch = text.match(/入住[时時]间[::]\s*([^\n]{5,30})/);
|
||
if (checkInMatch) result.checkInOut = checkInMatch[1].trim();
|
||
|
||
const checkOutMatch = text.match(/退房[时時]间[::]\s*([^\n]{5,30})/);
|
||
if (checkOutMatch) result.checkInOut += ' | 退房: ' + checkOutMatch[1].trim();
|
||
|
||
// Deposit
|
||
const depositMatch = text.match(/押金[::]\s*([^\n]{3,30})/);
|
||
if (depositMatch) result.deposit = depositMatch[1].trim();
|
||
|
||
// Look for rules sections
|
||
const elements = document.querySelectorAll('div, p, li, span');
|
||
elements.forEach(el => {
|
||
const elText = el.textContent.trim();
|
||
|
||
// House rules
|
||
if (elText.includes('房屋守则') || elText.includes('入住须知')) {
|
||
if (elText.length < 200) {
|
||
result.houseRules.push(elText);
|
||
}
|
||
}
|
||
|
||
// Restrictions
|
||
if ((elText.includes('不允许') || elText.includes('禁止') || elText.includes('不可'))
|
||
&& elText.length < 100) {
|
||
result.restrictions.push(elText);
|
||
}
|
||
|
||
// Special conditions (男士/女士/夫妻等)
|
||
if ((elText.includes('男士') || elText.includes('女士') || elText.includes('夫妻') || elText.includes('确认'))
|
||
&& elText.length < 150 && elText.length > 5) {
|
||
result.specialNotes.push(elText);
|
||
}
|
||
|
||
// Description snippets
|
||
if (elText.includes('房源介绍') && elText.length > 50) {
|
||
result.description = elText.substring(0, 500);
|
||
}
|
||
});
|
||
|
||
return result;
|
||
});
|
||
|
||
console.log('📋 CONDITIONS & RULES:\n');
|
||
|
||
console.log('⏰ CHECK-IN/OUT:');
|
||
console.log(info.checkInOut || ' Not specified in extracted text');
|
||
|
||
console.log('\n💰 DEPOSIT:');
|
||
console.log(info.deposit || ' Not found - need to ask landlord');
|
||
|
||
if (info.restrictions.length > 0) {
|
||
console.log('\n⚠️ RESTRICTIONS:');
|
||
[...new Set(info.restrictions)].slice(0, 5).forEach(r => console.log(` - ${r}`));
|
||
}
|
||
|
||
if (info.specialNotes.length > 0) {
|
||
console.log('\n📌 SPECIAL NOTES:');
|
||
[...new Set(info.specialNotes)].slice(0, 5).forEach(n => console.log(` - ${n}`));
|
||
}
|
||
|
||
if (info.houseRules.length > 0) {
|
||
console.log('\n📜 HOUSE RULES:');
|
||
[...new Set(info.houseRules)].slice(0, 3).forEach(r => console.log(` - ${r}`));
|
||
}
|
||
|
||
// Extract key info from description
|
||
console.log('\n\n📄 KEY INFO FROM DESCRIPTION:\n');
|
||
|
||
const keyInfo = [];
|
||
|
||
if (info.fullText.includes('单身男士优先')) {
|
||
keyInfo.push('⚠️ Preference: Single men (单身男士优先)');
|
||
}
|
||
if (info.fullText.includes('女士或夫妻需和房东再确认')) {
|
||
keyInfo.push('⚠️ Women/couples need landlord confirmation (女士或夫妻需和房东再确认)');
|
||
}
|
||
if (info.fullText.includes('可做饭')) {
|
||
keyInfo.push('✅ Cooking allowed (可做饭)');
|
||
}
|
||
if (info.fullText.includes('可带宠物')) {
|
||
keyInfo.push('✅ Pets allowed (可带宠物)');
|
||
}
|
||
if (info.fullText.includes('有停车位')) {
|
||
keyInfo.push('✅ Parking available (有停车位)');
|
||
}
|
||
if (info.fullText.includes('立即确认')) {
|
||
keyInfo.push('✅ Instant booking (立即确认)');
|
||
}
|
||
if (info.fullText.includes('民用燃气')) {
|
||
keyInfo.push('✅ Gas stove/cooking (民用燃气)');
|
||
}
|
||
if (info.fullText.includes('独立卫生间')) {
|
||
keyInfo.push('ℹ️ Private bathroom available (extra cost - 独立卫生间套房需加价)');
|
||
}
|
||
|
||
keyInfo.forEach(k => console.log(` ${k}`));
|
||
|
||
console.log('\n\n🏠 ROOM TYPE:');
|
||
if (info.fullText.includes('单间')) console.log(' Private room (单间) - NOT entire apartment');
|
||
if (info.fullText.includes('15m²')) console.log(' Size: 15m²');
|
||
if (info.fullText.includes('3间卧室1厅2卫1厨')) console.log(' Shared apartment: 3 bedrooms, 1 living room, 2 bathrooms, 1 kitchen');
|
||
if (info.fullText.includes('1床2人')) console.log(' 1 bed, sleeps 2 people');
|
||
if (info.fullText.includes('加客30元/人')) console.log(' Extra guest: +¥30/person');
|
||
|
||
} catch (err) {
|
||
console.error('❌ Error:', err.message);
|
||
} finally {
|
||
await browser.close();
|
||
}
|
||
})();
|