jakie miałem naprawdę dużo kłopotów przy użyciu metody render
. Na szczęście w końcu mam dwa lepsze rozwiązania. Oto kod, którego użyłem w moim projekcie. Pierwsze rozwiązanie ma problem z aktualizacją pliku cookie, więc nie działa dobrze podczas pobierania obrazu captcha. Obie metody spowodują nowe żądanie http. Ale z kilkoma modyfikacjami, drugi może anulować takie żądanie.
Pierwsza pobiera plik cookie z phantomJs
i tworzy nowe żądanie http za pomocą request
. Drugi używa base64
do przekazania obrazu.
async download(download_url, stream) {
logger.profile(`download(download_url='${download_url}')`);
let orig_url = await this.page.property('url');
download_url = url.resolve(orig_url, download_url);
let cookies = await this.page.property('cookies');
let jar = request.jar();
for (let cookie of cookies) {
if (cookie.name !== undefined) {
cookie.key = cookie.name;
delete cookie.name;
}
if (cookie.httponly !== undefined) {
cookie.httpOnly = cookie.httponly;
delete cookie.httponly;
}
if (cookie.expires !== undefined)
cookie.expires = new Date(cookie.expires);
jar.setCookie(new Cookie(cookie), download_url, {ignoreError: true});
}
let req = request({
url: download_url,
jar: jar,
headers: {
'User-Agent': this.user_agent,
'Referer': orig_url
}
});
await new Promise((resolve, reject) => {
req.pipe(stream)
.on('close', resolve)
.on('error', reject);
});
// Due to this issue https://github.com/ariya/phantomjs/issues/13409, we cannot set cookies back
// to browser. It is said to be redesigned, but till now (Mar 31 2017), no change has been made.
/*await Promise.all([
new Promise((resolve, reject) => {
req.on('response',() => {
jar._jar.store.getAllCookies((err, cookies) => {
if (err) {
reject(err);
return;
}
cookies = cookies.map(x => x.toJSON());
for (let cookie of cookies) {
if (cookie.key !== undefined) {
cookie.name = cookie.key;
delete cookie.key;
}
if (cookie.httpOnly !== undefined) {
cookie.httponly = cookie.httpOnly;
delete cookie.httpOnly;
}
if (cookie.expires instanceof Date) {
cookie.expires = cookie.expires.toGMTString();
cookie.expiry = cookie.expires.toTime();
}
else if (cookie.expires == Infinity)
delete cookie.expires;
delete cookie.lastAccessed;
delete cookie.creation;
delete cookie.hostOnly;
}
this.page.property('cookies', cookies).then(resolve).catch(reject);
});
}).on('error', reject);
}),
new Promise((resolve, reject) => {
req.pipe(fs.createWriteStream(save_path))
.on('close', resolve)
.on('error', reject);
})
]);*/
logger.profile(`download(download_url='${download_url}')`);
}
async download_image(download_url, stream) {
logger.profile(`download_image(download_url='${download_url}')`);
await Promise.all([
new Promise((resolve, reject) => {
this.client.once('donwload image', data => {
if (data.err)
reject(err);
else
stream.write(Buffer.from(data.data, 'base64'), resolve);
});
}),
this.page.evaluate(function (url) {
var img = new Image(), callback = function (err, data) {
callPhantom({
event: 'donwload image',
data: {
err: err && err.message,
data: data
}
});
};
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext("2d").drawImage(img, 0, 0);
callback(null, canvas.toDataURL("image/png").replace(/^data:image\/(png|jpg);base64,/, ""));
};
img.onerror = function() {
callback(new Error('Failed to fetch image.'));
};
img.src = url;
}, download_url)
]);
logger.profile(`download_image(download_url='${download_url}')`);
}
Tak, przepraszam za mój zły angielski. –
Czy Twój "->" jest jakimś skrótem funkcji 'function() {...}'? – LarsH
Tak, jest to notacja coffeescript dla funkcji. –