木偶师"/>
木偶师
我正在使用 Puppeteer 来绕过 cloudflare 安全性。
长话短说,我需要执行一个
POST
请求来启动某种二进制文件下载。实际上,这个文件只是一个纯文本,由于某种原因而没有像这样发送扩展名(由\n
分隔)并且可以使用任何文本编辑器轻松打开:
{ json string 1 }
{ json string 2 }
...
W当普通浏览器执行此请求时(当站点按应有的方式使用时),它只是获取此 url,等待它的响应,并且文本正在页面上显示。在 DevTools 中可以将响应本身视为带有
application/octet-stream
标题的纯文本。W当我尝试使用
GoToAsync
在 non-headless 模式下使用 Puppeteer 获取它时,它等待响应,然后开始使用 chromium 下载管理器将其作为二进制文件下载,并且在下载完成之前,立即抛出NavigationException: net::ERR_ABORTED
,但下载继续正常进行。没关系,我知道这只是某种错误,我实际上仍然可以用try-catch
处理它,然后通过打开下载的文件访问接收到的数据(不过,最好把它作为一个 Stream
或响应本身的纯文本).P问题是我需要在 headless 模式下使用 Puppeteer。在 headless 中,它的行为完全相同,但下载不会开始。我能用它做什么?我至少可以以某种方式下载它,以与非无头模式相同的方式忽略此异常吗?
这是我使用的示意性代码示例:
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
var browser = await Puppeteer.LaunchAsync(new() { Headless = true });
var page = await browser.NewPageAsync();
await page.SetRequestInterceptionAsync(true);
await page.Client.SendAsync("Page.setDownloadBehavior", new
{
behavior = "allow",
downloadPath = "_path_"
});
page.Request += async (s, e) =>
{
// sets method to POST, adds some headers, specifies post-data
var payloadOverride = new Payload() { ... };
await e.Request.ContinueAsync(payloadOverride); // executes normally and leaves code block
};
var response = await page.GoToAsync($"url"); // will throw exception "somewhere in await"
此外,我尝试做这样的事情:
page.Response+= async (s, e) =>
{
await e.Response.TextAsync();
await e.Response.JsonAsync();
await e.Response.BufferAsync();
};
当
GoToAsync
时,.Response
事件实际上被正常触发(就在net::ERR_ABORTED
抛出之前),但是这些方法只会抛出PuppeteerSharp.BufferException: "Unable to get response body"
。所以,我无法像我希望的那样从这里获取数据。
此外,我尝试使用
Evaulate
从浏览器本身对它进行 fetch()
,但 cloudflare 不允许我这样做。虽然,它 (fetch(...).then((response) => response.text())
) 在我的普通浏览器的控制台中工作,并成功返回预期的文本字符串,如本主题开头的示例,但前提是我首先访问该站点本身并在那里打开 DevTools。我试过用 Puppeteer GoTo
到它,然后 Evaulate
它在那里,但是 1020 Access Denied.几周前,在 cloudflare 更新某些东西之前,我只是简单地使用
HttpClient
和适当的用户代理标头。这段代码看起来像这样并且工作得非常好:
HttpRequestMessage request = new(HttpMethod.Post, "same url");
// ...setting post-data and headers...
var response = await _httpClient.SendAsync(request);
// => It will return simple text with few lines, as in example in beginning
string content = await response.Content.ReadAsStringAsync();
string[] chunks = content.Split("\n"); // => and here I have array of JSON string ["{ }", "{ }", ...]
如有任何建议,我们将不胜感激。我使用 Puppeteer 的
.NET
版本,但是如果您的示例将使用 JS
编写就可以了,因为我知道节点版本更受欢迎。
回答如下:
到目前为止我找到的解决方案: 第一的: 只需在
Puppeteer Stealth Plugin
中使用 fetch()
和 JS EvaluateFunctionAsync()
。是的,就这么简单。
第二: 这就是我在知道隐形插件之前使用的东西。
string someDownloadPath = "";
var page = await _browser.NewPageAsync();
await page.SetRequestInterceptionAsync(true);
// allows regular downloads for a headless mode
await page.Client.SendAsync("Page.setDownloadBehavior", new { behavior = "allow", someDownloadPath });
// sending POST with GoToAsync
page.Request += (s, requestArgs) => YourContinueRequestHandler(requestArgs, HttpMethod.Post, data);
// it will ALWAYS throw an exception
try { await page.GoToAsync(url); }
catch (NavigationException)
{
// puppeteer will download content as a binary file with a "download" name
string responsePath = $"{someDownloadPath}/download";
// Wait 30 seconds for a response to download
for (int i = 0; i < 15; i++)
{
await Task.Delay(2000);
if (File.Exists(responsePath)) break;
}
var content = await File.ReadAllTextAsync(responsePath);
}
更多推荐
木偶师
发布评论