7
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

QiitaのSelenium全記事からワードクラウドを作る

Last updated at Posted at 2021-06-19

はじめに

@kiyokiyo_kzsby さんの素敵な記事を拝見し、ワードクラウドというものを作ってみたくなりました。
題材としては、Qiitaに投稿されたSeleniumタグの付いた記事のタイトルを利用します。
全期間だけでなく、1年毎で作成した際に何か傾向があるのか見てみます。

完成形

記事作成時点(2021/06/19)のSeleniumタグの全1,859記事のタイトルから作成すると次のようなワードクラウドが出来ました。

selenium_wordcloud_2012-2021.png

以下は、2014年と2020年のワードクラウドを並べたものです。

2014vs2020.png

環境

  • 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)

selenium_wordcloud_2012-2021.png

期間別

次に、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年

selenium_wordcloud_2021.png

2020年

selenium_wordcloud_2020.png

2019年

selenium_wordcloud_2019.png

2018年

selenium_wordcloud_2018.png

2017年

selenium_wordcloud_2017.png

2016年

selenium_wordcloud_2016.png

2015年

selenium_wordcloud_2015.png

2014年

selenium_wordcloud_2014.png

考察

考察と言えるほどのものではなく、できたものを眺めた時の印象レベルですが、以下のようなことが言えるのかなと思います。

  • 2018年頃までは「テスト」というキーワードが大きく出力されているが、それ以降は「スクレイピング」というキーワードの方が目立つように見える。
  • Python」というキーワードが大きく表現されているので、Qiitaの記事上はPythonからSeleniumを利用してることが多そう。
  • 2014年時点では、「Firefox」が目立っていたが、そこからはあまり大きな存在感はない。2018年以降は「Chrome」が台頭しているように見える。2017年6月にリリースされたChrome 59で、ヘッドレスモードがサポートされたことに関係するかもしれない。

2014vs2020.png

ipynbファイル

参考までに、今回利用したipynbファイルを記載します。

wordcloud_selenium.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": []
    }
  ]
}

参考

7
12
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
7
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?