1
2

More than 1 year has passed since last update.

Zennのタグ情報をスクレイピング【備忘録】

Last updated at Posted at 2022-08-27

概要

QiitaとZennってどんな特徴があるの?
気になったけれどZennの情報はまとまっていなかったのでスクレイピングで調べてみました。
その時の困りごと等々忘れない様、備忘録代わりに書いてみました。
初投稿なので拙い部分多々あるかと思いますが、よろしくお願いいたします。

目次

1.タグ情報のランキング
2.実行環境
3.利用規約確認
4.サイトURLスクレイピング
5.タグ情報スクレイピング
6.最後に

1.タグ情報のランキング

2021年のタグ情報をまとめてみました。

個人的には特徴があるなと思ったのは、下記キーワード別順位比較を見ると、
Zenn、Qiitaともに上位のタグもある中、
TypeScript、Flutter、Go、Next.js、GitHubなどはZennのみで上位(10位以内)でした。
逆に初心者、Rails、Ruby等のタグはQiitaのみで上位(10以内)でそれぞれのサイトの特徴の様なものが見られました。

Zenn、Qiita順位比較

順位 Zenn Qiita
1 JavaScript Python
2 AWS JavaScript
3 TypeScript 初心者
4 Python Rails
5 React AWS
6 Docker Ruby
7 Flutter Docker
8 Go PHP
9 Next.js React
10 GitHub Swift
11 iOS Laravel
12 PHP Java
13 Laravel Vue.js
14 Node.js TypeScript
15 Linux Linux
16 初心者 HTML
17 Rails Unity
18 Ruby Git
19 Vue.js C#
20 Git CSS

キーワード別順位比較

キーワード Zenn順位 Qiita順位 差分
JavaScript 1 2 +1
AWS 2 5 +3
TypeScript 3 14 +11
Python 4 1 -3
React 5 9 +4
Docker 6 7 +1
Flutter 7 31 +24
Go 8 36 +28
Next.js 9 51 +42
GitHub 10 23 +13
iOS 11 22 +11
PHP 12 8 -4
Laravel 13 11 -2
Node.js 14 25 +11
Linux 15 15 ±0
初心者   16 3 -13
Rails 17 4    -13
Ruby 18 6 -12
Vue.js 19 13 -6
Git 20 18 -2

Zenn、Qiita順位比較(件数付き)

順位 Zenn Zenn件数 Qiita Qiita件数
1 JavaScript 1261 Python 12127
2 AWS 1248 JavaScript 7003
3 TypeScript 1226 初心者 6694
4 Python 1193 Rails 6599
5 React 1050 AWS 6501
6 Docker 664 Ruby 5543
7 Flutter 641 Docker 3620
8 Go 541 PHP 3283
9 Next.js 538 React 3088
10 GitHub 492 Swift 2833
11 iOS 456 Laravel 2647
12 PHP 452 Java 2558
13 Laravel 450 Vue.js 2318
14 Node.js 440 TypeScript 2293
15 Linux 422 Linux 2124
16 初心者 416 HTML 2121
17 Rails 398 Unity 2093
18 Ruby 391 Git 2062
19 Vue.js 370 C# 2000
20 Git 361 CSS 1925

2.実行環境

今回、Google Colaboratoryで実施しました。

3.利用規約確認

まずはスクレイピングしても良いかどうか確認しました。
Zenn利用規約
Zenn/robot.text

スクレイピングできるかどうかは利用規約の変更等の可能性もありますので、ご自身で確認頂き自己責任でお願いいたします。

↓基本的なスクレイピングの流れは動画にはなりますがこちらを参考にしました。

4.サイトURLスクレイピング

まずはZennの新着一覧記事から基本情報をスクレイピングで取得していきます。
Zenn新着記事-1ページ目-
このページから、「記事のタイトル」、「著者」、「投稿時間」、「記事URL」を取得していきます。

from bs4 import BeautifulSoup
import requests

# 変数urlに、Zenn新着ページのURLを入れる
url = 'https://zenn.dev/articles?page={1}'

# urlへのアクセス結果を、変数rに格納
r = requests.get(url)

# 取得結果を解析してsoupに格納
soup = BeautifulSoup(r.text)

# ArticleList_itemContainer__xlBMcクラスを持ったdivタグをすべて取得して、変数contentsに格納
contents = soup.find_all('div', class_='ArticleList_itemContainer__xlBMc')

