我想抓一个网站。我想要抓的网站没有API。
我想要做的是(在Python中):
import requests
with requests.Session() as conn:
url = "http://demo.ilias.de/login.php"
auth = {
"username": "benjamin",
"password": "iliasdemo"
}
conn.post(url, data=auth)
response = conn.get(url)
do_work(response)
当尝试使用HTTPoison做同样的事情时,网站会回答“请在浏览器中启用会话cookie!”。 Elixir代码:
HTTPoison.post "http://demo.ilias.de/login.php",
"{\"username\":\"benjamin\", \"password\":\"iliasdemo\"}"
我想问题出在cookie上。
UPD#1。似乎并非所有Cookie都已保存,因为:hackney.cookies(headers)
(headers
来自%HTTPoison.Response{headers: headers}
)并未输出其中一些Cookie(例如authchallenge
)我在浏览器和在上面的Python代码的响应中。可能是hackney实际上没有发布任何内容的情况吗?
答案 0 :(得分:1)
我遇到了类似的问题:
我向服务器api发出GET请求,它在同一位置响应301重定向,然后是#34; Set-Cookie"带有sessionId的标头。如果您按照重定向而不发送回cookie,则会使用相同的重定向和新的SessionId cookie进行响应。如果你从来没有把他们的饼干送回去,这个动机会继续这样。另一方面,如果您将他们的cookie发回给他们,他们会回复200状态代码和您询问的数据。
问题是hackney,因此HTTPoison无法遵循这种情况。 它实际上有一个:follow_redirect选项,当设置它时,它遵循重定向,但在抓取cookie并在重定向之间发回它们时不尽如人意。
我尝试过的所有浏览器(firefox,chrome,IE)都可以通过这种情况。 Python和wget也完成了这项工作。
无论如何,为了简短起见,我为我的案例写了一个解决方法,可能会给其他有类似问题的人提供一些想法:
defmodule GVHTTP do
defmacro __using__(_) do
quote do
use HTTPoison.Base
def cookies_from_resp_headers(recv_headers) when is_list(recv_headers) do
List.foldl(recv_headers, [], fn
{"Set-Cookie", c}, acc -> [c|acc]
_, acc -> acc
end)
|> Enum.map(fn(raw_cookie) ->
:hackney_cookie.parse_cookie(raw_cookie)
|> (fn
[{cookie_name, cookie_value} | cookie_opts] ->
{ cookie_name, cookie_value,
cookie_opts
}
_error ->
nil
end).()
end)
|> Enum.filter(fn
nil -> false
_ -> true
end)
end
def to_request_cookie(cookies) do
cookies
|> Enum.map(fn({ cookie_name, cookie_value, _cookie_opts}) ->
cookie_name <> "=" <> cookie_value
end)
|> Enum.join("; ")
|> (&("" == &1 && [] || [&1])).() # "" => [], "foo1=abc" => ["foo1=abc"]
end
def get(url, headers \\ [], options \\ []) do
case options[:follow_redirect] do
true ->
hackney_options = case options[:max_redirect] do
0 -> options # allow HTTPoison to handle the case of max_redirect overflow error
_ -> Keyword.drop(options, [:follow_redirect, :max_redirect])
end
case request(:get, url, "", headers, hackney_options) do
{:ok, %HTTPoison.Response{status_code: code, headers: recv_headers}} when code in [301, 302, 307] ->
{_, location} = List.keyfind(recv_headers, "Location", 0)
req_cookie =
cookies_from_resp_headers(recv_headers)
|> to_request_cookie()
new_options =
options
|> Keyword.put(:max_redirect, (options[:max_redirect] || 5) - 1)
|> Keyword.put(:hackney, [cookie:
[options[:hackney][:cookie]|req_cookie]
|> List.delete(nil)
|> Enum.join("; ")
]) # add any new cookies along with the previous ones to the request
get(location, headers, new_options)
resp ->
resp
end
_ ->
request(:get, url, "", headers, options)
end
end
end # quote end
end # __using__ end
end