环境配置
在开始之前,让我们先确认一下是否备齐了必要的工具。
首先,从官方网站下载并安装 Node.js,确保使用长期支持 (LTS) 版本。这也将自动安装 Node Package Manager(NPM),我们将使用它来安装更多依赖项。
在本教程中,我们将使用 Visual Studio Code 作为集成开发环境 (IDE),但您也可以选择使用任何其他 IDE。为项目创建一个新文件夹,打开终端,运行以下命令建立一个新的 Node.js 项目:
npm init -y
这将在项目目录中创建package.json文件,其中将存储有关项目及其依赖项的信息。
接下来,我们需要安装 TypeScript 和 Node.js 的类型定义。TypeScript 提供可选的静态类型,有助于防止代码出错。为此,请在终端运行
npm install typescript @types/node --save-dev
您可以运行
npx tsc --version
TypeScript 使用名为tsconfig.json的配置文件来存储编译器选项和其他设置。要在项目中创建该文件,请运行以下命令:
npx tsc -init
请确保将“outDir”的值设置为“dist”。这样,我们就能将 TypeScript 文件与编译后的文件分开。有关此文件及其属性的更多信息,请参阅 TypeScript 官方文档。
现在,在项目中创建一个 "src"目录和一个新的 "index.ts"文件。我们将在这里保存刮擦代码。要执行 TypeScript 代码,必须先编译它,因此为了确保我们不会忘记这个额外的步骤,我们可以使用自定义命令。
前往 "package.json"文件,然后像这样编辑 "脚本"部分:
"scripts": {
"test": "npx tsc && node dist/index.js"
}
这样,在执行脚本时,只需在终端中输入 "npm run test"即可。
最后,为了从网站抓取数据,我们将使用 Puppeteer——这是一个适用于 Node.js 的无头浏览器库,允许您通过编程方式控制网页浏览器并与网站进行交互。要安装它,请在终端中运行以下命令:
npm install puppeteer
当你想确保数据的完整性时,强烈建议使用它,因为如今许多网站都包含动态生成的内容。如果你很好奇,可以在继续阅读Puppeteer 文档之前先查看一下它的功能。
数据位置
既然环境已经搭建完毕,我们可以开始探讨如何提取数据了。在本文中,我选择抓取一家位于都柏林的爱尔兰餐厅的网页:https://www.yelp.ie/biz/the-boxty-house-dublin?osq=Restaurants。
我们将提取以下数据:
- 餐厅名称;
- 餐厅评分;
- 该餐厅的评论数量;
- 企业网站;
- 公司电话号码;
- 该餐厅的实际地址。
您可以在下面的截图中看到所有这些信息:

打开每个元素上的 "开发工具",你就能看到我们用来定位 HTML 元素的 CSS 选择器。如果你对 CSS 选择器的工作原理还不太了解,请参考这本新手指南。
提取数据
在编写脚本之前,让我们验证一下 Puppeteer 的安装是否顺利:
import puppeteer from 'puppeteer';
async function scrapeYelpData(yelp_url: string): Promise<void> {
// Launch Puppeteer
const browser = await puppeteer.launch({
headless: false,
args: ['--start-maximized'],
defaultViewport: null
})
// Create a new page
const page = await browser.newPage()
// Navigate to the target URL
await page.goto(yelp_url)
// Close the browser
await browser.close()
}
scrapeYelpData("https://www.yelp.ie/biz/the-boxty-house-dublin?osq=Restaurants")
在此,我们将打开一个浏览器窗口,创建一个新页面,导航至目标 URL,然后关闭浏览器。为了简化操作并便于可视化调试,我以非无头模式全屏打开浏览器窗口。
现在,让我们来看看网站的结构:

Yelp 的页面结构似乎有些复杂,因为类名是随机生成的,而且极少有元素具有唯一的属性值。
不过别担心,我们可以想出一个有创意的解决方案。首先,为了获取餐厅名称,我们锁定页面上唯一的“h1”元素。
// Extract restaurant name
const restaurant_name = await page.evaluate(() => {
const name = document.querySelector('h1')
return name ? name.textContent : ''
})
console.log(restaurant_name)
现在,要获取餐厅评分,你可以注意到,除了星级图标之外,具体的数值还存在于“aria-label”属性中。因此,我们定位“aria-label”属性以“starrating”字符串结尾的“div”元素。
// Extract restaurant rating
const restaurant_rating = await page.evaluate(() => {
const rating = document.querySelector('div[aria-label$="star rating"]')
return rating ? rating.getAttribute('aria-label') : ''
})
console.log(restaurant_rating)
最后(就这一段HTML而言),我们可以看到,通过定位高亮显示的锚点元素,可以轻松获取评论编号。
// Extract restaurant reviews
const restaurant_reviews = await page.evaluate(() => {
const reviews = document.querySelector('a[href="#reviews"]')
return reviews ? reviews.textContent : ''
})
console.log(restaurant_reviews)
小菜一碟。让我们来看看商业信息小工具:

