ざっくり言うと、webページで公開されている情報を効率的に集める手段です。
日々更新されるページのデータ自動収集や、コピペができない歌詞サイトなどのデータを集めるのに重宝します。
特に自動でスクレイピングを行う際に、アクセス頻度を制御しないと東京オリンピックのチケットサイトみたいに、アクセスが遅くなったりページが落ちたりします。したがって、短くともアクセスごとに1秒の間隔を開けるよう制御した方が良いと言われています。私は、2〜5秒くらいでランダムに間隔を取るよう設定したりしています。
webサイトには robots.txt という、端的に言うとプログラムを使ってアクセスしないでほしいページのリストが用意されている時があります。
用意されている場合、webサイトのトップページのURL末尾に /robots.txt をつけると閲覧できます。 私自身がrobots.txtに詳しいわけではないので、具体的な見方は ここらへんに任せますが、 Disallow: と書いてあるURLについてはアクセスを避けておくと無難です。
ある種の紳士協定なので、これを破ったからといって訴えられることはないと思いますが(サーバー負荷はやらかすとサイバー犯罪扱いされる場合もあります)、ゼミの一員として研究する場合などは、robots.txtでアクセスを禁じられていないページを扱った方が良いと思います。
「世界全体に公開されている情報を集めて何が悪いのか?」という声も多いですし、気にせずバンバン集める方もいますが、よくスクレイピング界隈で見かけるフレーズをお借りすると、そういった作業は「お行儀が悪い」みたいです。
robots.txtに似た話ですが、利用規約でツールによるアクセスを禁じているサイトもあります。Amazonとかtwitterとかが該当します。
これらのサイトについて、例えばログインしてから情報を集めるコードを実行すると、アカウントが停止される可能性があります。
特に、自分個人のものではないアカウント(大学・バイト先等)で利用しているサイトから情報を集めたい時は利用規約は必読ですし、所属先の責任者への相談がmustです。絶対やらない方がいいと思いますが。
人のサイトを勝手に全部コピペして公開してはいけないように、集めた情報をそのまま公開するようなことはやめましょう。あくまで分析や私的利用のためにスクレイピングを行なってください。
スクレイピングは他者が運営しているサイトに対して行う作業なので、ミスをした際に相手方に迷惑をかける可能性があります。コーディングは慎重に、何か問題が起こった時にディフェンスできるよう行う必要があります。特に自動化の際は、本格的な運用の前に教授の許可を得るようにしましょう。
前置きが長くなりましたが、webスクレイピングを先程スクショを貼ったwebサイトでやってみます。
こちらは私の友人が作った、絵文字3つと本文で簡単な絵日記が書ける匿名サイトです。「過負荷で落とさない」という条件付きで、スクレイピングの練習台にする許可をもらいました。
ただし、ある種の社会実験として禁止ワードが一切設定されていないので、不適切な内容の絵日記が投稿されることもあります。閲覧に少し気をつけていただきたいのと、そういった投稿が私や作成した友人の意図するところではない旨をご了承ください。
さて、先程貼った画像のページには https://nikki.yasan.app/posts/af5a3a14-9ea6-40fd-ae4b-72558cf8bd6a から飛べます。
以下では chrome でのアクセスを想定しています。
chromeでは右上の \(\vdots\) を押して、「その他のツール」\(\rightarrow\) 「デベロッパーツール」を押すとこんな感じの画面になります。
右側に開いたデベロッパーツールの一番左上に、四角と矢印を組み合わせたマークがあります。これをクリックしてからwebページ上の情報を見たい場所を選択すると、デベロッパーツール内のHTMLコードで対応する箇所がハイライトされます。
例えば「Rからでも投稿できる」という文字を選択すると <p class="jss17">Rからでも投稿できる</p> という部分がハイライトされます。
この class= が情報を抜くのに大切になってきます。
まずは必要なパッケージを読み込みます。
library(rvest)
webページを読み込みます。
tmp <- read_html("https://nikki.yasan.app/posts/af5a3a14-9ea6-40fd-ae4b-72558cf8bd6a")
ここから、class情報を使ってほしい部分だけ抽出します。classの前にピリオドを打たないと動作しません。
tmp2 <- html_nodes(tmp,css=".jss17")
文字だけ抽出します。
html_text(tmp2)
> [1] "Rからでも投稿できる"
いい感じですね。ただ、一行目だけ取ってきても仕方ないので、全文とってみましょう。
デベロッパーツールを見てみると、他の行については class="jss16 となっています。
html_nodes は , で区切って複数のクラスを指定できるので、
tmp3 <- html_nodes(tmp,css=".jss17,.jss16")
とすると両方抽出できます。文字だけ抽出すると
html_text(tmp3)
> [1] "" "Rからでも投稿できる" "ようになりました!普"
> [4] "通にサイトから投稿し" "た方が便利です!"
で、全文取れてます。ぶつ切りで気持ち悪いので、最後に paste関数で合体させましょう。
# collapse="X" で文字列の間に X を挟みながら合体させます。
# 今回は何も挟みたくないので空欄で
paste(html_text(tmp3),collapse = "")
> [1] "Rからでも投稿できるようになりました!普通にサイトから投稿した方が便利です!"
次は楽天ショッピングの総合ランキングから、商品名と価格を取っていきます。
こちらは一応スクショを撮るのは控えておきます。
絵日記の時と同じ手順で取りたいclassを確認したところ、今回は rnkTop_itemName と rnkTop_price でした。
今回は html_text を html_nodes にくっつけておきます。 read_html から繋げることもコード的には可能ですが、そうするとやり直すたびにwebページにアクセスすることになってしまうので、分けて処理をしています。
raku <- read_html("https://ranking.rakuten.co.jp/?l-id=top_normal_rk_nonhist")
raku2 <- html_nodes(raku,css=".rnkTop_itemName,.rnkTop_price") %>% html_text()
head(raku2)
> [1] "【先着特典】KANJANI’S Re:LIVE 8BEAT(完全生産限定ーRoad to Re:LIVE-盤+初回限定盤+通常盤 Blu-rayセット)【Blu-ra…"
> [2] "20,424円"
> [3] "【先着特典】a r e a / 恋をするんだ / 春玄鳥 (【a r e a】盤(Blu-ray)+【恋をするんだ】盤(Blu-ray)+【春玄鳥】盤…"
> [4] "5,838円"
> [5] "【先着特典】KANJANI’S Re:LIVE 8BEAT(完全生産限定ーRoad to Re:LIVE-盤Blu-ray)【Blu-ray】(8BEATツアー 銀テープ)…"
> [6] "8,000円"
こんな感じで商品名と価格が交互に入っています。seq(X,Y,Z) で XからYまでの公差Zの等差数列を作ることができます。これを使って、 raku2 の奇数行・偶数行をそれぞれ抽出することで、商品名と価格を分けることができます。
# 追加のパッケージが必要
library(tidyverse)
> ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
> ✓ ggplot2 3.3.5 ✓ purrr 0.3.4
> ✓ tibble 3.1.5 ✓ dplyr 1.0.7
> ✓ tidyr 1.1.4 ✓ stringr 1.4.0
> ✓ readr 2.0.2 ✓ forcats 0.5.1
> ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
> x dplyr::filter() masks stats::filter()
> x readr::guess_encoding() masks rvest::guess_encoding()
> x dplyr::lag() masks stats::lag()
rakudata <- data.frame(goods=raku2[seq(1,200,2)],price=raku2[seq(2,200,2)])
head(rakudata %>% mutate(goods=str_sub(goods,1,15)))
> goods price
> 1 【先着特典】KANJANI’S 20,424円
> 2 【先着特典】a r e a / 5,838円
> 3 【先着特典】KANJANI’S 8,000円
> 4 【先着特典】KANJANI’S 8,000円
> 5 【先着特典】a r e a / 5,838円
> 6 【先着特典】KANJANI’S 8,000円
price が 「円」 が入っているせいで文字列になってしまっているので、数字に変換しましょう。これをしないと価格の比較ができません。
それと、継続的にデータを取った時に「いつのデータか」わかるようにしたいので、日付の列を足しておきます。ついでに、順位もちゃんとデータとして残しておきましょう。
rakudata <- rakudata %>% mutate(price=parse_number(price),date=Sys.Date(),rank=row_number())
head(rakudata %>% mutate(goods=str_sub(goods,1,15)))
> goods price date rank
> 1 【先着特典】KANJANI’S 20424 2022-03-16 1
> 2 【先着特典】a r e a / 5838 2022-03-16 2
> 3 【先着特典】KANJANI’S 8000 2022-03-16 3
> 4 【先着特典】KANJANI’S 8000 2022-03-16 4
> 5 【先着特典】a r e a / 5838 2022-03-16 5
> 6 【先着特典】KANJANI’S 8000 2022-03-16 6
ところで、先述の絵日記サイトですが、トップページからデータを取ろうとすると、おそらく上手くいかないと思います。
正確な表現ではありませんが、webページの中には、chrome等のブラウザでアクセスして初めてデータが表示されるものがあります。
こういったサイトにrvestで直にアクセスしても、ページの枠組みしか取得できず、欲しいデータが手に入ることは稀です。
このようなある意味で動的なサイトでスクレイピングをする手段として、仮想のブラウザを立ち上げて、Rで操作して情報収集するというものがあります。
これにはDockerというソフトウェアを使いますが、そこそこ処理が重いせいで私の骨董品Windowsでは動作テストができず、かつゼミ生からWindowsで上手く動かないと言う報告を受けているので、Windowsユーザーの方々には参考程度の情報になってしまいます。
もっとも、Dockerもかなりwidespreadなので、Windowsで絶対に動かないと言うわけではなく、動くようにするのがちょっと大変なだけだとは思います。
ちなみに、これまで情報の取得しかしていませんが、絵日記の書き込みや大学サイトへのログインなど、入力が必要なものについてもDockerを使います。
なおDockerはたぶん初学者にはかなり難しいので(単純なスクレイピングすらできる人はそう多くありません)、無理して習得する必要はないと思います。
参考にしたのはこちら
まずは https://www.docker.com/get-started からDocker Desktop をインストールします。 インストールできたら、MacのターミナルやWindowsのPowerShellで、 docker pull selenium/standalone-chrome を実行してchromeを動かす環境を作ります。 次に、 docker run -d -p 4444:4444 selenium/standalone-chrome (4444は別の数字でも大丈夫なはず) でサーバーを立ち上げます。
2回目以降はDockerを開いてSTARTを選べば大丈夫です。
Docker の動作には RSelenium を使います。
library(RSelenium)
先ほど立ち上げたサーバーをRに伝えてブラウザを開きます。
eCapsは安定した動作のために付け加えている Ever Growing List of Useless Arguments by https://stackoverflow.com/questions/48450594/selenium-timed-out-receiving-message-from-renderer です。
なお、このページはRSeleniumについてのものではないので、R用にコードを書き換えてます。ここら辺からRユーザーとしてはある種のフロンティアなのかもしれません。
eCaps <- list(chromeOptions = list(
args = c(
"start-maximized",
"enable-automation",
"--headless",
"--no-sandbox",
"--disable-infobars",
"--disable-dev-shm-usage",
"--disable-browser-side-navigation",
"--disable-gpu"
)
))
brows = remoteDriver(
remoteServerAddr = "localhost",
port = 4444,
browserName = "chrome",
extraCapabilities = eCaps
)
brows$open()
> [1] "Connecting to remote server"
> $acceptInsecureCerts
> [1] FALSE
>
> $browserName
> [1] "chrome"
>
> $browserVersion
> [1] "98.0.4758.102"
>
> $chrome
> $chrome$chromedriverVersion
> [1] "98.0.4758.102 (273bf7ac8c909cde36982d27f66f3c70846a3718-refs/branch-heads/4758@{#1151})"
>
> $chrome$userDataDir
> [1] "/tmp/.com.google.Chrome.Y7f8s0"
>
>
> $chromeOptions
> $chromeOptions$args
> $chromeOptions$args[[1]]
> [1] "start-maximized"
>
> $chromeOptions$args[[2]]
> [1] "enable-automation"
>
> $chromeOptions$args[[3]]
> [1] "--headless"
>
> $chromeOptions$args[[4]]
> [1] "--no-sandbox"
>
> $chromeOptions$args[[5]]
> [1] "--disable-infobars"
>
> $chromeOptions$args[[6]]
> [1] "--disable-dev-shm-usage"
>
> $chromeOptions$args[[7]]
> [1] "--disable-browser-side-navigation"
>
> $chromeOptions$args[[8]]
> [1] "--disable-gpu"
>
>
>
> $`goog:chromeOptions`
> $`goog:chromeOptions`$debuggerAddress
> [1] "localhost:42313"
>
>
> $javascriptEnabled
> [1] TRUE
>
> $nativeEvents
> [1] TRUE
>
> $networkConnectionEnabled
> [1] FALSE
>
> $pageLoadStrategy
> [1] "normal"
>
> $platform
> [1] "ANY"
>
> $platformName
> [1] "ANY"
>
> $proxy
> named list()
>
> $`se:cdp`
> [1] "ws://172.17.0.2:4444/session/893962ad554657ee62cc182d30167a3e/se/cdp"
>
> $`se:cdpVersion`
> [1] "98.0.4758.102"
>
> $`se:vnc`
> [1] "ws://172.17.0.2:4444/session/893962ad554657ee62cc182d30167a3e/se/vnc"
>
> $`se:vncEnabled`
> [1] TRUE
>
> $`se:vncLocalAddress`
> [1] "ws://172.17.0.2:7900"
>
> $setWindowRect
> [1] TRUE
>
> $strictFileInteractability
> [1] FALSE
>
> $timeouts
> $timeouts$implicit
> [1] 0
>
> $timeouts$pageLoad
> [1] 300000
>
> $timeouts$script
> [1] 30000
>
>
> $unhandledPromptBehavior
> [1] "dismiss and notify"
>
> $version
> [1] ""
>
> $`webauthn:extension:credBlob`
> [1] TRUE
>
> $`webauthn:extension:largeBlob`
> [1] TRUE
>
> $`webauthn:virtualAuthenticators`
> [1] TRUE
>
> $id
> [1] "893962ad554657ee62cc182d30167a3e"
立ち上げたブラウザで https://nikki.yasan.app/ にアクセスします。
brows$navigate("https://nikki.yasan.app/")
Sys.sleep(5)
brows$screenshot(display = T)
ページソースを入手・保存して、そのソースコードに対してスクレイピングを行います。
PS <- brows$getPageSource()
write(unlist(PS), "enikki.html")
enikki <- read_html("enikki.html") %>% html_nodes(css=".jss19,.jss18") %>% html_text()
head(enikki)
> [1] ""
> [2] "社é\u0095·ã\u0081®ã\u0081¿ã\u0081ªã\u0081\u0095ã\u0082\u0093ï¼\u0081 ï¼\u0092"
> [3] "æ\u009c\u0088ã\u0081§ã\u0081\u0099ã\u0082\u0088ã\u0080\u009cï¼\u0081"
> [4] ""
> [5] "å\u008f\u008bé\u0081\u0094ã\u0081«å\u0080\u009fã\u0082\u008aã\u0081\u009fæ\u0099\u0082è¨\u0088ã\u0082\u0092è¿\u0094"
> [6] "ã\u0081\u009dã\u0081\u0086ã\u0081¨æ\u0080\u009dã\u0081£ã\u0081\u009fã\u0082\u0089æ\u0099\u0082è¨\u0088ã\u0082\u0092"
めちゃくちゃ文字化けしてますね。MacのRStudioはデフォルトのエンコーディングがUTF-8 (WindowsはShift-JIS)なので、read_htmlで文字コードを指定しましょう。
enikki <- read_html("enikki.html",encoding = "UTF-8") %>% html_nodes(css=".jss19,.jss18") %>% html_text()
head(enikki)
> [1] "" "社長のみなさん! 2" "月ですよ〜!"
> [4] "" "友達に借りた時計を返" "そうと思ったら時計を"
完璧です。さて、こちらは日記の頭に必ず "" が付くみたいです。これをヒントに日記を区切っていきましょう。
endata <- enikki
# "" を分割用のキーワードに置換しておく
endata[endata==""] <- "KEY_PHRASE_HERE"
# まず一度文章を合体させてから、設定したキーワードに従って分割し直す
endata <- endata %>% paste(collapse = "") %>% str_split(pattern = "KEY_PHRASE_HERE")
# 必要な部分だけにする
endata <- endata[[1]][-1]
endata
> [1] "社長のみなさん! 2月ですよ〜!"
> [2] "友達に借りた時計を返そうと思ったら時計を忘れた。また明日返しに行こう。"
> [3] "今、お尻を出しています"
> [4] "今日は必要なものを買いに外に行きました。必要なものを大体買うことができたのでよかったです。"
> [5] "社長の皆さん! 1月ですよ〜!"
> [6] "日記職人の皆さん、あけましておめでとうございます!!"
> [7] "2021年に取り残されてしまいました。皆さんは2022年を楽しんでください"
> [8] "あけおめ!!!"
> [9] "あけましておめでとうございます!"
> [10] "ご存知でしたか?デフォルトの絵文字がクリスマス仕様になっています!"
> [11] "メリークリスマス!"
> [12] "東京では雪が降ってるみたいですが、今日から3日間沖縄に行ってきます。修学旅行ではありません。"
> [13] "朝から眠気とだるさが止まらなくて心臓がドキドキしたり手が震えたりする😢"
> [14] "自転車の鍵穴が凍って鍵が入らないという事件が起きました。最後の冬に面白い経験ができました。"
> [15] "BDSPもバグってるんだから、自分のコードがバグってても仕方ないよね"
> [16] "冬仕様になってるね〜"
> [17] "またみんなが絵文字日記のことを忘れていて悲しいです。"
> [18] "今日からホテルに泊まります。るんるん♪"
> [19] "今日はスープカレーを食べたので、明日は焼き肉を食べたいなあ!"
> [20] "歯が痛いの!1日に何回も痛み止め飲んでるけど、お腹が痛くならない!鉄壁の胃に生んでくれてありがとう"
\(\cdots\) 3番目に変なのがありますね。こういうのが怖いのであまりSNSとかは使いたくないのですが、練習台にできるサイトが他にないのでご容赦ください。
終わったらブラウザを閉じましょう。
brows$close()
絵日記サイトには、「名前で日記を検索!」という機能があります。
これを使って、「UPDATE」という名前の日記だけを集めてみましょう。
# ブラウザを開く
brows$open()
brows$navigate("https://nikki.yasan.app/")
Sys.sleep(3)
これまでのように、検索ワードを入力する場所をデベロッパーツールの右上のアイコンをクリックしてから選択して、ハイライトされた部分を
右クリック > Copy > Copy XPath としていきます。
findElement 関数でRにXPathを伝えることでこのボックスを見つけてもらい、 clickElement でクリックします。
fill <-
brows$findElement(using = "xpath", value = '//*[@id="__next"]/div/div[4]/div/div/input')
fill$clickElement()
brows$screenshot(display = T)
縦線が入って、入力待ちになりました。
ここに “UPDATE” という文字を入力します。
fill$sendKeysToElement(list("UPDATE"))
brows$screenshot(display = T)
HTMLを書き出して、そのHTMLファイルに対してスクレイピングします。
ups <- brows$getPageSource()
write(unlist(ups),"UPDATE.html")
tmp <- read_html("UPDATE.html",encoding = "UTF-8") %>% html_nodes(css=".jss19,.jss18") %>% html_text()
temp2 <- tmp
temp2[temp2==""] <- "KEY_PHRASE_HERE"
temp2 <- temp2 %>% paste(collapse = "") %>% str_split(pattern = "KEY_PHRASE_HERE")
temp2 <- temp2[[1]][-1]
temp2
> [1] "デフォルトで指定される絵文字を季節に合わせたものにしました!"
> [2] "実験的機能として、「\n名前で投稿者の身元を捜索!」\nを実装しました。"
> [3] "実験的機能として、「名前で日記を検索!」を実装しました。"
> [4] "Rからでも投稿できるようになりました!普通にサイトから投稿した方が便利です!"
> [5] "・空白の日記が投稿できる問題を修正しました。"
> [6] "未来からの投稿を、表示できるようにしました。"
> [7] "投稿後、おならが止まらなくなるようにしました。"
> [8] "投稿者を自動的に5Gと接続しDNAを絵文字に書き換えるようにしました。"
> [9] "投稿後、全絵文字を強制的にひよこに変換し表示するようにしました。"
> [10] "・名前の上限を9文字にしました ・日付を選択していないとき、全期間の日記が表示されるようにしました"
> [11] "投稿一覧から,投稿をシェアできるようになりました!"
brows$close()
RMeCabを使ってスクレイピングした文書の解析をしましょう。
RMeCabの導入についてはここら辺を参考にしてください。
Windows \(\rightarrow\) https://qiita.com/nkojima/items/54ca56ac819e5e085b0a
Mac \(\rightarrow\) https://qiita.com/Haruka-Ogawa/items/64efefa861a49eaa4e71
特にWindowsの方は、インストール先がOnedrive等にならないよう気をつけてください。
ざっくりと、解析ソフトの MeCab と、MeCabが使う辞書、MeCabをRで動かす為のパッケージ、 RMeCab が必要になります。
先ほどの日記の2つめを品詞分解してみます。
library(RMeCab)
RMeCabC(endata[2]) %>% unlist()
> 名詞 助詞 動詞 助動詞 名詞 助詞 動詞 助動詞 助詞 動詞 助動詞
> "友達" "に" "借り" "た" "時計" "を" "返そ" "う" "と" "思っ" "たら"
> 名詞 助詞 動詞 助動詞 記号 接続詞 名詞 名詞 助詞 動詞 助動詞
> "時計" "を" "忘れ" "た" "。" "また" "明日" "返し" "に" "行こ" "う"
> 記号
> "。"
なお、 RMeCabC 関数には、 mypref という引数があります。デフォルトでは0になっていますが、これを1にすると
RMeCabC(endata[2],mypref = 1) %>% unlist()
> 名詞 助詞 動詞 助動詞 名詞 助詞 動詞 助動詞
> "友達" "に" "借りる" "た" "時計" "を" "返す" "う"
> 助詞 動詞 助動詞 名詞 助詞 動詞 助動詞 記号
> "と" "思う" "た" "時計" "を" "忘れる" "た" "。"
> 接続詞 名詞 名詞 助詞 動詞 助動詞 記号
> "また" "明日" "返し" "に" "行く" "う" "。"
という感じで、標準形で結果が返ってきます。テキスト解析ではこちらの方式が便利です。
さて、どの日記にどんな名詞が何回出てきたかの表を作ってみましょう。
dM <- docMatrixDF(endata,"名詞")
> to make data frame
dM
> ROW.1 ROW.2 ROW.3 ROW.4 ROW.5 ROW.6 ROW.7 ROW.8 ROW.9 ROW.10 ROW.11
> 1 0 0 0 0 1 0 0 0 0 0 0
> 2021 0 0 0 0 0 0 1 0 0 0 0
> 2022 0 0 0 0 0 0 1 0 0 0 0
> 3 0 0 0 0 0 0 0 0 0 0 0
> BDSP 0 0 0 0 0 0 0 0 0 0 0
> ♪ 0 0 0 0 0 0 0 0 0 0 0
> お腹 0 0 0 0 0 0 0 0 0 0 0
> こと 0 0 0 1 0 0 0 0 0 0 0
> ご存知 0 0 0 0 0 0 0 0 0 1 0
> さ 0 0 0 0 0 0 0 0 0 0 0
> みたい 0 0 0 0 0 0 0 0 0 0 0
> みなさん 1 0 0 0 0 0 0 0 0 0 0
> みんな 0 0 0 0 0 0 0 0 0 0 0
> め 0 0 0 0 0 0 0 1 0 0 0
> もの 0 0 0 2 0 0 0 0 0 0 0
> ん 0 0 0 0 0 0 0 0 0 0 0
> カレー 0 0 0 0 0 0 0 0 0 0 0
> クリスマス 0 0 0 0 0 0 0 0 0 1 0
> コード 0 0 0 0 0 0 0 0 0 0 0
> スープ 0 0 0 0 0 0 0 0 0 0 0
> デフォルト 0 0 0 0 0 0 0 0 0 1 0
> ドキドキ 0 0 0 0 0 0 0 0 0 0 0
> バグ 0 0 0 0 0 0 0 0 0 0 0
> ホテル 0 0 0 0 0 0 0 0 0 0 0
> 事件 0 0 0 0 0 0 0 0 0 0 0
> 今 0 0 1 0 0 0 0 0 0 0 0
> 今日 0 0 0 1 0 0 0 0 0 0 0
> 仕方 0 0 0 0 0 0 0 0 0 0 0
> 仕様 0 0 0 0 0 0 0 0 0 1 0
> 何 0 0 0 0 0 0 0 0 0 0 0
> 修学旅行 0 0 0 0 0 0 0 0 0 0 0
> 冬 0 0 0 0 0 0 0 0 0 0 0
> 友達 0 1 0 0 0 0 0 0 0 0 0
> 回 0 0 0 0 0 0 0 0 0 0 0
> 外 0 0 0 1 0 0 0 0 0 0 0
> 大体 0 0 0 1 0 0 0 0 0 0 0
> 尻 0 0 1 0 0 0 0 0 0 0 0
> 年 0 0 0 0 0 0 2 0 0 0 0
> 心臓 0 0 0 0 0 0 0 0 0 0 0
> 必要 0 0 0 2 0 0 0 0 0 0 0
> 手 0 0 0 0 0 0 0 0 0 0 0
> 日 0 0 0 0 0 0 0 0 0 0 0
> 日記 0 0 0 0 0 1 0 0 0 0 0
> 日間 0 0 0 0 0 0 0 0 0 0 0
> 明日 0 1 0 0 0 0 0 0 0 0 0
> 時計 0 2 0 0 0 0 0 0 0 0 0
> 最後 0 0 0 0 0 0 0 0 0 0 0
> 月 0 0 0 0 1 0 0 0 0 0 0
> 朝 0 0 0 0 0 0 0 0 0 0 0
> 東京 0 0 0 0 0 0 0 0 0 0 0
> 歯 0 0 0 0 0 0 0 0 0 0 0
> 沖縄 0 0 0 0 0 0 0 0 0 0 0
> 焼き肉 0 0 0 0 0 0 0 0 0 0 0
> 皆さん 0 0 0 0 1 1 1 0 0 0 0
> 眠気 0 0 0 0 0 0 0 0 0 0 0
> 社長 1 0 0 0 1 0 0 0 0 0 0
> 穴 0 0 0 0 0 0 0 0 0 0 0
> 経験 0 0 0 0 0 0 0 0 0 0 0
> 絵文字 0 0 0 0 0 0 0 0 0 1 0
> 職人 0 0 0 0 0 1 0 0 0 0 0
> 胃 0 0 0 0 0 0 0 0 0 0 0
> 自分 0 0 0 0 0 0 0 0 0 0 0
> 自転車 0 0 0 0 0 0 0 0 0 0 0
> 返し 0 1 0 0 0 0 0 0 0 0 0
> 鉄壁 0 0 0 0 0 0 0 0 0 0 0
> 鍵 0 0 0 0 0 0 0 0 0 0 0
> 雪 0 0 0 0 0 0 0 0 0 0 0
> 2月 1 0 0 0 0 0 0 0 0 0 0
> ROW.12 ROW.13 ROW.14 ROW.15 ROW.16 ROW.17 ROW.18 ROW.19 ROW.20
> 1 0 0 0 0 0 0 0 0 1
> 2021 0 0 0 0 0 0 0 0 0
> 2022 0 0 0 0 0 0 0 0 0
> 3 1 0 0 0 0 0 0 0 0
> BDSP 0 0 0 1 0 0 0 0 0
> ♪ 0 0 0 0 0 0 1 0 0
> お腹 0 0 0 0 0 0 0 0 1
> こと 0 0 0 0 0 1 0 0 0
> ご存知 0 0 0 0 0 0 0 0 0
> さ 0 1 0 0 0 0 0 0 0
> みたい 1 0 0 0 0 0 0 0 0
> みなさん 0 0 0 0 0 0 0 0 0
> みんな 0 0 0 0 0 1 0 0 0
> め 0 0 0 0 0 0 0 0 0
> もの 0 0 0 0 0 0 0 0 0
> ん 0 0 0 1 0 0 2 0 0
> カレー 0 0 0 0 0 0 0 1 0
> クリスマス 0 0 0 0 0 0 0 0 0
> コード 0 0 0 1 0 0 0 0 0
> スープ 0 0 0 0 0 0 0 1 0
> デフォルト 0 0 0 0 0 0 0 0 0
> ドキドキ 0 1 0 0 0 0 0 0 0
> バグ 0 0 0 2 0 0 0 0 0
> ホテル 0 0 0 0 0 0 1 0 0
> 事件 0 0 1 0 0 0 0 0 0
> 今 0 0 0 0 0 0 0 0 0
> 今日 1 0 0 0 0 0 1 1 0
> 仕方 0 0 0 1 0 0 0 0 0
> 仕様 0 0 0 0 1 0 0 0 0
> 何 0 0 0 0 0 0 0 0 1
> 修学旅行 1 0 0 0 0 0 0 0 0
> 冬 0 0 1 0 1 0 0 0 0
> 友達 0 0 0 0 0 0 0 0 0
> 回 0 0 0 0 0 0 0 0 1
> 外 0 0 0 0 0 0 0 0 0
> 大体 0 0 0 0 0 0 0 0 0
> 尻 0 0 0 0 0 0 0 0 0
> 年 0 0 0 0 0 0 0 0 0
> 心臓 0 1 0 0 0 0 0 0 0
> 必要 0 0 0 0 0 0 0 0 0
> 手 0 1 0 0 0 0 0 0 0
> 日 0 0 0 0 0 0 0 0 1
> 日記 0 0 0 0 0 1 0 0 0
> 日間 1 0 0 0 0 0 0 0 0
> 明日 0 0 0 0 0 0 0 1 0
> 時計 0 0 0 0 0 0 0 0 0
> 最後 0 0 1 0 0 0 0 0 0
> 月 0 0 0 0 0 0 0 0 0
> 朝 0 1 0 0 0 0 0 0 0
> 東京 1 0 0 0 0 0 0 0 0
> 歯 0 0 0 0 0 0 0 0 1
> 沖縄 1 0 0 0 0 0 0 0 0
> 焼き肉 0 0 0 0 0 0 0 1 0
> 皆さん 0 0 0 0 0 0 0 0 0
> 眠気 0 1 0 0 0 0 0 0 0
> 社長 0 0 0 0 0 0 0 0 0
> 穴 0 0 1 0 0 0 0 0 0
> 経験 0 0 1 0 0 0 0 0 0
> 絵文字 0 0 0 0 0 1 0 0 0
> 職人 0 0 0 0 0 0 0 0 0
> 胃 0 0 0 0 0 0 0 0 1
> 自分 0 0 0 1 0 0 0 0 0
> 自転車 0 0 1 0 0 0 0 0 0
> 返し 0 0 0 0 0 0 0 0 0
> 鉄壁 0 0 0 0 0 0 0 0 1
> 鍵 0 0 2 0 0 0 0 0 0
> 雪 1 0 0 0 0 0 0 0 0
> 2月 0 0 0 0 0 0 0 0 0
日記だと微妙なので、楽天の商品名でもやってみます。記号が邪魔なのである程度消しています。
dM2 <- docMatrixDF(rakudata$goods %>% str_remove_all(c("\\W","\\d")),"名詞")
> to make data frame
今回は登場する回数の多い順で名詞を並べましょう。
colnames(dM2) <- rakudata$rank
dM2[order(rowSums(dM2),decreasing = T),] %>% head()
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
> 円 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 1 1 1 1 1 0 0 0 0 1
> マスク 0 0 0 0 0 0 2 0 0 0 0 0 4 0 3 0 0 0 2 3 2 0 0 0 0 3
> 限定 2 0 1 1 0 1 1 1 1 2 0 0 1 0 0 2 0 0 1 0 1 1 2 2 1 0
> クーポン 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 1 1 1 0 0 0 0 1
> 盤 3 3 1 1 3 1 0 0 1 3 0 0 0 0 0 0 0 0 0 0 0 1 0 3 1 0
> 1 0 0 0 0 0 0 2 0 0 0 1 0 2 0 2 0 0 0 1 0 2 0 0 0 0 0
> 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
> 円 0 0 0 1 0 1 0 1 1 1 0 0 1 0 1 0 1 0 0 0 1 0 0
> マスク 4 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0
> 限定 1 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0
> クーポン 0 0 0 1 0 1 0 1 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0
> 盤 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0
> 1 2 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 2 0 1 0 1
> 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
> 円 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 2 0
> マスク 0 0 0 3 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0
> 限定 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 1 0 0
> クーポン 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 1 1 0
> 盤 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
> 1 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0
> 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
> 円 0 0 3 0 0 0 1 0 0 3 1 3 0 0 0 0 0 0 2 1 1 0 0
> マスク 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
> 限定 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0
> クーポン 0 1 1 0 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0
> 盤 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
> 1 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
> 96 97 98 99 100
> 円 0 1 0 0 3
> マスク 0 0 0 0 0
> 限定 0 0 0 0 0
> クーポン 0 1 0 0 1
> 盤 0 0 0 3 0
> 1 0 0 0 0 0
マスクと限定商品が強そうですね。