#「Puppeteer入門ースクレイピング+Web操作自動処理プログラミング」の動かないサンプルコード③
「Puppeteer入門ースクレイピング+Web操作自動処理プログラミング」を購入し、ただいま勉強中です。
これね↓
Puppeteer入門 スクレイピング+Web操作自動処理プログラミング
動かないサンプルコードがいくつかありますね。
のちのち使えそうなコードはせっかくなので修正して残しておこうと思いました。
####まずpuppeteerについて
「Puppeteer入門ースクレイピング+Web操作自動処理プログラミング」の動かないサンプルコード① の「まずpuppeteerについて」の項目をご覧ください。
#####7章の6,「ブログの画像を保存する」
これも「Puppeteer入門ースクレイピング+Web操作自動処理プログラミング」の動かないサンプルコード②と同じです。do { 処理 } while(true)の中にawait Promise.all([ 処理 ]);があって、その中にpage.clickがある。そのせいか?ループ途中でハングアップします。
解決方法も同じ。
const nextに、セレクタオブジェクト'(a[rel="next"])'を代入するのではなく、そのhref属性(リンク先URL)を代入。
const next = await page.evaluate(() => document.querySelector('a[rel="next"]'));
const next = await page.evaluate(() => document.querySelector('a[rel="next"]').href);
そしてPromise.allの中で、**page.click()ではなくpage.goto()**を使う。
です。
したがって修正箇所はほんの2箇所です。
/// 修正日(2019年2月11日)修正箇所は'///--修正--///'の行です
const puppeteer = require('puppeteer');
const path = require('path');
const request = require('request');
const { promisify } = require('util');
const fs = require('fs');
const delay = require('delay');
/**
* ファイルのダウンロードを行う.
* @param {string} url - ダウンロードするファイルのURL
*/
const downloadFile = async (url) => {
// ダウンロードファイル名の確定.
const filename = url.split('/').pop();
// ファイルの取得.
const res = await promisify(request)({ method: 'GET', uri: url, encoding: null });
// 成功(200)したかどうか?
if (res.statusCode === 200) {
// 成功していればjsと同じフォルダーにファイル出力
await promisify(fs.writeFile)(path.join(__dirname, filename), res.body, 'binary');
} else {
// 失敗した場合はエラー処理.
throw new Error(`${res.statusCode} ダウンロードエラー`);
}
};
/**
* メインロジック.
*/
(async () => {
// Puppeteerの起動.
const browser = await puppeteer.launch({
headless: false, // Headlessモードで起動するかどうか.
slowMo: 50, // 指定のミリ秒スローモーションで実行する.
});
// 新しい空のページを開く.
const page = await browser.newPage();
// view portの設定.
await page.setViewport({
width: 1200,
height: 800,
});
// ページの遷移.
console.log('----------------------------------------goto');
await page.goto('http://ryoichi0102.hatenablog.com/');
await delay(1000); // スクレイピングする際にはアクセス間隔を1秒あける.
// 先頭の記事のurlを取得し、そのurlへ遷移.
console.log('----------------------------------------goto');
const firstPage = await page.evaluate(() => document.querySelector('#main article:nth-child(1) h1.entry-title a').href);
// const firstPage = 'http://ryoichi0102.hatenablog.com/entry/2018/12/28/101519';
await page.goto(firstPage);
await delay(1000); // スクレイピングする際にはアクセス間隔を1秒あける.
// 各記事に対してのそれぞれの処理.
do {
console.log('----------------------------------------do');
const imageUrls = await page.evaluate(() => Array.from(document.querySelectorAll('img.hatena-fotolife')).map(img => img.src));
for (url of imageUrls) {
console.log(`Downloading... ${url}`);
await downloadFile(url);
}
console.log('----------------------------------------eval next');
// 最後の記事までたどると次へボタンは表示されないので、その場合はループを抜ける.
///---修正---/// const next = await page.evaluate(() => document.querySelector('a[rel="next"]'));
const next = await page.evaluate(() => document.querySelector('a[rel="next"]').href);
console.log('--------------------------------------nextのhrefは、' + next);
if (next === null) {
break;
}
// process.on('unhandledRejection', console.dir); // Promise内の捕捉されなかった例外について表示する
// 次のページを読み込む.
console.log('----------------------------------------next');
await Promise.all([
console.log('----------------------------------------inside Promise.all'),
page.waitForNavigation({ waitUntil: 'load' }),
///---修正---/// page.goto('a[rel="next"]'),
page.goto(next),
]);
await delay(1000); // スクレイピングする際にはアクセス間隔を1秒あける.
} while (true);
// ブラウザの終了.
console.log('----------------------------------------close');
await browser.close();
})();