Help us understand the problem. What is going on with this article?

CAMPFIREのページをモニタリングしてクラウドファンディングの状況をウォッチする - スクレイピング編

クラウドファンディングプラットフォームの大手CAMPFIREさんのWebサイトをスクレイピングして、ファンディング中のプロジェクトの現在の進捗や、パトロン数などをウォッチしたいと思います。

今回はスクレイピング編です。

対象とするSPARKSチャンネル

今回はCAMPFIREの中でも、プロトアウトスタジオ x CAMPFIREで現在開催中のSPARKS by BOOSTER STUDIOのチャンネルを対象にスクレイピングしてみます。

Sparks - https://camp-fire.jp/channels/sparks

スクリーンショット 2019-12-18 1.49.15.png

環境など

  • Node.js 13.3.0
  • axios 0.19.0

Node.js 13.3.0で試してみていて、ES Modulesな記述(import)にしてみています。
もし真似しようとしてエラーが出る人は冒頭のimport文をconst axios = require('axios');に書き換えて従来の読み込みにしましょう。

npm init -y
npm i axios

こんな感じで事前に準備はしておきます。

まずは要素の特定

Chromeのディベロッパーツール(右クリック->検証)で各要素の抜き出しをしてみます。

スクリーンショット 2019-12-18 1.48.49.png

何となく、class="box"やdata_project_idの辺りの記述で引っ張ってこれるかもしれないとアタリを付けてみます。

campfire.js
'use strict';

import axios from 'axios';

const CF_URL = `https://camp-fire.jp/channels/sparks`;

axios.get(CF_URL).then(res => {
    const bodyall = res.data;
    let parts = bodyall.split('data_project_id='); // `data_project_id=`の箇所でスプリット
    parts.shift(); //HTML全体の最初を削除
    console.log(parts[0]);
})

この時点でこんなHTMLが取得できます。

"210634"><div class="box-in"><div class="box-thumbnail"><a href="/projects/view/210634?list=channel_sparks"><img class="lazyload" data-srcset="https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=320&amp;h=213&amp;fit=clip&amp;auto=format 320w, https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=414&amp;h=276&amp;fit=clip&amp;auto=format 414w, https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=768&amp;h=512&amp;fit=clip&amp;auto=format 768w, https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=960&amp;h=639&amp;fit=clip&amp;auto=format 960w, https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=1024&amp;h=682&amp;fit=clip&amp;auto=format 1024w" data-sizes="100vw" data-src="https://static.camp-fire.jp/uploads/project_version/image/329602/f8330bbc-b104-437f-a1b0-773052ddd9d6.png?ixlib=rails-2.1.4&amp;w=1120&amp;h=746&amp;fit=clip&amp;auto=format"></a></div><div class="box-title"><a title="EZ-Lapse いつでもどこでも気軽にタイムラプス動画を撮影できるカメラ" href="/projects/view/210634?list=channel_sparks"><h4>EZ-Lapse いつでもどこでも気軽にタイムラプス動画を撮影できるカメラ</h4></a><div class="sub"><p>「いつでもどこでも気軽にタイムラプス動画を。」タイムラプス動画を撮影したことはありますか?確かに素敵な動画が撮れますが、撮影中ずっとスマホが使えず、気軽に撮るこ...</p></div></div><div class="box-date sp-none"><div class="category"><a href="/projects/category/technology"><i class="fa fa-tag"></i> テクノロジー・ガジェット</a></div><div class="ownner"><a href="/profile/takeaship"><i class="fa fa-user"></i> takeaship</a></div></div><div class="meter">
<div class="meter-in"><div class="bar" style="width: 0%;"><span>0%</span></div></div>
<span>0%</span>
</div><div class="overview">
<div class="total" data-js="money-unit">
<small>現在</small>0円</div>
<div class="rest">
<small>パトロン</small>0人</div>
<div class="per">
<small>残り</small>9日</div>
</div></div></div><div class="box  " 

細々と情報を抜き出し

<small>残り</small>9日</div>

9の部分だったり

<small>パトロン</small>0人</div>

0の部分だったりを正規表現で抜き出します。

campfire.js
'use strict';

import axios from 'axios';

const CF_URL = `https://camp-fire.jp/channels/sparks`;

