学習内容

  • Pythonを利用した、Webスクレイピング を学びます。
  • 環境構築も不要で、Googleアカウントがあれば簡単に実行できる、Google Colaboratoryを利用します。

受講における必須条件

  • Windows / MacなどのPCが利用できる方
  • Google Chromeをインストールできる方
  • Pythonの基礎的な文法がわかる方
  • Python スクレイピング チュートリアルを完了されている方

事前に、以下のチュートリアルを完了しておくことをおすすめします。

Progate | Pythonコース
プログラミングの基礎を、環境構築なしで学べる、日本最大級のプログラミング学習サイト。まず、基礎を学ぶならここでやってみましょう。
Pythonで環境構築をしてみよう
Google Drive上で簡単に環境構築ができる、Google Colaboratoryを利用して、環境構築を行います。10分ほどで簡単に環境構築することができるので、初学者の方におすすめです。
Pythonによるスクレイピング①入門編|スクレイピング を用いて、データを自動抽出してみよう
PythonでWebスクレイピング を学びます。Webスクレイピング は、オンライン上でページのデータを読み取り、自動収集する技術となります。今回は、ブログから記事タイトルとURLを自動収集する方法を学びます。

このチュートリアルで完成するもの

今回は、環境構築一切不要でスクレイピングを学べるように解説します。 このコンテンツでは、ブログデータのカテゴリを全て取得して、「次へ」ボタンが存在しなくなるまで全記事収集するという方法を学んでいきたいと思います。 下記が、取得するCSVのイメージとなります。 普通に情報収集すると、一ページずつ「次へ」を押して、それをCSVにコピペして…とやっていく必要があるのですが、これをPythonで自動化していきたいと思います。

対象者

以下のようなニーズがある方には、おすすめです。

  • ポータルサイト(転職サイトなど)の全データを収集したいけど、自分の手でやるにはあまりにも時間がかかりすぎる…
  • Python スクレイピングコースは学んだけど、もう少し発展的なことを学習してみたい
  • 日々の情報収集業務でスクレイピング を利用してみたい

データ取得のイメージ

解説箇所:5:21


それでは、さっそく進めていきましょう。 今回は、ある仕事が業務であるとします。

  • Pythonに関連する記事の名前と、URLを全部取得してほしい

実際に、自分の手でやるとしたら、どんな感じになるでしょう。 以下のサイトにアクセスしてみてください。 https://dividable.net 最初にわかるのが、カテゴリがヘッダーに存在するということですね。 それぞれのカテゴリページにアクセスすると、カテゴリ一覧ページに進みます。 Python学習に関するページが必要なので、上記のカテゴリである、「Python学習」にアクセスします。 https://dividable.net/category/python ページの下の方まで見ると、ページャーと呼ばれる、次のページに移動できるボタンがありますね。 「次へ」を押すと、以下のようなページに遷移します。 https://dividable.net/category/python/page/2 同様に、さらに上記のページで「次へ」を押すと、 https://dividable.net/category/python/page/3 にアクセスされます。 さて、次へが存在しないページである、5ページ目にアクセスしてみましょう。 https://dividable.net/category/python/page/5/ そうすると、以下のページになります。 みてもらえるとわかるように、Python学習に関わる、全ての記事を取得する場合は、

  • Python学習をクリックして、カテゴリページに飛ぶ
  • 記事一覧を全てスプレッドシートにコピーする
  • 次へがあったら、押して、次のページに移動する
  • 同じように記事一覧をコピーする
  • 次へがなくなるまでやる…

といった、大変な作業になります。 このページ自体、10ページもないので、なんとかできてしまいますが、例えば

  • 転職サイトの求人の特定のカテゴリを取得したい

という場合は、非常に大変ですよね。 ですので、このような仕事を、Pythonで自動化してしまいましょう。

作業をプログラムに落とす

