はじめに
@kiyokiyo_kzsby さんの素敵な記事を拝見し、ワードクラウドというものを作ってみたくなりました。
題材としては、Qiitaに投稿されたSeleniumタグの付いた記事のタイトルを利用します。
全期間だけでなく、1年毎で作成した際に何か傾向があるのか見てみます。
完成形
記事作成時点(2021/06/19)のSeleniumタグの全1,859記事のタイトルから作成すると次のようなワードクラウドが出来ました。
以下は、2014年と2020年のワードクラウドを並べたものです。
環境
- Google Colaboratory(Google Colab)
作成環境としては、Google Colabを利用して、Webブラウザ上で作業しました。
必要ライブラリインストール
!apt update
!apt install fonts-ipafont fonts-ipaexfont
!pip install mecab-python3 unidic-lite
mecab-python3のREADMEに従って、MeCabのPythonライブラリと辞書、日本語フォントをインストールしました。
wordcloudとグラフ描画ライブラリのmatplotlibは既にインストールされていました。
MeCab動作確認
MeCabが使えることを次のコマンドで確認します。
import MeCab
print(MeCab.Tagger().parse("すもももももももものうち"))
すもも スモモ スモモ 李 名詞-普通名詞-一般 0
も モ モ も 助詞-係助詞
もも モモ モモ 桃 名詞-普通名詞-一般 0
も モ モ も 助詞-係助詞
もも モモ モモ 桃 名詞-普通名詞-一般 0
の ノ ノ の 助詞-格助詞
うち ウチ ウチ 内 名詞-普通名詞-副詞可能 0
EOS
記事タイトルの収集
Qiita API v2を利用して記事を取得し、各記事のタイトル部分を集めます。
import urllib.request
import json
def get_article_titles(token = None, query = None):
text = ""
page_num = 0
while page_num < 100:
page_num += 1
# クエリパラメータの準備
params = {
'page': str(page_num),
'per_page': '100'
}
if (query is not None):
params['query'] = query
# アクセストークンが指定された場合に付与
req_headers = {}
if (token is not None):
req_headers = {
'Authorization': 'Bearer ' + token
}
url = "https://qiita.com/api/v2/items?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url, headers=req_headers)
with urllib.request.urlopen(req) as res:
body = json.load(res)
for article in body:
# print("Title: " + article["title"])
text += article["title"] + " "
print("Page: " + str(page_num))
res_headers = res.info()
# 最後のページまで取得したかを判定
if page_num >= (int(res_headers['Total-Count']) + 99 ) // 100:
print('# of articles: ', res_headers['Total-Count'])
break
return text
- 関数の引数としては、Qiita APIのアクセストークンと検索クエリを指定可能なようにしました(省略可)。
- Qiita API v2のドキュメントに
Total-Count
というHTTPレスポンスヘッダに要素数(記事数)が含まれていると記載されているので、これを使って何ページ分取得するかを判断しています。
上記を使って、Seleniumタグの付いた2021年に投稿された記事のタイトルを取得し、その最初の部分を表示すると、次のようになりました。
text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium created:2021')
print(text[0:100])
Page: 1
Page: 2
# of articles: 184
Python+Seleniumを用いたGoogle検索結果のCSV保存方法 メモ youtubeのURLから字幕を生成 karateでWebUIのテストをする。seleniumとのソース比較も ホーム
形態素解析
形態素解析の部分については、参考記事のコードを参考にさせていただきました。
import MeCab
def mecab_analysis(text):
print("start mecab analysis")
tagger = MeCab.Tagger()
# tagger.parse("")
node = tagger.parseToNode(text)
output = []
while(node):
if node.surface != "":
word_type = node.feature.split(",")[0]
if word_type not in ["助詞","助動詞","副詞","連体詞","記号"]:
output.append(node.surface)
node = node.next
print("end mecab analysis")
return output
ワードクラウド出力
ワードクラウド出力部分についても、参考記事のコードを拝借しました。
ストップワードの部分は、関数の引数として指定できるようにしています。
import matplotlib.pyplot as plt
from wordcloud import WordCloud
def create_wordcloud(text, stop_words = []):
print("start create wordcloud")
# フォントのパスを指定
fpath = "/usr/share/fonts/truetype/fonts-japanese-mincho.ttf"
wordcloud = WordCloud(background_color="white",font_path=fpath, width=1024, height=768, \
stopwords=set(stop_words)).generate(text)
print("end create wordcloud")
print("now showing")
plt.figure(figsize=(15,12))
plt.imshow(wordcloud)
plt.axis("off")
plt.show()
Selenium記事の全期間のワードクラウド作成
ここまでのコードを使って、全期間のワードクラウドを作成します。
stop_words = ['Selenium', u'する', u'ため', u'でき', u'使っ', u'いる', u'とき', u'みる', u'作っ', u'よう', u'こと', u'なっ', u'せる', u'れる']
text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium')
wordlist = mecab_analysis(text)
create_wordcloud(" ".join(wordlist), stop_words = stop_words)
期間別
次に、2014年〜2021年について、1年毎に区切ってワードクラウドを作成します。
2011〜2013年も投稿がありますが、記事数が少ないため割愛します。
(2011年: 2記事、2012年: 9記事、2013年: 20記事)
stop_words = ['Selenium', u'する', u'ため', u'でき', u'使っ', u'いる', u'とき', u'みる', u'作っ', u'よう', u'こと', u'なっ', u'せる', u'れる']
for year in ['2021', '2020', '2019', '2018', '2017', '2016', '2015', '2014']:
print('◆', year, '年', '◆')
text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium created:' + year)
wordlist = mecab_analysis(text)
create_wordcloud(" ".join(wordlist), stop_words = stop_words)
2021年
- 記事数: 184記事(2021/06/19 時点)
- 記事一覧: https://qiita.com/search?q=tag%3ASelenium+created%3A2021&sort=created
2020年
2019年
2018年
2017年
2016年
2015年
2014年
考察
考察と言えるほどのものではなく、できたものを眺めた時の印象レベルですが、以下のようなことが言えるのかなと思います。
- 2018年頃までは「テスト」というキーワードが大きく出力されているが、それ以降は「スクレイピング」というキーワードの方が目立つように見える。
- 「Python」というキーワードが大きく表現されているので、Qiitaの記事上はPythonからSeleniumを利用してることが多そう。
- 2014年時点では、「Firefox」が目立っていたが、そこからはあまり大きな存在感はない。2018年以降は「Chrome」が台頭しているように見える。2017年6月にリリースされたChrome 59で、ヘッドレスモードがサポートされたことに関係するかもしれない。
ipynbファイル
参考までに、今回利用したipynbファイルを記載します。
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "wordcloud_selenium.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "code",
"metadata": {
"id": "1wBwTbF0ZeAz"
},
"source": [
"!apt update\n",
"!apt install fonts-ipafont fonts-ipaexfont\n",
"!pip install mecab-python3 unidic-lite"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "cfpYeOOf8WtZ"
},
"source": [
"import MeCab\n",
"print(MeCab.Tagger().parse(\"すもももももももものうち\"))"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "RG5aXwcoZuST"
},
"source": [
"import urllib.request\n",
"import json\n",
"\n",
"def get_article_titles(token = None, query = None):\n",
" text = \"\"\n",
" page_num = 0\n",
" while page_num < 100:\n",
" page_num += 1\n",
" # クエリパラメータの準備\n",
" params = {\n",
" 'page': str(page_num),\n",
" 'per_page': '100'\n",
" }\n",
" if (query is not None):\n",
" params['query'] = query\n",
" # アクセストークンが指定された場合に付与\n",
" req_headers = {}\n",
" if (token is not None):\n",
" req_headers = {\n",
" 'Authorization': 'Bearer ' + token\n",
" }\n",
"\n",
" url = \"https://qiita.com/api/v2/items?\" + urllib.parse.urlencode(params)\n",
"\n",
" req = urllib.request.Request(url, headers=req_headers)\n",
" with urllib.request.urlopen(req) as res:\n",
" body = json.load(res)\n",
" for article in body:\n",
" # print(\"Title: \" + article[\"title\"])\n",
" text += article[\"title\"] + \" \"\n",
" print(\"Page: \" + str(page_num))\n",
" res_headers = res.info()\n",
" # 最後のページまで取得したかを判定\n",
" if page_num >= (int(res_headers['Total-Count']) + 99 ) // 100:\n",
" print('# of articles: ', res_headers['Total-Count'])\n",
" break\n",
" \n",
" return text"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "R-5QOn1BZvcd"
},
"source": [
"text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium created:2021')\n",
"print(text[0:100])"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "XiDk7lc8ZyiU"
},
"source": [
"import MeCab\n",
"\n",
"def mecab_analysis(text):\n",
" print(\"start mecab analysis\")\n",
" tagger = MeCab.Tagger()\n",
" # tagger.parse(\"\")\n",
" node = tagger.parseToNode(text)\n",
" output = []\n",
" while(node):\n",
" if node.surface != \"\":\n",
" word_type = node.feature.split(\",\")[0]\n",
" if word_type not in [\"助詞\",\"助動詞\",\"副詞\",\"連体詞\",\"記号\"]:\n",
" output.append(node.surface)\n",
" node = node.next\n",
" print(\"end mecab analysis\")\n",
" return output"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "L5NBEBFOZ4vs"
},
"source": [
"wordlist = mecab_analysis(text)\n",
"#print(wordlist)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "LrQaNkUYZ-oP"
},
"source": [
"import matplotlib.pyplot as plt\n",
"from wordcloud import WordCloud\n",
"\n",
"def create_wordcloud(text, stop_words = []):\n",
" print(\"start create wordcloud\")\n",
"\n",
" # フォントのパスを指定\n",
" fpath = \"/usr/share/fonts/truetype/fonts-japanese-mincho.ttf\"\n",
"\n",
" wordcloud = WordCloud(background_color=\"white\",font_path=fpath, width=1024, height=768, \\\n",
" stopwords=set(stop_words)).generate(text)\n",
" print(\"end create wordcloud\")\n",
"\n",
" print(\"now showing\")\n",
" plt.figure(figsize=(15,12))\n",
" plt.imshow(wordcloud)\n",
" plt.axis(\"off\")\n",
" plt.show()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "ggc0WvYdg1A8"
},
"source": [
"stop_words = ['Selenium', u'する', u'ため', u'でき', u'使っ', u'いる', u'とき', u'みる', u'作っ', u'よう', u'こと', u'なっ', u'せる', u'れる']\n",
"text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium')\n",
"wordlist = mecab_analysis(text)\n",
"create_wordcloud(\" \".join(wordlist), stop_words = stop_words)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Vt9tQWASaC_a"
},
"source": [
"stop_words = ['Selenium', u'する', u'ため', u'でき', u'使っ', u'いる', u'とき', u'みる', u'作っ', u'よう', u'こと', u'なっ', u'せる', u'れる']\n",
"for year in ['2021', '2020', '2019', '2018', '2017', '2016', '2015', '2014']:\n",
" print('◆', year, '年', '◆')\n",
" text = get_article_titles(token = 'YOUR ACCESS TOKEN', query = 'tag:Selenium created:' + year)\n",
" wordlist = mecab_analysis(text)\n",
" create_wordcloud(\" \".join(wordlist), stop_words = stop_words)"
],
"execution_count": null,
"outputs": []
}
]
}