Ответ 1
Прохладный вопрос! Я потратил слишком много времени на это, но, думаю, наконец-то понял:)
Вкратце, вы должны загрузить необработанные байты вашего изображения, встроенные и отформатированные вместе с некоторыми другими вещами, в images.google.com/searchbyimage/upload
. Ответ на этот запрос будет содержать новый URL-адрес, который отправит вас на страницу фактических результатов.
Эта функция вернет URL страницы результатов. Вы можете делать все, что хотите, но просто открыть результаты в браузере, передать его на Start-Process
.
Конечно, Google может изменить рабочий процесс для этого в любое время, поэтому не ожидайте, что этот script будет работать навсегда.
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-адрес, который он отправляет вам, очень длинный (~ 1500 символов в случае longcat), но не достаточно длинный, чтобы полностью кодировать изображение (60 КБ). Таким образом, вы можете сразу сказать, что это сложнее, чем просто делать что-то вроде кодировки base64.
Затем я запустил Fiddler и посмотрел, что происходит, когда вы выполняете локальный поиск изображений. После просмотра/выбора изображения вы увидите трафик на images.google.com/searchbyimage/upload
. Подробный просмотр этого запроса показывает основной механизм.
- Данные отправляются в формате
multipart/form-data
, и вам нужно указать, какая строка символов разделяет разные поля (красные поля). Если вы используете Bing/Google, вы обнаружите, чтоmultipart/form-data
является своего рода веб-стандартом, но для этого в действительности это не важно. - Вам необходимо (или, по крайней мере, должно) включить исходное имя файла (оранжевый ящик). Возможно, это влияет на результаты поиска.
- Полное, необработанное изображение включено в поле
encoded-image
(зеленый квадрат). - Ответ не содержит фактических результатов, это просто перенаправление на страницу фактических результатов (фиолетовые поля).
Здесь есть несколько полей, которые показаны ниже. Они не очень интересны.
Как только я понял основной рабочий процесс, это было всего лишь вопросом его кодирования. Я просто скопировал веб-запрос, который я видел в Fiddler, насколько это было возможно, используя стандартные API веб-запросов .NET. Ответы на этот вопрос SO демонстрируют необходимые вам API, чтобы правильно кодировать и отправлять данные тела в веб-запрос.
Из некоторых экспериментов я обнаружил, что вам нужны только два поля тела, которые я включил в свой код (encoded_image
и image_content
). Переход через веб-интерфейс включает в себя больше, но, по-видимому, они не требуются.
Больше экспериментов показало, что ни один из других заголовков или файлов cookie, показанных в Fiddler, действительно не требуется.
Для наших целей мы фактически не хотим получать доступ к странице результатов, а только получаем указатель на нее. Таким образом, мы должны установить AllowAutoRedirect
в $false
. Таким образом, перенаправление Google 302 предоставляется нам напрямую, и мы можем извлечь URL страницы результатов из него.
Во время написания этого редактирования я ударил меня по лбу и понял, что у Powershell v3 есть командлет Invoke-WebRequest
, который потенциально может устранить необходимость в вызовах веб-API.NET. К сожалению, я не мог заставить его работать правильно после того, как поработал в течение 10 минут, поэтому я сдался. Похоже, что некоторые проблемы связаны с тем, как командлет кодирует данные, хотя я могу ошибаться.