【実用的】スクレイピング・データをWebサイトに応用


【実用的】スクレイピング・データをWebサイトに応用

人工知能にブロックチェーンに、データ解析と業務・私用関係なくPythonに触れる機会が多い昨今。

今までWordPressや他のブログエンジンでメディア運営されていた方の中には、「WebでPython使えた方が学習効率よくない?」と思われる方もいらっしゃるのでは。

そこで今回は、Pythonの学習事例で多い「スクレイピング」で取得するデータをWebページに反映させる、ということを行ってみます。

目次
  1. 【実用的】スクレイピング・データをWebサイトに応用
  2. 今回試す内容
  3. 準備
  4. スクレイピング・ターゲットの選定
  5. クラウドワークスのデータをスクレイピング
  6. スクレイピングしたデータをHTMLファイル内で読み込み
  7. シュフティのデータをスクレイピング
  8. 最終的なコード
  9. まとめ

【実用的】スクレイピング・データをWebサイトに応用

今回試す内容

image

今回は、サンプルのプロフィールページに、フリーランス仲介サイトで公開されている実績数などを引用してみます。

仲介サイトで表示されるデータを自分のプロフサイトに反映できますので、仕事の実績がライブで反映。

これでイチイチ自分のプロフィール・データ更新を気にしなくても、仕事に集中するだけで数字が伸びます。

そしてまたそれを見たクライアントの方から仕事のオファーをもらえるかもしれません。

いい循環が生まれそうですね。

準備

image

  • Jupyter Notebook
  • テキストエディタ
  • bottle.py

PythonコードをWebブラウザで手っ取り早く実行させようと思うと「Bottle」というWebフレームワークが便利です。 フレームワークというと初期設定や内容理解に時間が、、、と思われるかもしれませんが、今回は bottle.py と index.html、 スクレイピング用Pythonファイルの 3つのみ使います。

Jupyter Notebook は、Webスクレイピングしていく中で最終抜き取りたいデータまでのプログラム加工に便利なため、使用します。

スクレイピング・ターゲットの選定

image

今回は、Webスクレイピングで有名な「Beautiful Soup4」を使って作業していきます。

データ引用したいターゲットサイトは以下の通りにしました。

  • Lancers(ランサーズ)
  • CrowdWorks(クラウドワークス)
  • Shufti(シュフティ)

まずWebスクレイピングがブロックされていないか確認してみます。

【ランサーズ 自分のアカウント】

import requests
url = requests.get('https://www.lancers.jp/profile/oshima_masara')
url

【クラウドワークス 自分のアカウント】

url = requests.get('https://crowdworks.jp/public/employees/14218')
url

【シュフティ トップユーザー】

url = requests.get('https://www.shufti.jp/users/profile/113910/contract')
url

シュフティ新規アカウント作成するも、データ反映されないためトップユーザーの Y.Matsuzaki_fortyoneさんのプロフィールを参考にさせて頂きました。ご了承ください。

<< 実行結果 >>

image

なんとランサーズは 403 の読み込みエラー。

403:アクセスしたページはインターネット上に存在するが、閲覧することが許可されていません。

あとの2件は読み込み可能なので、クラウドワークスとシュフティの2つからプロフィールデータを引用してみます。

尚、3件とも現段階(2018.07.25)ではAPI公開されていませんでした。

クラウドワークスのデータをスクレイピング

クラウドワークスの自分の実績をスクレイピングしてみます。

まずは自分のプロフィールページ https://crowdworks.jp/public/employees/14218 にアクセスして、実績数の CSS を確認。

image

span.score contracts とスクレイピングに適した CSS です。

それでは Beautiful Soup の雛形に上記要素をあてはめてみます。

import requests, bs4
res = requests.get('https://crowdworks.jp/public/employees/14218')
soup = bs4.BeautifulSoup(res.text, "html.parser")
elems = soup.select('span.score.contracts')
elems

<< 実行結果 >>

image

今回は、実績数だけ欲しいので getText() 関数を使ってデータ整形。

image

いいですね、クラウドワークス上の実績数と同じ "10" を引用できています。

せっかくなので評価も引用。

image

import requests, bs4
res = requests.get('https://crowdworks.jp/public/employees/14218')
soup = bs4.BeautifulSoup(res.text, "html.parser")
elems = soup.select('span.score.feedbacks')

