

# 如何合并 Bing 搜索导出数据，同时保留查询和排名证据

将搜索结果观测记录和目标地址分别合并到独立的表中，以完整保留查询、排名和时间证据。Actor 是托管运行的数据采集程序。Task 是保存好的输入配置。canonicalUrl 是返回的、经过规范化的候选地址，不是经核实的 HTML canonical 地址，也不是原始 href。snippet 是搜索来源提供的文本摘要，不是抓取的文章正文。

配置 Apify Google Search Scraper 的构建版本 1.0.15 时，将已保存的示例查询列表和引擎列表整体替换为你要使用的查询短语和 `["bing"]`，使用以下输入 JSON：`{"queries": ["Python asyncio documentation"], "engines": ["bing"], "maxResultsPerExecutedQuery": 2, "country": "US", "language": "en", "safeSearch": true, "includeSponsoredResults": false}`。请注意，公开构建版本 1.0.15 支持多个引擎，但此处仅 Bing 通过了验收；Google、Brave 和 DuckDuckGo 的处理路径暂缓验收或已被排除。对照测试的 OUTPUT 运行摘要报告了以下计数：`queriesRequested`: 1、`searchesCompiled`: 1、`executionsPlanned`: 1、`enginesSuccessful`: 1、`enginesFailed`: 0、`pagesAttempted`: 1、`pagesParsed`: 1、`resultsSaved`: 2、`engineStatusCounts`: {"bing": {"ok": 1}}、`engineResultCounts`: {"bing": 2}、`queryResultCounts`: {"Python asyncio documentation": 2}、`stopReason`: "result_caps_reached"。这些运行摘要计数反映的是抓取程序的执行总量，不是各目标地址对应的数据行数。这项测试仅确认了在限定范围内对 Bing URL、排名和来源追溯信息的支持，并未确认尚未通过验收的其他引擎、其他市场或目标页面抓取。

## 1. 设置数据采集规则

1. 设置数据采集规则：将已保存的查询列表和引擎列表整体替换为 1 个要使用的查询短语和 `["bing"]`，设置 `maxResultsPerExecutedQuery: 2`，国家为美国，语言为英语，并设置 `safeSearch: true` 和 `includeSponsoredResults: false`。此处未规定公开的页数上限输入参数。
2. 保存原始 Bing 结果行和 OUTPUT 元数据。原生数据行字段包括 `runId`、`country`、`language`、`safeSearch`、`engine`、`executedQuery`、`page`、`rankOnPage`、`rank`、`title`、`canonicalUrl`、`resultDomain`、`snippet`、`isSponsored`、`advertiserDomain`、`observedAt` 和 `schemaVersion`。完整保留用于识别运行及其设置的信息。
3. 使用 `runId` + `engine` + `executedQuery` + `page` + `rankOnPage` 构建观测记录键。保留 rank 和 observedAt 作为证据。从工作表中移除完全重复的导出行，并留下重复记录凭据。如果同一个键对应的 URL 值存在冲突，应保留 2 个候选值，等待核查来源，不要自行选择而不作说明，也不要按多数值判定。
4. 根据返回的 `canonicalUrl` 创建目标键。该字段是规范化后的候选地址，不是 HTML canonical 证据，也不是原始 href。先使用返回的完整 URL 字符串进行精确匹配。如果要规范化主机名大小写、片段标识或跟踪参数，应记录目标地址仍指向同一位置的依据。路径或查询参数不同的地址应保持独立。
5. 通过目标键列，将观测记录关联到目标地址表。同一个 URL 若由 2 个查询找到，或在 2 个时间点被找到，应对应多条观测记录和 1 个经过核查的目标地址。URL 与查询的归属关系只能来自实际观测记录。
6. 安全导出工作表。完整保留查询中的引号、逗号、换行符和 Unicode 字符。以原始 JSON 或 JSONL 作为主要核对依据，因为 CSV 导出或导入工具可能无法保留 null 与空值之间的区别。合并大量导出数据前，先用一个小型测试样例检查导出后再导入能否精确保留原值。
7. 分别报告原始行数、重复导出行数、不同的有效观测记录数、冲突数，以及不同的目标键数。去重后的 URL 数或查询归属关系数，不能用于估算搜索总量或全部网站的数量。在采集凭据中保留失败、无结果或达到上限的搜索记录。
8. 将目标地址和观测记录引用交给[独立的目标页面检查流程](/zh-Hans/articles/%E7%94%A8%E5%BF%85%E5%BA%94%E6%90%9C%E7%B4%A2%E7%BB%93%E6%9E%9C%E6%9E%84%E5%BB%BA%E6%8A%93%E5%8F%96%E9%98%9F%E5%88%97/)。合并资源不能证明页面可访问、内容相关或来源可靠。明确说明范围仅限 Bing。

## 虚构的合并台账与数据检查

*注：以下表格和记录均为原创虚构内容，仅用于教学。*