axios.get(CF_URL).then(res => {
    const bodyall = res.data;
    let parts = bodyall.split('data_project_id=');
    parts.shift();

    const part = parts[0]; //0件目

    const project = {};
    project.percentage = part.match(/<span>(.*?)%<\/span>/)[1]; //達成率
    project.yen = part.match(/<\/small>(.*?)円<\/div>/)[1]; //円
    project.patron = part.match(/パトロン<\/small>(.*?)人<\/div>/)[1]; //パトロン数
    project.remaining_days = part.match(/残り<\/small>(.*?)日<\/div>/)[1]; //残り日数
    project.title = part.match(/<a title="(.*?)" href="/)[1]; //タイトル
    project.description = part.match(/<div class="sub"><p>(.*?)...\/p><\/div><\/div>/)[1]; //概要
    project.link = 'https://camp-fire.jp' + part.match(/div class="box-thumbnail"><a href="(.*?)">/)[1]; //リンク

    console.log(project);
})
node campfire.js
(node:2508) ExperimentalWarning: The ESM module loader is experimental.
{
  percentage: '0',
  yen: '0',
  patron: '0',
  remaining_days: '9',
  title: 'EZ-Lapse いつでもどこでも気軽にタイムラプス動画を撮影できるカメラ',
  description: '「いつでもどこでも気軽にタイムラプス動画を。」タイムラプス動画を撮影したことはありますか?確かに素敵な動画が撮れますが、撮影中ずっとスマホが使えず、気軽に撮るこ.',
  link: 'https://camp-fire.jp/projects/view/210634?list=channel_sparks'
}

こんな雰囲気ですね。

あとは複数プロジェクト分処理を回す

campfire.js
'use strict';

import axios from 'axios';

const CF_URL = `https://camp-fire.jp/channels/sparks`;

axios.get(CF_URL).then(res => {
    const bodyall = res.data;
    let parts = bodyall.split('data_project_id=');
    parts.shift();

    for (let i = 0, len = parts.length; i < len; i++) {
        const part = parts[i];
        const project = {};
        project.percentage = part.match(/<span>(.*?)%<\/span>/)[1]; //達成率
        project.yen = part.match(/<\/small>(.*?)円<\/div>/)[1]; //円
        project.patron = part.match(/パトロン<\/small>(.*?)人<\/div>/)[1]; //パトロン数
        project.remaining_days = part.match(/残り<\/small>(.*?)日<\/div>/)[1]; //残り日数
        project.title = part.match(/<a title="(.*?)" href="/)[1]; //タイトル
        project.description = (part.match(/class="sub"><p>(.*?)<\/p>/)) ? (part.match(/class="sub"><p>(.*?)<\/p>/)[1]) : ''; //概要
        project.link = 'https://camp-fire.jp' + part.match(/div class="box-thumbnail"><a href="(.*?)">/)[1]; //リンク
        // project.image = part.match(/class=" lazyloaded" data-srcret="(.*?)">/)[1]; //画像
        console.log(project);      
    }

})
$ node campfire.js

・
・
・
{
  percentage: '56',
  yen: '5,700',
  patron: '7',
  remaining_days: '4',
  title: '【おかたづけ】こどもが自分で片付けしたくなるIoTおもちゃ箱',
  description: 'こどもがおもちゃを散らかしっぱなしにして困っているお母さんお父さん大助かり!おもちゃ箱を電子工作して子供が自分で片付けしたくなります!',
  link: 'https://camp-fire.jp/projects/view/211804?list=channel_sparks'
}
{
  percentage: '10',
  yen: '13,000',
  patron: '11',
  remaining_days: '4',
  title: 'オフィスワーカー向け 座り過ぎを解決するクッション CiliCill シリシル',
  description: '世界一の「座りすぎ大国」日本そんな日本人特有の問題を解決したい!座ってる時間がわかるクッション CiliCill -シリシル-を作りました。一日の中で自分がどの...',
  link: 'https://camp-fire.jp/projects/view/207642?list=channel_sparks'
}

こんな感じでプロジェクトの情報を抜き出せました。

次回

次はこれを定期実行させる&チャット通知させる予定です。

n0bisuke
プロトタイピング専門スクール「プロトアウトスタジオ」で教えたりしてます。 プロフ -> https://dotstud.io/members/n0bisuke
https://protoout.studio
dotstudio
全ての人がモノづくりを楽しむ世界を目指して活動しています。
https://dotstud.io
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした