Node.jsでHTMLを取得してparseして必要なデータを取得する
2021/05/16
はじめに
スクレイピングをする機会が多いのでメモとして残す。
htmlを取得する
url を渡して取得する。コメントを外せば保存ができるようにしてある。
エラー対策してないのは割愛
const get = (url) =>{
return new Promise((resolve, reject)=>{
request.get({
uri: url,
headers: {'Content-type': 'text/html;'},
}, function(err, req, data){
if(req.statusCode == 200){
resolve(data)
//fs.writeFileSync(path.resolve(__dirname,`./temp-${Date.now()}.html`), data);
}
console.log("complate:",url);
resolve("");
});
})
}
htmlをパースする
JSDOM を使ってHTMLをパースして window から色々取れるようになる。javascript で描画されるものは対応してないっぽいので、ちょっと処理が重くなっちゃうけど Headless Chrome とかの方が良いかも。試してはない document.addEventListener("DOMContentLoaded",()=>{...}) みたいに取得できたら良いかなと思おうけど非同期処理を入れる必要があるかな。runScripts: "dangerously" プロパティを設定すると js などが走るっぽい。resources: "usable" を設定することで外部リソースの利用を許可する。
const htmlParse = (htmlText) =>{
try{
const dom = new JSDOM(htmlText,{
runScripts: "dangerously",
resources: "usable"
});
// get element
const text = dom.window.document.title;
console.log(`text:`,text);
}catch (e){
console.error(e)
}
}
全体
url.txt からUrl群を取得してUrlからHTMLを取得する。
const fs = require('fs');
const glob = require('glob');
const request = require('request');
const path = require('path');
const { JSDOM } = require('jsdom')
const urls = fs
.readFileSync(
path.resolve(__dirname,"url.txt"),'utf8'
)
.split('\n')
.filter(t=>t.length > 0);
const get = (url) =>{
return new Promise((resolve, reject)=>{
request.get({
uri: url,
headers: {'Content-type': 'text/html;'},
}, function(err, req, data){
if(req.statusCode == 200){
resolve(data)
//fs.writeFileSync(path.resolve(__dirname,`./temp-${Date.now()}.html`), data);
}
console.log("complate:",url);
resolve("");
});
})
}
const htmlParse = (htmlText) =>{
try{
const dom = new JSDOM(htmlText,{
runScripts: "dangerously",
resources: "usable"
});
// get element
setTimeout(()=>{
const text = dom.window.document.title;
console.log(`text:`,text);
},1000);
}catch (e){
console.error(e)
}
}
Promise.all(urls.map(url=>get(url))).then((htmls)=>{
htmls.forEach((html) => {
htmlParse(html)
});
});
// glob.sync(path.resolve(__dirname,`./temp-*.html`)).forEach((p)=>{
// fs.unlinkSync(p);
// });
終わりに
JSDOMにfromURL()というメソッドがあるのでHTMLファイルとして保存しない場合requestが必要ないかも。fromFile()メソッドもあるので保存したHTMLファイルから再開もfsを使わないでできそう。
JSDOM.fromURL("https://example.com/", options).then(dom => {
console.log(dom.serialize());
});
window.matchMediaがない。Headless Chromeでいいかな…。