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