instagramの情報をスクレイピングして自動で整理できないかと目論んで、調べてみた。
結果としてinstagramのウェブページからはスクレイピングはできなかったが、スクレイピングの手法だけでもメモとして残す。
Nuxtプロジェクト作成
スクレイピングテスト用のnuxtのプロジェクトを作成
create-nuxt-appの初期設定の中で、modulesにAxiosを明示的に指定しておく。
? Nuxt.js modules: Axios
puppeteer
node.jsではpuppetterというライブラリを使用するのがいいらしい。apiでヘッドレスでブラウザを呼び出してウェブページからその内容を取得するイメージ。
$ npm i puppeteer
API実装
Apiを実装するためのサーバーミドルウェアとしてExpressを使用する。
実は書いててよくわかってはいないけれど、APIを呼び出したりなどサーバとのやり取りを担当する仕組みの部分が必要で、NuxtだとExpressをいうライブラリを介してAPIを実現する。
nuxt.config.jsに以下を追加
export default {
...
,
serverMiddleware: {
'/api': '~/api'
}
}
上記により/apiを叩いたとき~/api/index.jsを参照するようになる。
api/index.jsと、実処理を担当するapi/scraping.jsを作成。
$ mkdir api $ touch api/index.js api/scraping.js
それぞれのファイルの中身は以下の通り。
api/index.js(参考サイトのソースほぼそのまま)
const app = require('express')()
const scraping = require('./scraping')
app.get('/get_posts', async(req, res) => {
const image = await scraping.getPosts()
res.send(image)
})
module.exports = {
path: '/api',
handler: app
}
api/scraping.js(このブログの記事のタイトル一覧を取得するよう記述)
const puppeteer = require('puppeteer')
async function getPosts() {
const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-dev-shm-usage'
]
})
const page = await browser.newPage()
await page.goto("http://blog.wald-grun.biz",{waitUntil: 'domcontentloaded'})
const image = await page.evaluate(() => {
const posts = document.getElementsByClassName("c-entries__item")
const listcount = posts.length
const list = []
for(i=0;i<listcount;i++) {
list.push(posts[i].getElementsByClassName("c-entry-summary__title")[0].textContent)
}
return list
})
return image
}
module.exports = {
getPosts
}
page.evaluateでページの内容を取得している。
取得にあたっては通常のjsの記述を使う。今回は.c-entries__itemを持った要素(1記事)のリストを取得し、その後にループでタイトル一覧の配列にして返している。
この辺は初見で適当に書いたけど、要素を取得して配列にする記述とかありそうだから要勉強。
フロント側実装
index.vue
<template>
<div class="container">
<ul>
<li v-for="(item,index) in src" :key="index">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
src: ""
}
},
methods: {
async showBird() {
this.src = await this.$axios.$get("/api/get_posts")
}
},
mounted() {
this.showBird()
}
}
</script>
<style>
.container {
margin: 0 auto;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.title {
font-family:
'Quicksand',
'Source Sans Pro',
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif;
display: block;
font-weight: 300;
font-size: 100px;
color: #35495e;
letter-spacing: 1px;
}
.subtitle {
font-weight: 300;
font-size: 42px;
color: #526488;
word-spacing: 5px;
padding-bottom: 15px;
}
.links {
padding-top: 15px;
}
</style>
api/get_postsをaxiosでgetし、取得した値をv-forで展開。
無事に表示されました。
instagramはうまくいかない
さて、上記のやり方でinstagramの内容を取得しようとしたところ、まずログインが必要でした。
調べたところ、まずログインの手順を踏んでcookieを取得し、そのcookieを使ってログイン状態としてページを参照する、という流れらしい。
cookie取得
ログインしてcookieを取得するまでは試しました。(成功してませんが)
dotenvの導入
まず事前に、環境変数的にプロジェクトごとの.envで定数を管理するためのライブラリを導入。
$npm install @nuxtjs/dotenv
.envファイルを用意して設定値を記述
.env(インスタのアカウント等々)
ACCOUNT_NAME=kng_s ACCOUNT_PASS=XXXXXXXXX LOGIN_URL=https://www.instagram.com/accounts/login/
cookieを取得するjsを用意。
getCookie.js
require('dotenv').config();
const puppeteer = require('puppeteer');
const fs = require('fs');
const ACCOUNT_NAME = process.env.ACCOUNT_NAME;
const ACCOUNT_PASS = process.env.ACCOUNT_PASS;
const LOGIN_URL = process.env.LOGIN_URL;
const requireEnvs = [
ACCOUNT_NAME,
ACCOUNT_PASS,
LOGIN_URL
];
console.log(requireEnvs)
const COOKIES_PATH = 'token/cookies.json';
// entry point
(async () => {
for (let requireEnv of requireEnvs) {
if (requireEnv == undefined) {
console.log('local env is not set.');
return;
}
}
console.log('try to get cookie...');
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// パスワードを使ってログイン
await page.goto(LOGIN_URL, {waitUntil: 'networkidle0'});
await page.type('input[name="username"]', ACCOUNT_NAME);
await page.type('input[name="password"]', ACCOUNT_PASS);
page.click('button[type="submit"]');
await page.waitForNavigation({timeout: 60000, waitUntil: 'domcontentloaded'});
// Cookieをローカルに保存
const afterCookies = await page.cookies();
fs.writeFileSync(COOKIES_PATH, JSON.stringify(afterCookies));
browser.close();
console.log('success!');
})();
こちらをnodeで実行するとcookieが取れるはず…なのですが、instagramだとログインできず。うまくいきませんでした。
$ node getCookie.js
inputoのNodeが存在しないというエラーになるのですが、instagramは基本的にすべて動的にコンテンツ生成がされており、微妙なタイミングなどでNodeが取得できなかったりする?のかと解釈しています。
こちらも要勉強です。
取得後の流れは参考サイトを参照。
