manablogをPythonでスクレイピングしてブログ情報を取得する

manablogをPythonでスクレイピングしてブログ情報を取得する

Bicepper

こんにちは!Bicepperです。
今回はブロガーとして有名なマナブさんのmanablogからデータを取得してみようと思います!
え!?
取得って、どうやるの?

プログラミング初心者

Bicepper

盗作とかではないですよ!
Pythonスクレイピングをします。

アフィリエイターやブロガーが、ブログを収益化のために参考する有名なブログといえば、マナブさんのmanablog.orgですよね!

ブログの要素から何か分析できないかと思い、今回はPythonでスクレイピングしてデータを収集してみることにしました。

Pythonが何か知らない人は、まずこちらの記事をどうぞ!

Pythonで何ができる?Pythonを学習して未来をつかめPythonで何ができる?プログラミングでPythonを学習して未来をつかめ

スクレイピングの注意点

スクレイピングを始める前に、まず先にスクレイピングの注意点を紹介しておきます。

そして、必ず守ってください

  • 節度あるスクレイピングをしよう
  • 著作権があることを忘れないようにしよう

スクレイピングについての詳しいことはググればたくさん出て来ますが、例えばこちらのQiitaも参考になります。

参考 Webスクレイピングの注意事項一覧Qiita

スクレイピングは、相手のサーバに一定の負荷をかける行為です。
アクセスする際は、必ず一定(例えば1秒以上)間隔を空けることを意識します。

スクレイピングに関連するこんな事件もありましたので、ぜひ読んでみてください。

参考 岡崎市立中央図書館事件Wikipedia

また、スクレイピングした情報の著作権は消えません

あくまでも、情報解析のためのみに使用しましょう。

Bicepper

まちがっても盗作なんかしちゃダメですよ!

スクレイピングのPythonコード解説

それでは先にスクレイピングのPythonコードを見ていきましょう!

初心者の方にはちょっと難しいかもしれませんが、スクレイピングはルールを守れば非常に有用です。

import urllib.request
import urllib.error
import time
import re
import pandas as pd
from bs4 import BeautifulSoup

# ブログURL指定
url = "https://manablog.org/"

# ユーザーエージェント偽装
headers = {
    "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0",
}

request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
soup = BeautifulSoup(response, "html.parser")

# ページ数取得
blog_page = soup.find("ul", attrs={"class", "list-unstyled"})
blog_page_list = blog_page.find_all('li')
blog_page_dict = {str(x.find_all("a")[0].get("href")): re.search('\(([^)]+)', str(x)).group(1) for x in blog_page_list}

