[R] PTT推文文化的社群網絡分析(Social Network Analysis),帶你一窺社群網絡中的互動情況

Bryan Yang
A multi hyphen life
6 min readMay 11, 2018

爬網+社群網絡分析

PTT是台灣最大的社群網站(http://zh.wikipedia.org/wiki/批踢踢),很多人介紹過我就不多費脣舌,PTT中很特別的功能就是可以推文回應作者,畢竟回文相對來說比較正式,推文感覺更加閒聊的性質.Wxxxxd版相對PTT中的其他版來說,由於有每日發文限制,版友之間更頃向利用推文的方式來互動,而且因為男多女少的緣故,通常女生的推文比較多人推,男生(例如我)可能發文都沒人鳥,然後網友之間彼此也會利用推文互動,本文就是要以圖像化的方式來展現這種推文互動關係。

我知道大家對程式語言沒興趣,所以我先講怎樣看圖:

  1. 每個圈圈代表一個網友,圈圈的大小代表該網友被“不同人”推文的文章次數
  2. 箭頭代表的推文的方向 A -> B表示A推B的文章,而且不同大小的圈圈

從圖中來看,可以發現網友之間的互動真的非常密切和多樣,除了幾位人氣王之外,也有很多小團體和出現眾星拱月的情況.人氣王彼此之間的互動也不在少數,相信有逛過wanted版的朋友看起來會更有感觸.至於ID的部分我就隱藏起來了,雖然是這是公開資料,但是畢竟不是每個人都喜歡被別人分析XD

以下講的是做法:本次分析包含資料截取到畫圖都是利用R軟體來實做,用的主要方法包括爬網和社群網絡分析(social network analysis),以下是原始碼.

安裝 Rstudio

當然要用 Docker 比較方便…

docker pull rocker/rstudio

開啟的時候記得要開 root 才能安裝套件

docker run -d -p 8787:8787 -e ROOT=TRUE rocker/rstudio

安裝 ubuntu 套件

sudo apt-get update
sudo apt-get install libxml2-dev

安裝 R 套件

install.packages(c("xml2","XML","httr","stringr", "igraph", "dplyr"));

爬網

library("XML")
library("httr")
library("stringr")
library("igraph")
library("dplyr")
##這部分是爬網,先從網頁版PTT中抓下每篇文章的聯結
rm(data)
data <- list()
for( i in 1628:1630){
url <- paste('bbs/Wanted/index', i, '.html', sep='')
html <- content(GET("https://www.ptt.cc/", path = url),as = 'parsed')
htmlParsed <- htmlParse(html, asText=TRUE)
url.list <- xpathSApply(htmlParsed, "//div[@class='title']/a[@href]", xmlAttrs)
data <- rbind(data, url.list[[1]])
}
##把一些空值刪掉
data = Filter(function(x) x != "www.ptt.cc",data)
##根據每篇文章代碼進去抓作者和推文的資料
popu <- function(link){
html <- content(GET('https://www.ptt.cc', path = data[[link]]), as = 'parsed')
htmlParsed <- htmlParse(html, asText=TRUE)
poster <- xpathApply(htmlParsed,"//div[@class='article-metaline']/span[@class='article-meta-value']",xmlValue)[[1]]

#這邊特別說明一下,因為作者這個欄位很討厭,除了ID之外還有暱稱,要把暱稱刪掉
poster <- str_split_fixed(poster, "\\(",2)[,1]
puller <- xpathApply(htmlParsed,"//div[@class='push']/span[@class='f3 hl push-userid']",xmlValue)

#這個if是排除那些沒人有推文的作者(例如我)
if (length(puller) >0 ){
puller <- as.data.frame(matrix(unlist(puller),nrow = length(puller),T))
edgelist <- merge(poster,puller,all = TRUE)
return (edgelist)
}
}

整理資料

edge = list()
for (l in 1:length(data)){
edge <- rbind(edge, popu(l))
}
#依照推文和被推者分組,計算每個pair的數量
edge2 <- group_by(edge,x,V1)
edge2 <- summarise(edge2,weight=n())
#from 表示推文者 in 表示被推文的
edge2 <- data.frame(from = edge2$V1, to = edge2$x,weight = edge2$weight)
  • 原始的 Edge
  • 計算 Weight

最後畫圖

g <- graph.data.frame(edge2,directed = TRUE)#用被推文的數量“in"來決定大小
V(g)$size <- degree(g,mode = ("in"))/2
#接下來就是畫圖輸出了
png(filename="org_network.png", height=1080, width=1080)
plot(g, layout = layout.fruchterman.reingold,
vertex.label=NA,
edge.arrow.size = 1
)

--

--

Bryan Yang
A multi hyphen life

Data Engineer, Data Producer Manager, Data Solution Architect