かっこいい質問です!私はこれをいじくり回すのにあまりにも多くの時間を費やしました、しかし私はついにそれを手に入れたと思います:)
一言で言えば、他のものと一緒に埋め込まれ、適切にフォーマットされた画像の生のバイトをにアップロードする必要がありますimages.google.com/searchbyimage/upload
。そのリクエストへの応答には、実際の結果ページに移動する新しい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
、さまざまなフィールド(赤いボックス)を区切る文字列を指定する必要があります。multipart/form-data
Bing / Googleを使用している場合、それはある種のWeb標準であることがわかりますが、この例では実際には問題ではありません。
- 元のファイル名(オレンジ色のボックス)を含める必要があります(または少なくとも含める必要があります)。おそらく、これが検索結果に影響します。
- 完全な生の画像が
encoded-image
フィールド(緑色のボックス)に含まれています。
- 応答には実際の結果は含まれていません。実際の結果ページ(紫色のボックス)へのリダイレクトにすぎません。
ここに表示されていないフィールドがいくつかあります。彼らはあまり面白くありません。
基本的なワークフローを理解したら、それをコーディングするだけでした。標準の.NETWebリクエストAPIを使用して、Fiddlerで見たWebリクエストを可能な限り忠実にコピーしました。このSOの質問に対する回答は、Webリクエストで本文データを適切にエンコードして送信するために必要なAPIを示しています。
encoded_image
いくつかの実験から、コードに含めた2つのbodyフィールド(と)だけが必要であることがわかりましたimage_content
。Web UIを通過することにはさらに多くのことが含まれますが、明らかにそれらは必須ではありません。
さらに実験を重ねた結果、Fiddlerに表示されている他のヘッダーやCookieは実際には必要ないことが明らかになりました。
私たちの目的では、実際には結果ページにアクセスするのではなく、そのページへのポインタを取得するだけです。したがって、に設定AllowAutoRedirect
する必要があり$false
ます。そうすれば、Googleの302リダイレクトが直接提供され、そこから結果ページのURLを抽出できます。
この編集を書いているときに、額を叩いて、Powershell v3にInvoke-WebRequest
コマンドレットがあることに気付きました。これにより、.NETWebAPI呼び出しが不要になる可能性があります。残念ながら、10分間いじった後、正しく動作させることができなかったので、あきらめました。私は間違っているかもしれませんが、コマンドレットがデータをエンコードする方法に問題があるようです。