for key, value in blog_page_dict.items():
    now_month = key.split("/")
    print("現在【{}_{}】分を取得中".format(now_month[3], now_month[4]))
    # CSV用
    blog_output = {}

    # 今回は7月分のみ取得
    if int(value) == 30:
        break

    # 月別でページ分ループ
    for pages in range(int(value) // 10):
        in_request = urllib.request.Request(url="{}{}".format(key, "page/{}".format(pages+1) if pages != 0 else ""),
                                         headers=headers)
        in_response = urllib.request.urlopen(in_request)

        in_soup = BeautifulSoup(in_response, "html.parser")
        in_soup_list = in_soup.find_all("div", attrs={"class", "readmore"})
        in_soup_link = [x.find_all("a")[0].get("href") for x in in_soup_list]

        # 個別記事別分ループ
        for item_page in in_soup_link:
            item_request = urllib.request.Request(url=item_page, headers=headers)
            item_response = urllib.request.urlopen(item_request)
            item_soup = BeautifulSoup(item_response, "html.parser")

            blog_title = item_soup.find("h1")  # タイトル取得
            blog_content = item_soup.find("div", itemprop="articleBody")  # 記事内容取得
            blog_output[blog_title] = blog_content

            time.sleep(1)

    df = pd.DataFrame(blog_output.items(), columns=['タイトル', 'コンテンツ'])
    df.to_csv('{}_{}.csv'.format(now_month[3], now_month[4]))

print("終了")

今回使用しているPythonの主なパッケージを紹介しておきます。

  • BeautifulSoup:HTMLからデータを抽出するためのPythonパッケージ
  • pandas:データ解析をより簡単に、高速にしてくれるPythonパッケージ

それでは順番に見ていきましょう。

最初の箇所でmanablogのURLを定義しており、その次でユーザーエージェントを偽装しています。

これは、クローリング禁止を回避するための方法です。

Webサイト側でクローリングの禁止が設定されているとChromeのままではスクレイピングできないため、FireFoxに偽装しています。今回は特にチェックしていませんが、形式的に入れています。

# ブログURL指定
url = "https://manablog.org/"

# ユーザーエージェント偽装
headers = {
    "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0",
}

次の箇所で、html.parserを使用してHTMLを取得しています。

request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
soup = BeautifulSoup(response, "html.parser")

さて、ここで今回のスクレイピングのゴールを確認しておきましょう。

  • ブログの記事をアーカイブごとに取得
  • スクレイピングの結果をアーカイブ別にCSVで出力

闇雲にデータを入力するのでは、あまり汎用性がありません。

そこでブログの記事をアーカイブごとに区切って取得することにして、その結果もアーカイブごとにCSVで出力します。

そのため、最初にブログにアクセスした段階でアーカイブの一覧アーカイブごとに格納されている記事数を取得し、dict形式で保存します。

# ページ数取得
blog_page = soup.find("ul", attrs={"class", "list-unstyled"})
blog_page_list = blog_page.find_all('li')
blog_page_dict = {str(x.find_all("a")[0].get("href")): re.search('\(([^)]+)', str(x)).group(1) for x in blog_page_list}

manablogのソースコードの特性上、リンクはBeautifulSoupのfind_allで簡単に取得できますが、記事の総数は何も特徴が無いためそのままでは取得できません。

そこで、()カッコの中だけを取得するように、Pythonの正規表現を組み合わせています。

以上の組み合わせでプログラムを実行すると、以下のような結果が取得できます。アーカイブのURLkeyで、記事総数valueですね。

{'https://manablog.org/2019/07/': '28', 'https://manablog.org/2019/06/': '30', 'https://manablog.org/2019/05/': '31', 'https://manablog.org/2019/04/': '30', ...}

これでアーカイブと記事数の一覧が取得できましたので、これをループさせて順番にURLを叩いてアクセスして記事をスクレイピングするだけです!

が、manablogの記事数がかなり多いため、今回は2019年7月分のみを取得することにします。

Bicepper

いきなり全部の記事を取得する必要はありません!
少しずつ取得しましょう。

最初にCSV書き出し用の空のdictを用意しておきます。
この位置に書いておけば、アーカイブが変わるごとにdictが空になります。

2019年7月分のみのため、2019/06の記事数30が引っかかったらループが止まるようにしています。ループ止めるの面倒だったんで凄いテキトーですが許してください。。。

for key, value in blog_page_dict.items():
    now_month = key.split("/")
    print("現在【{}_{}】分を取得中".format(now_month[3], now_month[4]))
    # CSV用
    blog_output = {}

    # 今回は7月分のみ取得
    if int(value) == 30:
        break

上のループはアーカイブごとのループです。
ですので、今度は個別記事ごとのURLを取得する必要があります。

manablogは10記事でページが分割されていますので、「記事総数 ÷ 10」で出た商の分ループを回せば、アーカイブ内の記事全てを回ることができます。

# 月別でページ分ループ
for pages in range(int(value) // 10):
    in_request = urllib.request.Request(url="{}{}".format(key, "page/{}".format(pages+1) if pages != 0 else ""),
                                         headers=headers)
    in_response = urllib.request.urlopen(in_request)

    in_soup = BeautifulSoup(in_response, "html.parser")
    in_soup_list = in_soup.find_all("div", attrs={"class", "readmore"})
    in_soup_link = [x.find_all("a")[0].get("href") for x in in_soup_list]

これで2019年7月の記事を、ページごとにURLリスト取得できます。

# 1ページ目のURL一覧
['https://manablog.org/motivation-keep-way/', 'https://manablog.org/freelance-continue/', 'https://manablog.org/mental-success/', 'https://manablog.org/life-changing-solo-travel/',...]

いよいよ個別のページからHTMLを取得する番です。

記事へアクセスする方法は今まで通りであり、今回は「タイトル」と「記事コンテンツ」を取得します。

タイトルと記事コンテンツの取得はBeautifulSoupを使えば一発で取得できますので、この2つをdict形式で保存しておきます。

最後の部分が重要です

1記事のスクレイピングが終了したら、次の記事のスクレイピングまでに1秒の間隔をここで入れています。これを入れないと高負荷になるため、注意しましょう。

# 個別記事別分ループ
for item_page in in_soup_link:
    item_request = urllib.request.Request(url=item_page, headers=headers)
    item_response = urllib.request.urlopen(item_request)
    item_soup = BeautifulSoup(item_response, "html.parser")

    blog_title = item_soup.find("h1")  # タイトル取得
    blog_content = item_soup.find("div", itemprop="articleBody")  # 記事内容取得
    blog_output[blog_title] = blog_content

    time.sleep(1) # 超重要!

この2つのループが終了すると、blog_outputにアーカイブ1月分のデータが貯まります。

あとはこれをCSVとして書き出すだけです。

今回はpandasのcsvを使用しています。

流れとしては「dict → pandasのDataframeに変換 → csvに変換」です。

pandasってそんなこともできるんだ。

プログラミング初心者

Bicepper

いろいろ便利に使えるPythonパッケージなので、覚えておきましょう!

CSVのタイトルは分かりやすいように、アーカイブ名(年、月)をつけています。

    df = pd.DataFrame(blog_output.items(), columns=['タイトル', 'コンテンツ'])
    df.to_csv('{}_{}.csv'.format(now_month[3], now_month[4]))
print("終了")

以上で完了です!

これがうまくいくと、2019_07.csvというCSVファイルが書き出されます。

ファイルの中身を確認してみると…

manablogのHTMLデータ

大丈夫のようです!これで取得完了です!

【まとめ】スクレイピングでデータを活用しよう

今回は、manablogをPythonでスクレイピングしてブログ情報を取得する方法について紹介しました。

この時点ではデータを取得しただけですが、ここからデータをパースして分析すればさまざまなことがわかります!

例えば

  • どんなジャンルの記事が多いか
  • 記事の中で多く使われている単語はなにか
  • どんなアフィリエイトや商材が多いか

などなどあらゆる情報が丸見えになるわけです。

ここからさらに発展させて機械学習などにかけてみても面白いかもしれないですね。

それではまた!

Bicepper

ルールは守って楽しいスクレイピングライフを送ってください!