さて、まずは作業を、プログラムに落としていきましょう。 さきほどは、Python学習に関するカテゴリを全て取得しましたが、全カテゴリを収集することにしましょう。 さて、そういう場合は、

  1. 全カテゴリのURLと、カテゴリ名を取得する
  2. カテゴリURLにアクセスする
  3. カテゴリURLの記事のタイトルと、URLと、カテゴリ名を列に追加する
  4. 「次へ」があれば、次のページに遷移する
  5. 次のページがなくなるまで繰り返す
  6. 一つにカテゴリが終わったら、次のカテゴリへ移動する
  7. 全てのカテゴリが終わったら、終了する

といった形にコードを組んでいきます。

①全カテゴリのURLと、カテゴリ名を取得する

それでは、さっそくカテゴリのURLとカテゴリ名を取得しましょう。画像でいうと、ここの部分です。

  • Requests,
  • Pandas,
  • BeautifulSoup

を利用して進めます。もしわからない方は、こちらのチュートリアルを進めてください。

Pythonによるスクレイピング①入門編|スクレイピング を用いて、データを自動抽出してみよう
PythonでWebスクレイピング を学びます。Webスクレイピング は、オンライン上でページのデータを読み取り、自動収集する技術となります。今回は、ブログから記事タイトルとURLを自動収集する方法を学びます。
まずは、ライブラリをインポートします。

import requests
import pandas as pd
from bs4 import BeautifulSoup

まずは、ヘッダーのカテゴリ部分をとりたいので、 https://dividable.net のヘッダー部分を取得しましょう。

print (requests.get("https://dividable.net").text)

そうすると、ヘッダーの部分は、以下のように出力されます。

... 中略
<nav id="category" class="col-md-12 col-12">
<ul><li><a href="https://dividable.net/category/career/">IT転職</a></li>
<li><a href="https://dividable.net/category/sidework/">IT副業</a></li>
<li><a href="https://dividable.net/category/freelance/">ITフリーランス</a></li>
<li><a href="https://dividable.net/category/nisotsu-tenshoku/">一般転職</a></li>
<li><a href="https://dividable.net/category/programming-school/">プログラミングスクール</a></li>
<li class="current"><a href="https://dividable.net/category/python/" aria-current="page">Python学習</a></li>
<li><a href="https://dividable.net/try-python-scraping-lp/">無料チュートリアル</a></li>
<li><a href="https://dividable.net/lp1/">DAINOTE</a></li>
</ul>
</nav><!-- col-md-7-->
</div><!-- row -->
</div><!-- container -->
</header>

カテゴリの中のURLと、名前は、 nav#category > ul > li > a タグの中に存在するので、それらのaタグを取得してみます。
aタグをCSS Selectorを利用して取得する場合は、nav#category ul li aとなります。(id=categoryのnavの中の、ulの中のliの中のaタグ)

category_li_path = "nav#category ul li a"
res = requests.get("https://dividable.net").text
soup = BeautifulSoup(res, 'html.parser')
soup.select(category_li_path)

selectという関数の引数には、CSSセレクターを入れると、合致するタグをリスト形式で取得してくれます。
参考)Beautiful Soup のfind_all( ) と select( ) の使い方の違い
出力すると、こんな感じになります。

[<a href="https://dividable.net/category/career/">IT転職</a>,
 <a href="https://dividable.net/category/sidework/">IT副業</a>,
 <a href="https://dividable.net/category/freelance/">ITフリーランス</a>,
 <a href="https://dividable.net/category/nisotsu-tenshoku/">一般転職</a>,
 <a href="https://dividable.net/category/programming-school/">プログラミングスクール</a>,
 <a href="https://dividable.net/category/python/">Python学習</a>,
 <a href="https://dividable.net/try-python-scraping-lp/">無料チュートリアル</a>,
 <a href="https://dividable.net/lp1/">DAINOTE</a>]

これらのURLとカテゴリを、今後ループを回して、それぞれのカテゴリのページを取りに行く都合上、データを扱いやすいように、辞書型(dictionary)のデータに、URLとカテゴリを変換します。