contents の中身を確認すると大量のdivタブ情報が取得できるのがわかるかと思います。

contents

これで必要な情報に関連するdivタブ情報の一覧が取得できたので、
ここから必要な情報を取得していきます。

d_list=[]

for content in contents:
    #URLlink
    link = 'https://zenn.dev' + content.a.get('href')
    #タイトル
    title= content.find('h2').text
    #著者
    author = content.find('div', class_='ArticleList_userName__GWXDx').text
    #時間
    time  = content.time.get('datetime')
    # 変数dに、これまで取得した項目を格納する
    d = {
        'title': title,
        'author': author,
        'link': link,
            'time': time
    }
    d_list.append(d)

こちらのページは
https://zenn.dev/articles?page={ページ番号}
という形で、ページ番号を増やしていけば全ての記事情報を得ることができます。
ページが正常に取得できる状態の間、ページ番号を増やしていきます。
ページの正常取得はstatus_codeで確認します。
200であれば正常にページが開けています。
そうでなければループが閉じる様にします。

if r.status_code == 200:
    ・・・
    ・・・
    ・・・
else:
    break

ここまでをまとめると下記の様になります。

from bs4 import BeautifulSoup
import requests
import pandas as pd
from time import sleep

# 変数urlに、Zenn新着ページのURLを入れる
url = 'https://zenn.dev/articles?page={}'

# 変数d_listに空のリストを作成する
d_list = []
#ページ番号i
i=0
# アクセスするためのURLをtarget_urlに格納する 
while True:
    i +=1
    target_url = url.format(i)
    # target_urlへのアクセス結果を、変数rに格納
    r = requests.get(target_url)

    if r.status_code == 200:
        print('d_listの大きさ:', len(d_list))
        # print()してtarget_urlを確認する
        print(target_url)

        # 取得結果を解析してsoupに格納
        soup = BeautifulSoup(r.text)
        # ArticleList_itemContainer__xlBMcクラスを持ったdivタグをすべて取得して、変数contentsに格納
        contents = soup.find_all('div', class_='ArticleList_itemContainer__xlBMc')
        #1秒ウェイトを入れる
        sleep(1)

        for content in contents:
            #URLlink
            link = 'https://zenn.dev' + content.a.get('href')
            #タイトル
            title= content.find('h2').text
            #著者
            author = content.find('div', class_='ArticleList_userName__GWXDx').text
            #時間
            time  = content.time.get('datetime')
            # 変数dに、これまで取得した項目を格納する
            d = {
                'title': title,
                'author': author,
                'link': link,
                 'time': time
            }
            d_list.append(d)
    else:
        break 


# 変数d_listを使って、データフレームを作成する
df = pd.DataFrame(d_list)

# to_csv()を使って、データフレームをCSV出力する
df.to_csv('ファイルの保存場所/ファイル名.csv', index=None, encoding='utf-8-sig')

5.タグ情報スクレイピング

先ほど取得した,個別のページのurlを用いてタグ情報を取得していきます。

Google Colaboratoryセッション切れ対策

Google Colaboratoryで実行した場合限定ですが、
セッションが
1.何も操作しなかった場合90分
2.無条件で12時間
で切れる仕様になっています。

1の対策はこちらのサイトに対処しました。

2の対策はどうしようもないので、(有料登録すれば24時間まで伸ばせる様ですが、根本的には同じ)
時間制限を超えない様に工夫するしかないです。

ファイルの長さを確認します。

import pandas as pd

#csvからページ名を取得
df = pd.read_csv('ファイル保存場所/ファイル名.csv')

print(len(df))
28000

スリープ1秒の長さを考慮し、1処理当たり2秒かかるとすると

28000行 × 2秒 ÷ 3600時間/秒 = 約15時間

となり、ギリギリ12時間を超えてしまいます。

なので、ファイルを分割し、それぞれ処理することで処理を12時間以内におさめます。
今回は2分割しますが、かかる時間に合わせて分割します。

分割方法はこちらを参考にしました。

ファイル分割

import pandas as pd

#csvからページ名を取得
df = pd.read_csv('/ファイル保存場所/ファイル名.csv')
##行の中央を計算
row = len(df) //2
#前半後半に分けて格納
df1 = df[:row]
df2 = df[row:]

