Activepieces + Scrapeless: 无代码本地潜在客户流程
Web Data Collection Specialist
TL;DR:
- Activepieces通过其内置的HTTP组件获取实时搜索数据,因此一个潜在客户流程无需自定义组件、已发布的包或托管服务。
- Google搜索演员每次请求返回约20个本地企业——捕获落在20、21和22上——因此本地流程通常只需要一次调用加上去重键,而不是分页循环。
- 解析后的响应位于
body下,这使得{{step_1.body.local_results.places}}成为工作参考;去掉body后,循环在仍报告成功的流程中运行零次。 phone字段在约一半的记录中包含电话号码——其余记录提供营业时间或服务标签——因此代码组件必须验证它,而不仅仅是修剪它。- 将API密钥存储为项目级值,而不是输入到头字段中,因为流程会被导出和共享。
What This Flow Gives You
一个有效的Activepieces流程,将类别和城市转换为本地企业的行——名称、类别、评级、评论数量和电话——准备好用于CRM、表格或数据库。
Activepieces很好地协调应用程序,并且不获取页面。搜索结果来自Deep SerpApi,通过其scraper.google.search演员返回解析的本地包作为JSON。下面的流程是在自托管的Activepieces 0.82.0实例上构建的,使用piece-http 0.11.18。
Prerequisites
- 一台Activepieces实例,云端或自托管
- 一个Scrapeless API密钥 — 创建一个免费帐户
- 行的目标组件:Google Sheets、Airtable、Postgres或你的CRM
将密钥放在项目级值或连接中,而不是在步骤的头字段中。流程定义包含其字面字段值,且流程会在项目之间导出、复制和共享。
Configure the HTTP Step
添加 HTTP → 发送HTTP请求 并填写五个字段:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.scrapeless.com/api/v1/scraper/request |
| Headers | x-api-token → 你的密钥 |
| Body type | JSON |
| Body | 下面的对象 |
json
{
"actor": "scraper.google.search",
"input": {
"q": "plumbers in Austin, TX",
"tbm": "lcl"
}
}
tbm设置为lcl是返回企业而不是网页的内容。查询需要本地意图:"plumbers in Austin, TX"返回一个本地包,而裸"plumbing"通常不这样。
在连接其余流程之前,确认构建器外部的请求。来自Shell的相同调用告诉你一个空结果是查询的错误还是流程的错误:
bash
curl -sS -X POST https://api.scrapeless.com/api/v1/scraper/request \
-H 'Content-Type: application/json' \
-H "x-api-token: $SCRAPELESS_API_KEY" \
-d '{"actor":"scraper.google.search","input":{"q":"plumbers in Austin, TX","tbm":"lcl"}}' \
| python3 -c 'import json, sys
data = json.load(sys.stdin)
places = (data.get("local_results") or {}).get("places") or []
print(len(places), "places")
if places:
print("raw phone:", repr(places[0]["phone"]), "| raw type:", repr(places[0]["type"]))
else:
print("no local pack in this response; top-level keys were", sorted(data))'
这将打印地点计数和第一个记录的原始phone和type——这两个字段是填充的。阅读else分支上的键而不是直接索引local_results是流程所需的同样习惯:检查你期望的形状是否存在,然后再进行相应的访问。
What Comes Back
成功运行返回约20个地点在local_results.places下,每个地点携带title、type、rating、reviews、phone和address。捕获落在同一查询的20、21和22地点上,因此将页面大小视为近似而非固定:将"start": 20添加到input对象中以获取下一页,并根据名称加电话进行去重,而不是假定确切的页面边界。
将tbm设置为lcl,信封为local_results、metadata、pagination和search_information——没有organic_results也没有related_searches。被拒绝的调用返回一个携带code和message的信封,而不是任何结果,这就是为什么流程应基于local_results的存在而不是仅仅HTTP状态进行分支。
参考路径是值得正确得到的部分。在Activepieces中,解析的JSON位于body下:
| Reference | Result |
|---|---|
{{step_1.body.local_results.places}} |
地点的数组 |
{{step_1.body.organic_results}} |
网络结果,当tbm被省略时 |
{{step_1.organic_results}} |
无 — 没有错误,没有警告 |
最后一行是昂贵的。如果缺少body段的参考解析为无,则项目循环步骤迭代零次,且运行仍然完成为成功。空的目标表看起来与查询返回无或参考错误相同,所以先检查路径。
在{{step_1.body.local_results.places}}上添加项目循环,使每个企业被当作自己的项目处理,而不是写入单个单元格的一个大块。
在免费计划上构建这个足以获得完整的本地包响应 - 从 Scrapeless 账户开始,并将密钥保存在项目值中。
存储前的标准化
两个行为决定你的行是否可用,而第二个是生成潜在客户流需要代码的原因。
字符串到达时被填充。 phone、type 和 hours 都带有前导空格 - " Plumber"、" (512) 690-4935"。这并不是外观问题:用作去重键的填充电话号码会在下一次运行时为同一企业创建第二条记录,而 "Plumber" 上的类别过滤器则匹配不到任何内容。 ITU-T 编号计划建议 是在电话号码成为标识符之前将其标准化为规范格式的原因。
几个字段存在但没有可用内容。 在同一捕获中,place_id、thumbnail 和 lsig 在所有 20 条记录中都是空的。gps_coordinates 是陷阱:它以 {"latitude": 0, "longitude": 0} 的形式存在,因此真实性检查通过,而映射步骤将每个企业绘制在赤道上的同一点上。取 address 的位置,并将坐标对视为缺失,除非两个值都是非零的。
phone 字段并不总是电话号码。 在 "plumbers in Austin, TX" 的 20 个位置捕获中,只有 11 条记录包含类似电话号码的值。其他九条持有的开启时间文本,如 " Closes 6 PM " 或服务标签,如 "Online estimates"。将该字段直接映射到 CRM 列中,几乎一半的行变得不可用,流程中没有任何错误。这些小时字符串中的某些还包含狭义的不换行空格 (U+202F),而不是普通空格,因此,即使在修剪后," " 的天真拆分也会表现得意外。
验证字段而不是信任它的名称,并保留被丢弃的文本而不是直接删除:
在请求和目标之间添加一个 代码 组件。 Activepieces 将主体包装为 export const code = async (inputs) => { … };内部逻辑是普通的 JavaScript:
javascript
// `phone` sometimes carries opening hours or a service label instead of a number,
// so the value is validated before it becomes a contact field.
const PHONE = /\(?\d{3}\)?[ -]?\d{3}-?\d{4}/;
const code = async (inputs) => {
const clean = (value) => (typeof value === 'string' ? value.trim() : value);
const places = inputs.response?.local_results?.places ?? [];
return places.map((place) => {
const contact = clean(place.phone) ?? '';
const isPhone = PHONE.test(contact);
return {
name: clean(place.title),
category: clean(place.type),
rating: place.rating ?? null,
reviews: place.reviews ?? 0,
phone: isPhone ? contact : null,
phone_field_note: isPhone ? null : contact,
address_snippet: clean(place.address),
};
});
};
const sample = {
response: {
local_results: {
places: [
{
title: 'Radiant Plumbing, Air Conditioning, & Electrical',
type: ' Plumber',
rating: 4.8,
reviews: 18000,
phone: ' (512) 690-4935',
address: '25+ years in business \u00b7 Austin, TX',
},
{
title: 'Beyond Wow Plumbing & Drains',
type: ' Plumber',
rating: 4.9,
phone: ' Closes 6\u202fPM ',
address: 'Austin, TX',
},
],
},
},
};
code(sample).then((rows) => console.log(JSON.stringify(rows, null, 2)));
将 {{step_1.body}} 传入组件的 response 输入。这里有两个细节比较重要。?? 0 默认值存在,因为没有评论的企业根本没有 reviews 密钥,而数字目的地列拒绝 undefined,同时接受 0。而 phone_field_note 保留了在其不是数字时占据该字段的内容,因此操作人员可以看到一行包含开启时间,而不是缺失的电话号码。
评分和评论数量是两个值得保留为数字的字段。其他都是文本,如果流程以电子表格导出而不是数据库结束,则 逗号分隔值格式规范 决定了包含逗号的企业名称如何成功往返。
负责任地处理商业联系数据
此流程收集企业联系详细信息,因此附带一些义务。仅从公共搜索结果中收集,仅收集工作流程所需的字段。保持存储联系数据的合法基础,并尊重选择退出请求,因为企业电话号码仍然可以将个体经营者识别为个人 - 通用数据保护条例 在商业背景中也适用于个人数据,其他法域也存在等效规则。尊重每个平台对导入联系人的条款,设置保留期限而不是无限期保持记录,并遵循您所联系国家的营销同意规则。这些都不是法律建议;在进行外展之前,请检查您自己的义务。
结论
三个部分构成整个流程:HTTP 召唤参与者,代码修剪和默认字段,循环项目为每个企业写一行。需要关注的失败模式在引用路径中:丢弃 body,成功运行会写入一个空表。
从这里开始,替换查询为城市列表,流程便变成了一个区域构建。Make 集成演练 涵盖了来自另一个无代码构建器的相同请求,而 Dify 监控构建 显示了同一参与者的代理驱动版本。
准备好构建它了吗?查看Deep SerpApi 文档以获取完整的参数集,比较计划和所含数量,并开始免费计划。
常见问题
问:我需要自定义 Activepieces 组件才能使用 Scrapeless 吗?
不需要。内置的 HTTP 组件覆盖了每个参与者,因为 API 接受一个单一的 POST 和一个 actor 字段以及一个 input 对象。自定义组件只有在您想要带有类型字段的品牌步骤时才有帮助,以便团队不应看到原始请求,这是一个包装决策,而不是功能问题。
问:当 HTTP 步骤成功时,我的循环项步骤为什么迭代零次?
解析的响应嵌套在 body 下,因此 {{step_1.local_results.places}} 解析为空,而 {{step_1.body.local_results.places}} 解析为数组。缺少的引用不会引发错误,因此流报告成功,但循环为空。在调查查询之前,请检查引用路径。
问:一个请求返回多少结果,我该如何获取更多?
本地包请求返回大约 20 个地点;对一个查询的重复捕获返回 20、21 和 22。在 input 对象中添加 "start": 20 以获取下一页,再添加 "start": 40 以获取之后的页,依此类推。因为页面大小并不是严格固定的,所以按名称和电话去重复,而不是信任偏移量进行对齐,并将短的最后一页视为集合的结束。
问:为什么 place_id 和 gps_coordinates 在本地结果中不可用?
place_id、thumbnail 和 lsig 在每个本地包记录中都返回空,因此需要 place_id 的流会丢弃所有 20 个结果。gps_coordinates 的表现有所不同且风险更大:它被填充了 {"latitude": 0, "longitude": 0},在指向每个业务的同一坐标时存活于空值检查中。使用 address 进行定位,只有当两个数字都非零时才能信任坐标对。
问:API 密钥应该放在哪里在 Activepieces 流中?
在项目级值或连接中,从标题字段引用。流定义携带文字字段值并在项目之间导出和复制,因此直接输入步骤中的密钥会随每个副本一起传递。
问:这个流可以按照计划运行而不是通过 webhook 吗?
可以。将触发器更换为 计划,其余流保持不变,这通常是区域更新的常规形状。保持运行频率与本地排名实际变化的频率相匹配;对于大多数类别来说,每天一次就足够了,而较慢的节奏可以保持数量的可预测性。
问:同样的流是否适用于网页结果而不是企业?
是的。从 input 对象中删除 tbm,结果将以 {{step_1.body.organic_results}} 的形式返回,每个结果都有 title、link 和 snippet。代码组件需要更新其路径以匹配,网页结果上不需要修剪。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