category_html_list = soup.select(category_li_path)
category_dict = {}
for category_html in category_html_list:
    category_dict[category_html.get("href")] = category_html.string
print (category_dict)

出力すると、以下のようになります。

{'https://dividable.net/category/career/': 'IT転職', 'https://dividable.net/category/sidework/': 'IT副業', 'https://dividable.net/category/freelance/': 'ITフリーランス', 'https://dividable.net/category/nisotsu-tenshoku/': '一般転職', 'https://dividable.net/category/programming-school/': 'プログラミングスクール', 'https://dividable.net/category/python/': 'Python学習', 'https://dividable.net/try-python-scraping-lp/': '無料チュートリアル', 'https://dividable.net/lp1/': 'DAINOTE'}

辞書型について簡単に解説すると、key, valueのペアでデータを取得することができます。例えば、IT転職のデータが欲しければ、

category_dict["https://dividable.net/category/career/"]

と出力すると、

IT転職

と返ってきます。

③カテゴリURLにアクセスする

それではカテゴリURLにアクセスしましょう。 最終的には、全てのカテゴリに自動アクセスするコードを書きますが、今後のカテゴリページからデータをとるコードをかくために、Python学習のカテゴリページのみを取得して、出力してみます。

category_res = requests.get("https://dividable.net/category/python/").text
print (category_res)

③カテゴリURLの記事のタイトルと、URLと、カテゴリ名を列に追加する

次に、カテゴリURLの中にある複数の記事の

  • タイトル
  • URL
  • カテゴリ

を自動取得します。 ただし、一旦こちらは後から手をつけていましょう。

③「次へ」があれば、次のページに遷移する

次に、③で記事のデータを取得したとして、「次へ」があれば、次のページに遷移するアクションを実装してみましょう。例えば、現在

  • https://dividable.net/category/python/page/2

にいるとします。 記事に「次へ」があれば、クリックすると

  • https://dividable.net/category/python/page/3

に飛ぶんでしたね。

※ちなみに、https://dividable.net/category/python/page/1 は存在しませんが、https://dividable.net/category/python/page/1にアクセスすると、https://dividable.net/category/python/ にリダイレクト(自動遷移)します。

ですので、「次へ」が存在すれば、page番号を+1してあげてデータを取得すれば良いわけです。これを実装していきます。 まず、「次へ」があるかを確認しますね。 次へに該当するソースコードをみてみましょう。
HTMLをみていきます。

<div class="pagination">
   <span class="page_num">Page 1 of 5</span>
   <span class="current pager">1</span>
   <a href="https://dividable.net/category/python/page/2/" class="pager">2</a>
   <a href="https://dividable.net/category/python/page/3/" class="pager">3</a>
   <a href="https://dividable.net/category/python/page/2/" class="next">次へ ›</a>
   <a href="https://dividable.net/category/python/page/5/" class="last">最後へ »</a>
</div>

ここの

<a href="https://dividable.net/category/python/page/2/" class="next">次へ ›</a>

が、次へに当たる部分のaタグとなっています。 ですので、以下のように実装すると、次へボタンが存在するか確認できます。

soup = BeautifulSoup(category_res, 'html.parser')
a_next_tag= soup.find_all("a", {"class": "next"}) # 次へがあるか確認するコード

BeautifulSoupのfind_allメソッドは、引数の最初にタグ、後半に属性, 値を指定すると、該当するタグのみをリストで返してくれます。 それでは、さっそくコードを書いていきます。
実際に、Pythonはカテゴリが4までは「次へ」があり、5になるとなくなります。試しに出力していましょう。

category_res = requests.get("https://dividable.net/category/python/").text
soup = BeautifulSoup(category_res, 'html.parser')
a_next_tag= soup.find_all("a", {"class": "next"}) # 次へがあるか確認するコード
print (a_next_tag)

出力結果

[<a class="next" href="https://dividable.net/category/python/page/2/">次へ ›</a>]

