要提取所有这些数据,我们需要先定位它们。右键点击高亮显示的区域,然后选择“检查”以打开开发者工具并查看 HTML 文档。将鼠标光标悬停在其上,即可轻松查看每个区域对应的具体部分:
在本教程中,我将使用 CSS 选择器,因为这是最直接的方法。如果您对这种方法不熟悉,不妨先阅读这篇通俗易懂的指南。
在开始编写脚本之前,让我们先验证一下 Puppeteer 是否安装成功:
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")
此处我们打开一个浏览器窗口,新建页面,导航至目标 URL,然后关闭浏览器。为了简化操作和便于可视化调试,我以非无头模式全屏打开浏览器。
由于每条房源信息都具有相同的结构和数据,在我们的算法中,我们将提取整个房源列表中的所有信息。在脚本结尾,我们将遍历所有结果,并将它们集中到一个列表中。
您可能已经注意到,在第一张截图中房源 URL 不可见,但在第二张截图中已标出并高亮显示。这是因为点击房源时,系统会将您重定向至该房源的 URL。
// 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)
我们通过筛选具有“data-testid”属性且值为“card-link”的锚点元素来定位 URL。随后将结果转换为 JavaScript 数组,并将每个元素映射到“href”属性的值。
不过,生成的列表中每个 URL 都会出现两次。这是因为每个房源在两个部分(房产图片和租赁详情)中都使用了相同的锚点元素。我们可以利用 Set 数据结构轻松解决这个问题:
const unique_listings_location = [...new Set(listings_location)]
console.log(unique_listings_location)
对于房源价格,我们将提取具有“data-testid”属性且值为“card-price”的“div”元素。同样需要将其转换为数组,然后映射到其文本内容。
// 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)
要获取浴室数量和房产面积,我们将使用操作符来处理直接子元素。这意味着父元素具有唯一标识,而子元素则使用更通用的 ID 或类名。除此之外,逻辑与之前相同:
// 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)
最后,对于房源的地址,我们筛选具有“data-testid”属性且其值为“card-address”的“div”元素。
// 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)
现在您应该有 5 个列表,每个列表对应我们抓取的一组数据。正如我之前提到的,我们应将它们合并为一个列表。这样,我们收集的信息将更容易进行后续处理。
// 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)
最终结果应类似如下:
[
{
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'
}
]