Edited at

「Puppeteer入門ースクレイピング+Web操作自動処理プログラミング」の動かないサンプルコード③


「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();
})();