ありましたね。同様に、Page=3でも、同じような結果になると思います。

category_res = requests.get("https://dividable.net/category/python/page/3").text
soup = BeautifulSoup(category_res, 'html.parser')
a_next_tag= soup.find_all("a", {"class": "next"}) # 次へがあるか確認するコード
print (a_next_tag)

出力結果

[<a class="next" href="https://dividable.net/category/python/page/4/">次へ ›</a>]

一方で、5ページ目になると、記事数がそれ以上ないので、a_next_tagの中身は空になります。

category_res = requests.get("https://dividable.net/category/python/page/5").text
soup = BeautifulSoup(category_res, 'html.parser')
a_next_tag= soup.find_all("a", {"class": "next"}) # 次へがあるか確認するコード
print (a_next_tag)

出力結果

[]

以上を踏まえて、次へボタンが存在する場合は、次のページへ、存在しない場合は処理を止めるようにプログラミングしてあげれば良いわけですね。
また、ヒントですが、次のページは、URLで/category/python/page/1 のように、pageの後の数字を+1してあげれば、次のページにアクセスできるはずです。
TODO

  • 次へが存在する場合、次のページを取得する ②存在しない場合、処理を中断する

実装結果

page_count = 1
category_res = ""
soup = ""
while True:
    category_res = requests.get("https://dividable.net/category/python/" + "page/" + str(page_count)).text
    soup = BeautifulSoup(category_res, 'html.parser') # BeautifulSoupの初期化
    print ("{} ページ目".format(page_count))
    a_next_tag= soup.find_all("a", {"class": "next"})
    if a_next_tag:
        page_count += 1
        continue
    break
print ("完了")

コードを解説すると、まずpage_countという変数を利用して、取得するページを定義しています。そして、次へボタンが存在する場合は、数字に1をプラスしてあげ、continueで再度while文に入っています。 一方で、存在しない場合は、break文で処理を中断させています。 このようにすると、Python学習の全てのページにアクセスすることができます。

③それぞれのカテゴリページから、記事の内容を取り出す

それでは、次にデータ https://dividable.net/category/python/page/2 のような、カテゴリページにアクセスし、その中でそれぞれの記事のURLを確認すると、以下のようになります。

<div class="post">
   <div class="post-content__time float-left">
      <span class="post-content-time__updated">2019/07/28</span>
   </div>
   <div class="post-header">
      <a href="https://dividable.net/python/python-freelance-beginner/">
         <img width="100%" height="auto" class="post-image" src="https://dividable.net/wp/wp-content/uploads/2019/03/data.jpg">
      </a> </div><!-- post-header -->
      <div class="post-content">
         <h3 class="post-content__title" id="single__title">
            <a href="https://dividable.net/python/python-freelance-beginner/">【フリーランス】Pythonで独立! 年収・おすすめ求人サイト・心得まとめ</a>
         </h3>
         <div class="post-content__text">≪この記事で紹介するフリーランス求人サイト一覧≫ 【第1位】ミッドワークス:フリーランスでも正社員並みの福利厚生待遇を受けられるITフリーランス向け求人サイト。 【第2位】レバテックフリーランス : 高報酬な案件が豊富。エージェントがITに詳しい求人サイト。 【第3位】ビッグデー... </div>
         <!-- post-content__text -->
      </div><!-- post-content -->
...

ここで取得したいのは、タイトルとURLなので、h3タグと、その中のaタグをとればよいわけですね。 コードとしては、以下の部分となります。

<h3 class="post-content__title" id="single__title">
            <a href="https://dividable.net/python/python-freelance-beginner/">【フリーランス】Pythonで独立! 年収・おすすめ求人サイト・心得まとめ</a>
         </h3>

ですので、以下のように実装することができます。

