couple_matters/tools/xiaozhu_click_urls.js
StillHammer 92c2a9f022 Sync couple_matters: December crisis, separation agreement, daily check v2, xiaozhu search
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>
2025-12-23 06:54:08 +08:00

220 lines
6.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const puppeteer = require('puppeteer');
const fs = require('fs');
/**
* Xiaozhu Click URLs - Based on working xiaozhu_fixed.js
* Clicks each listing to extract URLs
*/
const CONFIG = {
city: '上海',
searchQuery: '交通大学',
latitude: 31.1880,
longitude: 121.4367,
maxListings: 10
};
console.log('🔗 Xiaozhu URL Extractor (Click Method)');
console.log(`📍 Search: ${CONFIG.searchQuery}`);
console.log(`🎯 Will click up to ${CONFIG.maxListings} listings\n`);
async function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function extractURLs() {
const browser = await puppeteer.launch({
headless: "new",
defaultViewport: { width: 414, height: 896 },
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');
const context = browser.defaultBrowserContext();
await context.overridePermissions('https://minsu.xiaozhu.com', ['geolocation']);
await page.setGeolocation({ latitude: CONFIG.latitude, longitude: CONFIG.longitude, accuracy: 100 });
console.log('🌍 Geolocation set to Shanghai Xujiahui\n');
try {
// Navigate and search (same as xiaozhu_fixed.js)
console.log('🌐 Loading homepage...');
await page.goto('https://minsu.xiaozhu.com/', { waitUntil: 'networkidle2', timeout: 30000 });
await wait(3000);
const searchInput = await page.$('input[type="text"]');
if (searchInput) {
console.log('⌨️ Typing search query...');
await searchInput.click();
await wait(500);
await page.keyboard.down('Control');
await page.keyboard.press('A');
await page.keyboard.up('Control');
await page.keyboard.press('Backspace');
await searchInput.type(CONFIG.searchQuery, { delay: 150 });
await wait(2000);
console.log('👆 Clicking suggestion...');
const clicked = await page.evaluate((searchQuery, keyword) => {
const allElements = document.querySelectorAll('div, li, a, span');
const matchingElements = [];
for (const el of allElements) {
const text = el.textContent.trim();
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
if (text.includes(keyword)) {
matchingElements.push({ el, text, score: 100 });
} else if (text.includes(searchQuery)) {
matchingElements.push({ el, text, score: 80 });
} else if (text.includes('上海') && text.length < 15) {
matchingElements.push({ el, text, score: 30 });
}
}
}
matchingElements.sort((a, b) => b.score - a.score);
if (matchingElements.length > 0) {
console.log(`Clicking: "${matchingElements[0].text}"`);
matchingElements[0].el.click();
return true;
}
return false;
}, CONFIG.searchQuery, CONFIG.searchQuery);
if (clicked) {
console.log('✅ Clicked suggestion\n');
await wait(4000);
} else {
console.log('⚠️ No suggestion, pressing Enter\n');
await page.keyboard.press('Enter');
await wait(3000);
}
}
// Scroll to load listings
console.log('⏬ Scrolling to load all listings...');
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await wait(5000);
// Get listing count
const listingCount = await page.evaluate(() => {
const items = document.querySelectorAll('.list-item');
return items.length;
});
console.log(`📊 Found ${listingCount} listings\n`);
if (listingCount === 0) {
console.log('❌ No listings found');
await page.screenshot({ path: './xiaozhu_click_no_listings.png' });
await browser.close();
return;
}
const results = [];
// Click each listing to get URL
for (let i = 0; i < Math.min(listingCount, CONFIG.maxListings); i++) {
console.log(`🔍 Listing ${i + 1}/${Math.min(listingCount, CONFIG.maxListings)}...`);
try {
// Extract info before clicking
const info = await page.evaluate((index) => {
const items = document.querySelectorAll('.list-item');
const item = items[index];
if (!item) return null;
return {
title: item.querySelector('.list-title')?.textContent.trim() || 'No title',
price: item.querySelector('.list-price')?.textContent.trim() || 'No price',
priceNum: parseInt(item.querySelector('.list-price')?.textContent.match(/\d+/)?.[0] || '0'),
image: item.querySelector('img')?.src
};
}, i);
if (!info) {
console.log(' ⚠️ Could not extract info\n');
continue;
}
console.log(` 📝 ${info.title.substring(0, 60)}...`);
console.log(` 💰 ${info.price}`);
// Click it!
await page.evaluate((index) => {
const items = document.querySelectorAll('.list-item');
const item = items[index];
if (item) item.click();
}, i);
console.log(' 👆 Clicked, waiting for page load...');
await wait(5000); // Wait for new page to load
// Get URL
const url = page.url();
console.log(` 🔗 URL: ${url}`);
results.push({
index: i + 1,
...info,
url: url
});
// Go back to list
console.log(' ⬅️ Going back...');
await page.goBack({ waitUntil: 'networkidle2', timeout: 15000 });
await wait(2000);
console.log('');
} catch (err) {
console.log(` ❌ Error: ${err.message}`);
// Try to recover
try {
const currentUrl = page.url();
if (!currentUrl.includes('minsu.xiaozhu.com/')) {
console.log(' 🔄 Reloading list page...');
await page.goBack();
await wait(3000);
}
} catch (e) {
console.log(' ⚠️ Recovery failed, continuing...\n');
}
}
}
// Save results
const outputFile = './xiaozhu_urls.json';
fs.writeFileSync(outputFile, JSON.stringify(results, null, 2));
console.log(`\n💾 Saved ${results.length} URLs to ${outputFile}\n`);
// Print summary
console.log('=' .repeat(70));
console.log('📋 EXTRACTED URLS:\n');
results.forEach((r) => {
console.log(`${r.index}. ${r.title?.substring(0, 70)}`);
console.log(` 💰 ${r.price}${r.priceNum}/day × 29 days = ¥${r.priceNum * 29})`);
console.log(` 🔗 ${r.url}`);
console.log('');
});
console.log('=' .repeat(70));
} catch (err) {
console.error('❌ Fatal error:', err.message);
await page.screenshot({ path: './xiaozhu_click_error.png' });
} finally {
await browser.close();
}
}
extractURLs().catch(console.error);