设想这样一项测试：运行 R1 使用查询 "AI agents"，得到排名第 1 的 U1 和排名第 2 的 U2。运行 R1 使用查询 "small business automation"，得到排名第 1 的 U2 和排名第 2 的 U3。运行 R2 使用查询 "AI agents"，得到排名第 1 的 U1 和排名第 2 的 U4。这些都是 Bing 结果，使用已声明的相同设置，并各自带有时间记录。6 个观测记录键映射到 U1、U2、U3 和 U4 这 4 个目标键。U1 有 2 个时间点的观测记录；U2 有来自 2 个查询的观测记录。这 2 种重复出现的情况都不属于重复导出行。

第 7 条原始行完全重复了 R1 / 查询 "AI agents" / 第 1 页 / 排名第 2 的 U2，逐项复制了原始行的所有原始值，因此属于重复导出行。第 8 条原始行使用相同的键，但值为 U9。键 R1+bing+AI agents+1+2 对应的 U2 和 U9 两个值均处于冲突状态；应在冲突台账中保留这 2 个候选值，不要采信最先出现的值或多数值。共保留 8 条原始行、1 条重复导出行、6 个观测记录键、1 个冲突键、5 条无冲突的观测记录，以及 4 个已认可的目标地址。U9 仍不纳入已认可的目标地址。

*原创虚构示例：8 条原始行表。*

| RowId | RunId | Engine | ExecutedQuery | Page | RankOnPage | CanonicalUrl | ObservedAt |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | R1 | bing | AI agents | 1 | 1 | U1 | 2026-10-13T06:20:15Z |
| 2 | R1 | bing | AI agents | 1 | 2 | U2 | 2026-10-13T06:20:15Z |
| 3 | R1 | bing | small business automation | 1 | 1 | U2 | 2026-10-13T06:21:00Z |
| 4 | R1 | bing | small business automation | 1 | 2 | U3 | 2026-10-13T06:21:00Z |
| 5 | R2 | bing | AI agents | 1 | 1 | U1 | 2026-10-13T07:00:15Z |
| 6 | R2 | bing | AI agents | 1 | 2 | U4 | 2026-10-13T07:00:15Z |
| 7 | R1 | bing | AI agents | 1 | 2 | U2 | 2026-10-13T06:20:15Z |
| 8 | R1 | bing | AI agents | 1 | 2 | U9 | 2026-10-13T06:20:15Z |

*原创虚构示例：6 个键的状态表。*

| ObservationKey | CandidateCanonicalUrl | 状态 / 处理状态 |
| --- | --- | --- |
| R1+bing+AI agents+1+1 | U1 | 无冲突 |
| R1+bing+AI agents+1+2 | U2, U9 | 存在冲突，暂缓处理（保留 2 个候选值；第 7 行作为重复导出凭据） |
| R1+bing+small business automation+1+1 | U2 | 无冲突 |
| R1+bing+small business automation+1+2 | U3 | 无冲突 |
| R2+bing+AI agents+1+1 | U1 | 无冲突 |
| R2+bing+AI agents+1+2 | U4 | 无冲突 |

*原创虚构示例：目标地址引用表（4 个已认可的目标地址）。*

| TargetKey | 无冲突的观测记录引用 | 状态 |
| --- | --- | --- |
| U1 | R1 查询 AI agents 排名第 1，R2 查询 AI agents 排名第 1 | 无冲突 |
| U2 | R1 查询 small business automation 排名第 1 | 无冲突（R1 查询 AI agents 排名第 2 的记录存在冲突，暂缓处理） |
| U3 | R1 查询 small business automation 排名第 2 | 无冲突 |
| U4 | R2 查询 AI agents 排名第 2 | 无冲突 |

*原创虚构 CSV 测试样例，包含嵌入的换行符、以双写双引号表示的引号、Unicode 文本 café，以及带逗号的查询：*

代码片段

```text
runId,engine,executedQuery,page,rankOnPage,title,canonicalUrl
"R1","bing","garden, tray",1,1,"Café guide
""Best picks""","https://example.com/garden"
```

虚构的合并台账与数据检查

*测试样例解码后的值与读者检查：*查询字段解码后为 `garden, tray`，标题字段解码后为 `Café guide`，后接一个实际换行符，以及由 1 对引号括起的 `Best picks`；canonicalUrl 解码后为纯字符串 `https://example.com/garden` ，不含 Markdown 语法。读者通过导出后再导入的往返检查，可以确认逗号分隔、嵌入的换行符、1 对引号和 Unicode 字符均得到正确保留。任何关于已完成 Python 解析、写入或读取的陈述，都仅适用于这 1 个虚构测试样例，绝不适用于 Actor 导出数据或大型数据集。

## 下一步

借助官方 [Google Search Scraper Actor](https://apify.com/chronometrica/google-search-scraper) 和 [Export Search Results to CSV Task](https://apify.com/chronometrica/google-search-scraper/examples/export-search-results-to-csv)，检查你的数据采集设置，获取原始 Bing 观测记录，并在移除重复 URL 前完成核对。

## 来源和延伸阅读

- [自动查找用于抓取的 URL](https://www.reddit.com/r/learnpython/comments/f96o07/automate_the_process_of_finding_urls_for_scraping/)