很酷的问题!我花了太多时间修补这个,但我想终于明白了:)
简而言之,您必须将图像的原始字节、嵌入并正确格式化以及其他一些内容上传到images.google.com/searchbyimage/upload
. 对该请求的响应将包含一个新 URL,该 URL 会将您发送到实际结果页面。
此函数将返回结果页面 URL。你可以用它做任何你想做的事情,但要简单地在浏览器中打开结果,请将其传递给Start-Process
.
当然,Google 可以随时为此更改工作流程,所以不要指望这个脚本永远有效。
function Get-GoogleImageSearchUrl
{
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path $_ })]
[string] $ImagePath
)
# extract the image file name, without path
$fileName = Split-Path $imagePath -Leaf
# the request body has some boilerplate before the raw image bytes (part1) and some after (part2)
# note that $filename is included in part1
$part1 = @"
-----------------------------7dd2db3297c2202
Content-Disposition: form-data; name="encoded_image"; filename="$fileName"
Content-Type: image/jpeg
"@
$part2 = @"
-----------------------------7dd2db3297c2202
Content-Disposition: form-data; name="image_content"
-----------------------------7dd2db3297c2202--
"@
# grab the raw bytes composing the image file
$imageBytes = [Io.File]::ReadAllBytes($imagePath)
# the request body should sandwich the image bytes between the 2 boilerplate blocks
$encoding = New-Object Text.ASCIIEncoding
$data = $encoding.GetBytes($part1) + $imageBytes + $encoding.GetBytes($part2)
# create the HTTP request, populate headers
$request = [Net.HttpWebRequest] ([Net.HttpWebRequest]::Create('http://images.google.com/searchbyimage/upload'))
$request.Method = "POST"
$request.ContentType = 'multipart/form-data; boundary=---------------------------7dd2db3297c2202' # must match the delimiter in the body, above
$request.ContentLength = $data.Length
# don't automatically redirect to the results page, just take the response which points to it
$request.AllowAutoredirect = $false
# populate the request body
$stream = $request.GetRequestStream()
$stream.Write($data, 0, $data.Length)
$stream.Close()
# get response stream, which should contain a 302 redirect to the results page
$respStream = $request.GetResponse().GetResponseStream()
# pluck out the results page link that you would otherwise be redirected to
(New-Object Io.StreamReader $respStream).ReadToEnd() -match 'HREF\="([^"]+)"' | Out-Null
$matches[1]
}
用法:
$url = Get-GoogleImageSearchUrl 'C:\somepic.jpg'
Start-Process $url
编辑/解释
这里有更多细节。我基本上只是带你完成我在弄清楚这一点时所采取的步骤。
首先,我只是继续进行本地图像搜索。
它发送给您的 URL 很长(在 longcat 的情况下约为 1500 个字符),但不足以完全编码图像(60KB)。因此,您可以立即看出它比简单地执行 base64 编码之类的操作更复杂。
接下来,我启动了 Fiddler 并查看了当您进行本地图像搜索时实际发生的情况。浏览/选择图像后,您会看到一些images.google.com/searchbyimage/upload
. 详细查看该请求揭示了基本机制。
- 数据以 的格式发送
multipart/form-data
,您需要指定分隔不同字段的字符串(红色框)。如果您使用 Bing/Google,您会发现这multipart/form-data
是某种网络标准,但对于这个示例来说,这并不重要。
- 您需要(或至少应该)包含原始文件名(橙色框)。也许这会影响搜索结果。
- 完整的原始图像包含在该
encoded-image
字段中(绿色框)。
- 响应不包含实际结果,它只是重定向到实际结果页面(紫色框)
这里没有显示一些字段,在底部。它们不是很有趣。
一旦我弄清楚了基本的工作流程,就只需要编写代码即可。我只是使用标准的 .NET Web 请求 API 尽可能地复制了我在 Fiddler 中看到的 Web 请求。这个 SO 问题的答案展示了您需要的 API,以便在 Web 请求中正确编码和发送正文数据。
通过一些实验,我发现您只需要我在代码中包含的两个正文字段(encoded_image
和image_content
)。浏览 Web UI 包括更多内容,但显然它们不是必需的。
更多的实验表明,Fiddler 中显示的其他标头或 cookie 都不是真正需要的。
出于我们的目的,我们实际上并不想访问结果页面,而只是获取指向它的指针。因此我们应该设置AllowAutoRedirect
为$false
。这样,Google 的 302 重定向就直接提供给我们,我们可以从中提取结果页面 URL。
在编写此编辑时,我拍了拍我的额头并意识到 Powershell v3 具有Invoke-WebRequest
cmdlet,它可能会消除对 .NET Web API 调用的需要。不幸的是,我在修补 10 分钟后无法使其正常工作,所以我放弃了。cmdlet 编码数据的方式似乎有些问题,尽管我可能是错的。