# to_csv()を使って、データフレームをCSV出力する
df1.to_csv('/ファイル保存場所/ファイル名_1.csv', index=None, encoding='utf-8-sig')
df2.to_csv('/ファイル保存場所/ファイル名_2.csv', index=None, encoding='utf-8-sig')

csvファイルからタグ情報を取得します。
処理するファイルがいくつかあるので、関数を作ります。

また、今回ファイルを分割したので処理を2回行うので関数として下記の様に作成します。

タグ情報読出

def get_tag(num):
    from bs4 import BeautifulSoup
    import requests
    import pandas as pd
    from time import sleep

    #fileNoを定義
    read_file = '/ファイル保存場所/ファイル名_{}.csv'
    target_read_file=read_file.format(num)

    # 変数d_listに空のリストを作成する
    d_list = []
    #csvからページ名を取得
    df = pd.read_csv(target_read_file)

    #処理数nを定義
    n=0

    for title, author,link,time in zip(df.title, df.author, df.link, df.time):
        #処理数をカウント
        n += 1
        #エラー処理
        try:#3、予期せぬエラー対策〜except Exception
            # アクセスするためのURLをtarget_urlに格納する 
            if link is None:#1、リンク情報がない
                print("{}ページURL情報がありません。".format(n))
                continue
            target_url = link
            # target_urlへのアクセス結果を、変数rに格納
            r = requests.get(target_url)
            if r.status_code != 200:#2、リンク先が消えている
                print("{}ページアクセスエラー".format(n))
                continue
            # 取得結果を解析してsoupに格納
            soup = BeautifulSoup(r.text)
            #100処理毎に表示
            if n % 100 == 0:
                print("現在処理数は{}です。".format(n))

            #1秒ウェイトを入れる
            sleep(1)

            #変数tag情報リセット
            for x in range(1,11):
                exec("tag_{}={}".format(x, None))

            #タグ情報のクラス名
            tags = soup.find_all('div', class_='View_spTopicName__Jo5TO')

            i = 1

            for tag in tags:
                tag = tag.text
                exec("tag_{}={}".format(i, "tag"))
                i += 1
            # 変数dに、これまで取得した11項目を格納する
            d = {
            'title':title,
            'author':author,
            'link':link,
            'time':time,
            'tag_1': tag_1,
            'tag_2': tag_2,
            'tag_3': tag_3,
            'tag_4': tag_4,
            'tag_5': tag_5,
            'tag_6': tag_6,
            'tag_7': tag_7,
            'tag_8': tag_8,
            'tag_9': tag_9,
            'tag_10': tag_10         
            }
            d_list.append(d)
        except Exception as e:
            print(type(e))
            print("予期せぬエラーが{}で発生しスキップしました。".format(n))
            continue

    # 変数d_listを使って、データフレームを作成する
    df = pd.DataFrame(d_list)
    #fileNoを定義
    write_file='/ファイル保存場所/ファイル名_{}.csv'
    target_write_file=write_file.format(num)

    # to_csv()を使って、データフレームをCSV出力する
    df.to_csv(target_write_file, index=None, encoding='utf-8-sig')

処理の際、エラーがいくつか発生したので、エラー対策をします。
1、リンク情報がない

if link is None:

2、リンク先が消えている

 if r.status_code != 200:

3、予期せぬエラー対策

try:
    ・・・
    ・・・
    ・・・
except Exception

3、のエラー対策はこちらを参考にしました。

タグ情報はいくつあるかわからないので、こちらを参考に最大10個格納できる様にしました。
こちらのサイトを参考にしました。

for tag in tags:
    tag = tag.text
    exec("tag_{}={}".format(i, "tag"))
    i += 1

ファイルの結合

分割したファイルそれぞれに処理を行います。

#1回目
get_tag(1)
#2回目
get_tag(2)

分割されたcsvファイルを結合します。

import pandas as pd

t1 = pd.read_csv('/ファイル保存場所/ファイル名_1.csv')
t2 = pd.read_csv('/ファイル保存場所/ファイル名_2.csv')
t = pd.concat([t1, t2], ignore_index=True)

t.to_csv('/ファイル保存場所/ファイル名.csv', index=None, encoding='utf-8-sig')

6.最後に

こういった感じで、Zennのスクレイピングはできると思います。
はじめてのプログラミングでエラーが多発して非常に苦労しましたがなんとかできました。
もっとこうすればいいなどありましたら、アドバイス頂けると嬉しいです。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2