遗憾的是,在这种情况下,我们无法依赖 CSS 选择器。不过,我们还可以利用另一种方法来定位 HTML 元素:XPath。如果您对 CSS 选择器的运作原理还不太熟悉,欢迎查阅这篇入门指南。
要提取餐厅的网站地址,我们采用以下逻辑:
查找文本内容为“Business website”的“p”元素;
查找以下同级节点
查找锚点元素及其“href”属性。
// Extract restaurant website
const restaurant_website_element = await page.$x("//p[contains(text(), 'Business website')]/following-sibling::p/a/@href")
const restaurant_website = await page.evaluate(
element => element.nodeValue,
restaurant_website_element[0]
)
console.log(restaurant_website)
现在,对于电话号码和地址,我们可以采用完全相同的逻辑,但有两个例外:
- 对于电话号码,我们跳过下一个同级元素,并提取其textContent 属性 ;
- 对于该地址,我们针对父元素的以下同级元素。
// Extract restaurant phone number
const restaurant_phone_element = await page.$x("//p[contains(text(), 'Phone number')]/following-sibling::p")
const restaurant_phone = await page.evaluate(
element => element.textContent,
restaurant_phone_element[0]
)
console.log(restaurant_phone)
// Extract restaurant address
const restaurant_address_element = await page.$x("//a[contains(text(), 'Get Directions')]/parent::p/following-sibling::p")
const restaurant_address = await page.evaluate(
element => element.textContent,
restaurant_address_element[0]
)
console.log(restaurant_address)
最终结果应该是这样的
博克西之家
4.5 星评级
948 条评论
/biz_redir?url=http%3A%2F%2Fwww.boxtyhouse.ie%2F&cachebuster=1673542348&website_link_type=website&src_bizid=EoMjdtjMgm3sTv7dwmfHsg&s=16fbda8bbdc467c9f3896a2dcab12f2387c27793c70f0b739f349828e3eeecc3
(01) 677 2762
都柏林2区坦普尔巴20-21号绕过僵尸检测
虽然初看之下抓取Yelp似乎很简单,但随着项目规模的扩大,这一过程可能会变得更加复杂和具有挑战性。该网站采用了多种技术来检测和阻止自动化流量,因此当你扩大抓取规模时,抓取工具就会开始被封锁。
Yelp 会收集多种浏览器数据,以此生成并为您分配一个唯一的指纹。其中包括:
- Navigator 对象的属性(deviceMemory、hardwareConcurrency、platform、userAgent、webdriver 等)
- 时间和性能检查
- 服务 worker
- 屏幕尺寸检查
- 以及更多
要克服这些挑战并继续进行大规模数据抓取,一种方法是使用数据抓取 API。此类服务提供了一种简单可靠的方式,可从 yelp.com 等网站获取数据,而无需自行开发和维护数据抓取工具。
WebScrapingAPI 就是这样一款产品。它的代理旋转机制完全避免了验证码,其扩展知识库可以随机化浏览器数据,使其看起来像真实用户。
设置简单快捷。你只需注册一个账户,就会收到 API 密钥。您可以在仪表板上访问该密钥,它用于验证您发送的请求。

由于您已经设置了 Node.js 环境,我们可以使用相应的 SDK。运行以下命令将其添加到项目依赖项中:
npm install webscrapingapi
现在只需发送一个 GET 请求,我们就能收到网站的 HTML 文档。请注意,这并不是访问 API 的唯一方式。
import webScrapingApiClient from 'webscrapingapi';
const client = new webScrapingApiClient("YOUR_API_KEY");
async function exampleUsage() {
const api_params = {
'render_js': 1,
'proxy_type': 'residential',
}
const URL = "https://www.yelp.ie/biz/the-boxty-house-dublin?osq=Restaurants"
const response = await client.get(URL, api_params)
if (response.success) {
console.log(response.response.data)
} else {
console.log(response.error.response.data)
}
}
exampleUsage();
启用 "render_js "参数后,我们就可以使用无头浏览器发送请求,就像你之前在本教程中所做的那样。
结论
本文为您详细介绍了如何使用 TypeScript 和 Puppeteer 抓取 Yelp 网站数据。我们详细讲解了环境搭建、数据定位与提取的流程,并阐述了为何使用专业的抓取工具比自行开发更优。
从Yelp抓取的数据可用于多种用途,例如识别市场趋势、分析客户情绪、监控竞争对手、制定精准营销活动等。
总体而言,对于希望在本地市场获得竞争优势的人来说,抓取Yelp.com的数据是一项宝贵的资源,而本指南为此提供了绝佳的起点。




