将被刮擦
完整代码
如果您不需要解释,请看一下full code example in the online IDE
import dotenv from "dotenv";
import { config, getJson } from "serpapi";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
dotenv.config();
const rl = readline.createInterface({ input, output });
config.api_key = process.env.API_KEY; //your API key from serpapi.com
const engine = "home_depot"; // search engine
const resultsLimit = 40; // hardcoded limit for demonstration purpose
const params = {
q: "inverter generatot", // Parameter defines the search query
page: 1, // Value is used to get the items on a specific page
};
const getSearchRefinement = async () => {
const preResults = await getJson(engine, params);
let fixedQuery;
if (preResults?.search_information?.spelling_fix) {
fixedQuery = preResults.search_information.spelling_fix;
}
return { query: fixedQuery, filters: preResults.filters };
};
const applyNewSearchParams = async ({ query, filters }) => {
if (query) {
const answer = await rl.question(`Do you want to change the search query from "${params.q}" to "${query}"? y/n: `);
if (answer.toLowerCase() === "y") {
params.q = query;
console.log(`Now the search query is: "${query}"`);
} else {
console.log(`The search query didn't change`);
}
}
if (filters) {
const appliedFilters = [];
let tokens = "";
for (const filter of filters) {
const answer = await rl.question(`Do you want to apply some filter from "${filter.key}" category? y/n: `);
if (answer.toLowerCase() === "y") {
for (const filterValue of filter.value) {
const answer = await rl.question(`Do you want to apply "${filterValue.name}" filter from "${filter.key}" category? y/n: `);
if (answer.toLowerCase() === "y") {
tokens += `${filterValue.value},`;
appliedFilters.push(`${filter.key}: ${filterValue.name}`);
}
}
}
}
rl.close();
if (tokens) {
params.hd_filter_tokens = tokens.slice(0, -1);
}
}
};
const getResults = async () => {
const results = [];
while (true) {
const json = await getJson(engine, params);
if (json.products) {
results.push(...json.products);
params.page += 1;
} else break;
if (results.length >= resultsLimit) break;
}
return results;
};
getSearchRefinement()
.then(applyNewSearchParams)
.then(getResults)
.then((result) => console.dir(result, { depth: null }));
为什么要从serpapi?
使用The Home Depot API使用API通常可以解决在创建自己的解析器或爬网时可能会遇到的所有或大多数问题。从网络剪接的角度来看,我们的API可以帮助解决最痛苦的问题:
- 通过求解验证码或IP块,来自受支持的搜索引擎的旁路块。
- 无需从头开始创建解析器并维护它。
- 支付代理和验证码求解器。
- 如果需要更快地提取数据,则不需要使用浏览器自动化。
前往Playground进行现场互动演示。
准备
首先,我们需要创建一个node.js* project并添加koude0 packages koude1和koude2。
为此,在我们项目的目录中,打开命令行并输入:
$ npm init -y
,然后:
$ npm i serpapi dotenv
*如果您没有安装node.js,则可以download it from nodejs.org并遵循安装documentation。
-
SERPAPI软件包用于使用SERPAPI刮擦和解析搜索引擎结果。从Google,Bing,Bing,Yandex,Yahoo,Home Depot,eBay等获取搜索结果。
-
dotenv软件包是一个零依赖性模块,将环境变量从
.env
文件加载到process.env
。
接下来,我们需要在我们的package.json
文件中添加一个带有“模块”值的顶级“类型”字段,以允许using ES6 modules in Node.JS:
目前,我们完成了项目的设置node.js环境,然后转到分步代码说明。
代码说明
First, we need to import dotenv
from koude2 library, config
and getJson
from koude1 library, readline
from koude11 Node.js built-in library, stdin
and stdout
(declare them as input
and output
) from koude17 Node.js built-在图书馆:
import dotenv from "dotenv";
import { config, getJson } from "serpapi";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
然后,我们应用一些配置。调用dotenv
koude19方法,调用带有参数input
和output
的readline
koude21方法,然后将其设置为rl
常数,将您的serpapi私有API设置为global koude8对象。
dotenv.config();
const rl = readline.createInterface({ input, output });
config.api_key = process.env.API_KEY; //your API key from serpapi.com
-
dotenv.config()
将读取您的.env
文件,解析内容,分配给process.env
,并用parsed
键返回包含已加载内容或error
键的对象。 。
-
readline.createInterface()
创建了一个新的readlinePromises.Interface
实例。一旦创建了readlinePromises.Interface
实例,最常见的情况就是聆听'line'
事件。 -
config.api_key
您通过修改配置对象来声明全局api_key
值。
接下来,我们编写必要的搜索参数。我们定义搜索engine
,我们想要接收多少结果(resultsLimit
常数)和带有q
和page
参数的params
对象用于提出请求:
ð注意:我在搜索查询中特别犯了一个错误,以演示The Home Depot Spell Check API的工作方式。
const engine = "home_depot"; // search engine
const resultsLimit = 40; // hardcoded limit for demonstration purpose
const params = {
q: "inverter generatot", // Parameter defines the search query
page: 1, // Value is used to get the items on a specific page
};
您可以使用下一个搜索参数:
-
q
参数定义搜索查询。您可以使用常规的Home Depot搜索中使用的任何东西。 -
hd_sort
参数定义了以不同选项排序的结果。它可以设置为:top_sellers
(畅销书),price_low_to_high
(价格低到高),price_high_to_low
(价格高至低),top_rated
(最高评分),best_match
(最佳匹配)。 -
hd_filter_tokens
过去可以通过逗号划分的过滤器令牌。 Filter tokens可以从API响应中获得。 -
delivery_zip
邮政编码。通过选定的区域过滤运输产品。 -
store_id
商店ID仅通过特定商店过滤产品。 -
lowerbound
定义了美元的下限。 -
upperbound
定义了上价的上限。 -
nao
定义了产品结果的偏移。单页包含24
产品。第一页偏移量为0
,第二->24
,第三->48
等。 -
page
值用于将项目在特定页面上获取。 (例如,1
(默认)是结果的第一页,2
是结果的第二页,3
是结果的第三页,等等)。 。
-
ps
确定每个页面的项目数。在某些情况下,Home Depot覆盖了PS值。默认情况下,Home Depot返回24
结果。 -
no_cache
参数将迫使Serpapi获取App Store搜索结果,即使已经存在缓存版本。仅当查询和所有参数完全相同时,才能提供缓存。 1小时后缓存到期。缓存的搜索是免费的,并且不计入您每月的搜索。它可以设置为false
(默认值)以允许缓存的结果,也可以将true
的结果设置为禁止缓存结果。no_cache
和async
参数不应一起使用。 -
async
参数定义了要将搜索提交给SERPAPI的方式。可以将其设置为false
(默认值)以打开HTTP连接并保持打开状态,直到获得搜索结果,或者true
仅将搜索提交给SERPAPI并以后将其检索。在这种情况下,您需要使用我们的Searches Archive API来检索结果。async
和no_cache
参数不应一起使用。async
不应在启用Ludicrous Speed的帐户上使用。
接下来,我们声明获得初步结果的功能getSearchRefinement
。在此功能中,我们返回此搜索的可用过滤器,并检查preResults
是否具有spelling_fix
,将其设置为fixedQuery
,然后返回:
const getSearchRefinement = async () => {
const preResults = await getJson(engine, params);
let fixedQuery;
if (preResults?.search_information?.spelling_fix) {
fixedQuery = preResults.search_information.spelling_fix;
}
return { query: fixedQuery, filters: preResults.filters };
};
接下来,我们声明允许设置固定查询和过滤器的函数applyNewSearchParams
。我们需要destructure函数参数反对query
和filters
:
const applyNewSearchParams = async ({ query, filters }) => {
...
};
在此功能中,我们需要检查是否存在query
,然后在此之后打印一个有关控制台中更改搜索查询的问题(koude84方法),并等待用户的答案。如果用户的答案为“ y”,我们将新的搜索查询设置为params
对象:
if (query) {
const answer = await rl.question(`Do you want to change the search query from "${params.q}" to "${query}"? y/n: `);
if (answer.toLowerCase() === "y") {
params.q = query;
console.log(`Now the search query is: "${query}"`);
} else {
console.log(`The search query didn't change`);
}
}
接下来,我们需要通过使用koude86循环运行每个过滤器结果来询问有关应用接收过滤器的相同问题:
if (filters) {
const appliedFilters = [];
let tokens = "";
for (const filter of filters) {
const answer = await rl.question(`Do you want to apply some filter from "${filter.key}" category? y/n: `);
if (answer.toLowerCase() === "y") {
for (const filterValue of filter.value) {
const answer = await rl.question(`Do you want to apply "${filterValue.name}" filter from "${filter.key}" category? y/n: `);
if (answer.toLowerCase() === "y") {
tokens += `${filterValue.value},`;
appliedFilters.push(`${filter.key}: ${filterValue.name}`);
}
}
}
}
...
}
接下来,我们关闭readline
流(rl.close()
方法),如果用户选择了一些过滤器,请在tokens
字符串末端删除逗号,然后将其设置为params
对象中的hd_filter_tokens
值:
rl.close();
if (tokens) {
params.hd_filter_tokens = tokens.slice(0, -1);
}
接下来,我们声明从所有页面获得结果(使用分页)并返回的函数getResults
:
const getResults = async () => {
...
};
在此功能中,我们需要声明一个空的results
数组,然后使用koude94循环获得带有结果的json
,从每个页面中添加products
并设置下一页index(to params.page
value)。
如果页面上没有更多结果,或者接收结果的数量超过resultsLimit
,我们会停止循环(使用koude99)并返回带有结果的数组:
const results = [];
while (true) {
const json = await getJson(engine, params);
if (json.products) {
results.push(...json.products);
params.page += 1;
} else break;
if (results.length >= resultsLimit) break;
}
return results;
最后,运行getSearchRefinement
函数并使用Promise chaining运行applyNewSearchParams
和getResults
函数。然后,我们使用koude103方法在控制台中打印所有接收的信息,该方法允许您使用带有必要参数的对象来更改默认输出选项:
getSearchRefinement()
.then(applyNewSearchParams)
.then(getResults)
.then((result) => console.dir(result, { depth: null }));
输出
[
{
"position": 1,
"product_id": "318783386",
"title": "2500-Watt Recoil Start Ultra-Light Portable Gas and Propane Powered Dual Fuel Inverter Generator with CO Shield",
"thumbnails": [
[
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_65.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_100.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_145.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_300.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_400.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_600.jpg",
"https://images.thdstatic.com/productImages/95951f39-703a-4efc-b12b-ad3a3276e73c/svn/champion-power-equipment-inverter-generators-201122-64_1000.jpg"
],
[
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_65.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_100.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_145.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_300.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_400.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_600.jpg",
"https://images.thdstatic.com/productImages/27667698-e4fc-488f-b713-7f51e83d5b5b/svn/champion-power-equipment-inverter-generators-201122-e4_1000.jpg"
]
],
"link": "https://www.homedepot.com/p/Champion-Power-Equipment-2500-Watt-Recoil-Start-Ultra-Light-Portable-Gas-and-Propane-Powered-Dual-Fuel-Inverter-Generator-with-CO-Shield-201122/318783386",
"serpapi_link": "https://serpapi.com/search.json?delivery_zip=04401&engine=home_depot_product&product_id=318783386&store_id=2414",
"model_number": "201122",
"brand": "Champion Power Equipment",
"collection": "https://www.homedepot.com/collection/Outdoors/Champion-Inverter-Generators-Accessories-Collection/Family-311405669?omsid=318783386",
"rating": 4.7821,
"reviews": 1606,
"price": 899,
"delivery": {
"free": true,
"free_delivery_threshold": false
},
"pickup": {
"free_ship_to_store": true
}
}
]
链接
- Code in the online IDE
- The Home Depot API
- The Home Depot Filtering API
- The Home Depot Price Bound API
- The Home Depot Sorting API
- The Home Depot Spell Check API
- The Home Depot API Playground
如果您希望将其他功能添加到此博客文章中,或者您想查看Serpapi,write me a message的某些项目。
添加一个Feature Requestð«或Bugð