for elem in elems:
    out = elem.getText()
out

<< 実行結果 >>

image

欲しいデータを引用できたので、一旦ここで Webページに表示される作業をしてみます。

スクレイピングしたデータをHTMLファイル内で読み込み

Pythonでスクレイピングしたデータを変数化して、それを Bottle の書式にあてはめることでスクレイピングしたデータをWeb上に表示できます。

【編集するファイル】

  • scraping.py
  • index.html

解説の仕方は色々ありますが、とりあえず答えから。

scraping.py

from bottle import route, run, template, redirect, request
from requests import get
from bs4 import BeautifulSoup

@route("/")
def index():
    number1 = get_number1()
    number2 = get_number2()

    return template("index", out1=number1, out2=number2)

def get_number1():
    url = 'https://crowdworks.jp/public/employees/14218'
    response = get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    elems = soup.select('span.score.contracts')
    for elem in elems:
        out1 = elem.getText()
    return out1

def get_number2():
    url = 'https://crowdworks.jp/public/employees/14218'
    response = get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    elems = soup.select('span.score.feedbacks')
    for elem in elems:
        out2 = elem.getText()
    return out2

run(host="localhost", port=1029, debug=True, reloader=True)


index.html

<!DOCTYPE html>
<html lang="jp">
<head>
<meta charset="UTF-8">
<title>フリーランス向けプロフページのサンプル(Python3 + Bottle)</title>
</head>
<body>
<hr>
<hr>
<p style="font-size:2em">クラウドワークス受注件数: {{out1}} 件</p>
<p style="font-size:2em">クラウドワークス評価: {{out2}}</p>
<a href="https://crowdworks.jp/public/employees/14218"  target="_blank">わたしの実績</a>
<hr>
<hr>
</body>
</html>

ディレクトリ構成

image

<< 実行結果 >>

こちらはターミナルもしくはコマンドプロンプトから以下のコードを実行。

python scraping.py

そして指定したローカルホスト http://localhost:1029 にアクセスします。

解説

まず scraping.py の方では、『クラウドワークスのデータをスクレイピング』の章でご紹介したコードを get_number1()get_number2() にモジュール化。

そしてそれを index() 関数内の number1number2 という変数に代入。

最後に template() 関数に number1number2out1out2 に変数化して出力。

出力先は、最終行の run() 関数で制御。

次に index.html側。

こちらは {{out1}}{{out2}} とダブル波カッコで先ほどの out1out2 を囲むと出力。

image

ちょっと見た目が寂しいので、上記の index.html のようにコード編集しています。

シュフティのデータをスクレイピング

同じ要領で シュフティ のデータもスクレイピングしたいと思います。

シュフティは自分のアカウントがリストアップされていないため、トップユーザーの Y.Matsuzaki_fortyone さんを参考にさせて頂きました。

image

画像引用:シュフティ Y.Matsuzaki_fortyone さま

それでクラウドワークスと同じ要領で作業しようと思った矢先、なんと欲しいデータのCSS構造が他のデータと一緒。つまりデータを特定できないんですね。

そこでシュフティの場合は、段階的にコードを絞って、最終的なデータ抽出に結び付けたいと考えました。

ステップ1

まずWebページの抽出方法を、response.text, "html.parser" から page.content, "lxml" に変えます。

理由は、CSSコードではなくHTMLコードで取得したいデータまでたどり着きたいからです。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
soup

<< 実行結果 >>

image

ステップ2

次に欲しいデータ(納品実績)の周辺HTMLコードを見ていくと、リストタグの <ul> に納まっていることが確認できます。

image

Beautiful Soupの find() 関数を使って、<ul> コードを抽出できるか確認してみます。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
box = soup.find('ul' ,'stripe_list')
box

<< 実行結果 >>

image

ちょっと欲しいデータに近づけた気がします。

ステップ3

欲しいデータは、<li>タグ内に入っていますので、<li>タグのみの表示結果になるようにもう一段階絞り込みを行います。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
box = soup.find('ul' ,'stripe_list')
box_1  = box.findAll('li')
box_1

<< 実行結果 >>

image

ステップ4

一番簡単な方法で作業を進めます。 ステップ3の出力結果をよく見てみると、データ形式はリスト構造です。出力データが [] で囲まれていますよね。

