Um all diese Daten zu extrahieren, müssen wir sie zunächst lokalisieren. Klicken Sie mit der rechten Maustaste auf die hervorgehobenen Abschnitte und wählen Sie dann „Untersuchen“, um die Entwicklertools zu öffnen und das HTML-Dokument anzuzeigen. Wenn Sie den Mauszeiger darüber bewegen, können Sie leicht erkennen, welcher Teil jedem Abschnitt entspricht:
Für dieses Tutorial werde ich CSS-Selektoren verwenden, da sie die einfachste Option darstellen. Wenn diese Methode für Sie neu ist, können Sie sich gerne zuerst diese selbsterklärende Anleitung ansehen.
Bevor wir mit dem Schreiben unseres Skripts beginnen, überprüfen wir, ob die Installation von Puppeteer erfolgreich war:
import puppeteer from 'puppeteer';
async function scrapeRealtorData(realtor_url: string): Promise<void> {
// Launch Puppeteer
const browser = await puppeteer.launch({
headless: false,
args: ['--start-maximized'],
defaultViewport: null
})
const page = await browser.newPage()
// Navigate to the channel URL
await page.goto(realtor_url)
// Close the browser
await browser.close()
}
scrapeRealtorData("https://www.realtor.com/apartments/Plano_TX/beds-studio")
Hier öffnen wir ein Browserfenster, erstellen eine neue Seite, navigieren zu unserer Ziel-URL und schließen dann den Browser. Der Einfachheit und der visuellen Fehlerbehebung halber öffne ich den Browser im maximierten Modus im Nicht-Headless-Modus.
Da jede Anzeige die gleiche Struktur und die gleichen Daten aufweist, extrahieren wir in unserem Algorithmus alle Informationen für die gesamte Immobilienliste. Am Ende des Skripts durchlaufen wir alle Ergebnisse und fassen sie in einer einzigen Liste zusammen.
Vielleicht ist Ihnen aufgefallen, dass die URL des Eintrags im ersten Screenshot nicht sichtbar war, im zweiten jedoch erwähnt und hervorgehoben wurde. Das liegt daran, dass Sie zur URL der Immobilie weitergeleitet werden, wenn Sie darauf klicken.
// Extract listings location
const listings_location = await page.evaluate(() => {
const locations = document.querySelectorAll('a[data-testid="card-link"]')
const locations_array = Array.from(locations)
return locations ? locations_array.map(a => a.getAttribute('href')) : []
})
console.log(listings_location)
Wir lokalisieren die URL, indem wir die Ankerelemente auswählen, die das Attribut „data-testid“ mit dem Wert „card-link“ haben. Anschließend wandeln wir das Ergebnis in ein JavaScript-Array um und ordnen jedes Element dem Wert des Attributs „href“ zu.
Die resultierende Liste enthält jedoch jede URL zweimal. Das liegt daran, dass jedes Inserat dasselbe Ankerelement für zwei Abschnitte hat: die Immobilienbilder und die Mietdetails. Wir können dies ganz einfach beheben, indem wir die Datenstruktur „Set“ verwenden:
const unique_listings_location = [...new Set(listings_location)]
console.log(unique_listings_location)
Für den Immobilienpreis extrahieren wir die „div“-Elemente, die das Attribut „data-testid“ mit dem Wert „card-price“ haben. Auch diese müssen in ein Array konvertiert und dann ihrem Textinhalt zugeordnet werden.
// Extract listings price
const listings_price = await page.evaluate(() => {
const prices = document.querySelectorAll('div[data-testid="card-price"]')
const prices_array = Array.from(prices)
return prices ? prices_array.map(p => p.textContent) : []
})
console.log(listings_price)
Um die Anzahl der Badezimmer und die Wohnfläche zu ermitteln, verwenden wir den Operator für direkte untergeordnete Elemente. Das bedeutet, dass das übergeordnete Element eindeutig identifiziert wird, während das untergeordnete Element eine allgemeinere ID oder einen allgemeineren Klassennamen hat. Abgesehen davon ist die Logik dieselbe wie zuvor:
// Extract listings baths
const listings_baths = await page.evaluate(() => {
const baths = document.querySelectorAll('li[data-testid="property-meta-baths"] > span[data-testid="meta-value"]')
const baths_array = Array.from(baths)
return baths ? baths_array.map(b => b.textContent) : []
})
console.log(listings_baths)
// Extract listings sqft
const listings_sqft = await page.evaluate(() => {
const sqfts = document.querySelectorAll('li[data-testid="property-meta-sqft"] > span[data-testid="screen-reader-value"]')
const sqfts_array = Array.from(sqfts)
return sqfts ? sqfts_array.map(s => s.textContent) : []
})
console.log(listings_sqft)
Und schließlich wählen wir für die Adressen der Angebote die „div“-Elemente aus, deren „data-testid“-Attribut auf den Wert „card-address“ gesetzt ist.
// Extract listings address
const listings_address = await page.evaluate(() => {
const addresses = document.querySelectorAll('div[data-testid="card-address"]')
const addresses_array = Array.from(addresses)
return addresses ? addresses_array.map(a => a.textContent) : []
})
console.log(listings_address)
Nun sollten Sie 5 Listen haben, eine für jede von uns gescrapte Datenangabe. Wie bereits erwähnt, sollten wir diese in einer einzigen Liste zusammenfassen. Auf diese Weise lassen sich die gesammelten Informationen viel einfacher weiterverarbeiten.
// Group the lists
const listings = []
for (let i = 0; i < unique_listings_location.length; i++) {
listings.push({
url: unique_listings_location[i],
price: listings_price[i],
baths: listings_baths[i],
sqft: listings_sqft[i],
address: listings_address[i]
})
}
console.log(listings)
Das Endergebnis sollte in etwa so aussehen:
[
{
url: '/realestateandhomes-detail/1009-14th-St-Apt-410_Plano_TX_75074_M92713-98757',
price: '$1,349',
baths: '1',
sqft: '602 square feet',
address: '1009 14th St Apt 410Plano, TX 75074'
},
{
url: '/realestateandhomes-detail/1009-14th-St-Apt-1_Plano_TX_75074_M95483-11211',
price: '$1,616',
baths: '1',
sqft: '604 square feet',
address: '1009 14th St Apt 1Plano, TX 75074'
},
{
url: '/realestateandhomes-detail/1009-14th-St_Plano_TX_75074_M87662-45547',
price: '$1,605 - $2,565',
baths: '1 - 2',
sqft: '602 - 1,297 square feet',
address: '1009 14th StPlano, TX 75074'
},
{
url: '/realestateandhomes-detail/5765-Bozeman-Dr_Plano_TX_75024_M70427-45476',
price: '$1,262 - $2,345',
baths: '1 - 2',
sqft: '352 - 1,588 square feet',
address: '5765 Bozeman DrPlano, TX 75024'
},
{
url: '/realestateandhomes-detail/1410-K-Ave-Ste-1105A_Plano_TX_75074_M97140-46163',
price: '$1,250 - $1,995',
baths: '1 - 2',
sqft: '497 - 1,324 square feet',
address: '1410 K Ave Ste 1105APlano, TX 75074'
}
]