page_count = 1
category_res = ""
soup = ""
while True:
    print ("------------{} ページ目------------".format(page_count))
    category_res = requests.get("https://dividable.net/category/python/" + "page/" + str(page_count)).text
    soup = BeautifulSoup(category_res, 'html.parser') # BeautifulSoupの初期化
    post_tags = soup.select("div.post")
    for post_tag in post_tags:
        print (post_tag.select("h3")[0].text) # タイトル
        print (post_tag.select("a")[0].get("href")) # url
        print ("Python学習")
    a_next_tag= soup.find_all("a", {"class": "next"})
    if a_next_tag:
        page_count += 1
        continue
    break
print ("完了")

以下のコードの部分は、ほとんど前回のPythonスクレイピング チュートリアルと一緒なので、わからなかったら参考にしてみてください。

Pythonによるスクレイピング①入門編|スクレイピング を用いて、データを自動抽出してみよう
PythonでWebスクレイピング を学びます。Webスクレイピング は、オンライン上でページのデータを読み取り、自動収集する技術となります。今回は、ブログから記事タイトルとURLを自動収集する方法を学びます。

全カテゴリからデータを収集しよう

ここまでくれば、あとは全カテゴリからデータを収集するだけですね。 上記のコードを理解していれば、自分で実装できると思うので、まずは自分で試してみましょう。
TODO

  1. requests, pandas, beautifulsoupをインポートしてください
  2. url, title, categoryを列に持ったDataFrameを作成してください
  3. カテゴリURLとカテゴリ名を持った辞書型オブジェクトを作成してください
  4. カテゴリを一つ一つ取り出して、ページャーの最後まで記事を取得してください。
  5. result.csvというファイル名で、CSVに記事を保存してください。

答え

#TODO requests, pandas, beautifulsoupをインポートしてください
import requests
import pandas as pd
from bs4 import BeautifulSoup
#TODO url, title, categoryを列に持ったDataFrameを作成してください
columns = ["url", "title", "category"]
df = pd.DataFrame(columns=columns)
#TODO カテゴリURLとカテゴリ名を持った辞書型オブジェクトを作成してください
category_li_path = "nav#category ul li a"
soup = BeautifulSoup(res, 'html.parser')
category_html_list = soup.select(category_li_path)
category_dict = {}
for category_html in category_html_list:
   category_dict[category_html.get("href")] = category_html.string #URL, カテゴリ名をセットにする
## TODO: カテゴリを一つ一つ取り出して、ページャーの最後まで記事を取得し、CSVに記事を保存してください。
for key, value in category_dict.items():
   """
   k: url
   v: カテゴリ名
   """
   print ("------カテゴリ: {} ------".format(value))
   page_count = 1
   category_res = ""
   soup = ""
   while True:
      print ("------{} ページ目 ------".format(page_count))
      category_res = requests.get(key + "page/" + str(page_count)).text
      soup = BeautifulSoup(category_res, 'html.parser')
      post_tags = soup.select("div.post")
      for post_tag in post_tags:
         #ページのタイトル, URL,カテゴリを取得する
         title = post_tag.select("h3")[0].text
         url = post_tag.select("a")[0].get("href")
         se = pd.Series([title,url,value], columns)
         df = df.append(se, ignore_index=True)
      a_next_tag= soup.find_all("a", {"class": "next"})
      if a_next_tag:
         page_count += 1
         continue
      break
print ("完了")
#TODO result.csvというファイルとして出力してください。
df.to_csv("result.csv")

おそらく、つなげ合わせるだけなので理解できるかと思いますが、補足を入れますね。

for key, value in category_dict.items():

ここのコードですが、辞書型のkey, valueをfor文でかけています。 dict.items()を利用すると、forの要素に、keyとvalueを二つ代入して、それぞれを回せます。

  • key: url
  • value: category

が入るようになります。 詳しくは、こちらの記事を参考にしてみてください。

なお、CSVファイルとしてダウンロードする場合は、以下のコードを実行すると可能です。

from google.colab import files
files.download("result.csv")

完成

これで完成です!お疲れ様でした!
学習をTwitterに記録する