このリスト構造の特徴を活かして、欲しいデータの順位を確認。

image

3番目ですね、コードに書いてみます。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
box = soup.find('ul' ,'stripe_list')
box_1  = box.findAll('li')
work_count = box_1[2]
work_count

<< 実行結果 >>

image

おお、だいぶ目的のデータまで近づけたように思います。

ステップ5

次に <li> とか <strong> とかいらないので、 getText()関数でテキストのみの表示に変換。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
box = soup.find('ul' ,'stripe_list')
box_1  = box.findAll('li')
work_count = box_1[2]
work_count_2 = work_count.getText()
work_count_2

<< 実行結果 >>

image

ステップ6

最後に数字だけ取得するために、、、今回はリスト機能を使いました。

import requests
from bs4 import BeautifulSoup
page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
soup = BeautifulSoup(page.content, "lxml")
box = soup.find('ul' ,'stripe_list')
box_1  = box.findAll('li')
work_count = box_1[2]
work_count_2 = work_count.getText()
out3 = work_count_2[1:4]
out3

<< 実行結果 >> image

最終的なコード

シュフティの納品実績も抽出出来ましたので、こちらに最終的なコードをご紹介。

scraping.py

from bottle import route, run, template, redirect, request
from requests import get
import requests
from bs4 import BeautifulSoup

@route("/")
def index():
    number1 = get_number1()
    number2 = get_number2()
    number3 = get_number3()

    return template("index", out1=number1, out2=number2, out3=number3)

def get_number1():
    url = 'https://crowdworks.jp/public/employees/14218'
    response = get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    elems = soup.select('span.score.contracts')
    for elem in elems:
        out1 = elem.getText()
    return out1

def get_number2():
    url = 'https://crowdworks.jp/public/employees/14218'
    response = get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    elems = soup.select('span.score.feedbacks')
    for elem in elems:
        out2 = elem.getText()
    return out2

def get_number3():
    page = requests.get('https://www.shufti.jp/users/profile/113910/contract')
    soup = BeautifulSoup(page.content, "lxml")
    box = soup.find('ul' ,'stripe_list')
    box_1  = box.findAll('li')
    work_count = box_1[2]
    work_count_2 = work_count.getText()
    out3 = work_count_2[1:4]
    return out3

run(host="localhost", port=1055, debug=True, reloader=True)

index.html

<!DOCTYPE html>
<html lang="jp">
<head>
  <meta charset="UTF-8">
  <title>フリーランス向けプロフページのサンプル(Python3 + Bottle)</title>
</head>
<body>
<hr>
<hr>
<p style="font-size:2em">クラウドワークス受注件数: {{out1}} 件</p>
<p style="font-size:2em">クラウドワークス評価: {{out2}}</p>
<a href="https://crowdworks.jp/public/employees/14218"  target="_blank">わたしの実績</a>
<hr>
<hr>
<p style="font-size:2em">Shufti納品実績: {{out3}}</p>
<a href="https://www.shufti.jp/users/profile/113910/contract"  target="_blank">Y.Matsuzaki_fortyoneさんの実績引用</a>
<br>
<hr>
<hr>
</body>
</html>

<< 実行結果 >>

python scraping.py

image

AIエンジニアに必要なスキルが身に付く

無料カウンセリングはこちら

まとめ

今回フリーランスの実績を自動的に更新できるWebページを作ってみましたが、手間に感じましたでしょうか?

私自身シュフティの実績数引用は、サンプルコードもなくできるかな?と思いましたが、一つ一つコードを分解し、Pythonの基本機能を思い出しながら進めてみると、スクレイピングに成功しました。恐らくシュフティのスクレイピングは、正規表現でも難しく、今回のやり方が簡単でよかったように思います。楽しかったです。

Pythonの基礎を理解していればこうしたスクレイピングの場面をはじめ、色々なところで応用が効きますので、”基礎”早い段階で習得しておきたいですね。

CodeCampなら忙しくても、遠方でもオンラインなので大丈夫です。またマンツーマン方式を採用していますので、周りを気にせず聞きたいことを聞けます。Pythonの基礎、身につけてみませんか?


オシママサラ
この記事を書いた人
オシママサラ
まずは7日間お試し!人気プログラミング講座を無料公開中
オンライン・プログラミングレッスンNo.1のCodeCamp