Twitterデータから位置情報を取得し可視化してみた
Twitterをやっていると、様々な土地の人からフォローされますよね。特に公式Twitterを運用していると、今後ビジネスに関わる公式企業アカウントさんがフォロワーにいるかもしれません。今回はTwitterのデータを収集し、整形、可視化することで、どこを拠点にしているアカウントが、どれたけのインフルエンス力(=フォロワー数)があるのかを可視化してみました。
Twitterフォロワーの登録地をマッピング!
【公式PigDataのフォロワーマップ】
※2021/11/17現在、公式PigDataのTwitterアカウントは新アカウントで運用中です。

(公式PigDataのTwitterアカウント:https://twitter.com/pigdata_pigtan)
上記のマップをつくりだすために、いくつかの段階を踏みました。
- ①データ収集⇒twitterのフォロワーデータをスクレイピング
- ②データ整形⇒住所を緯度経度の数値に変換
- ③データの可視化⇒日本地図上に各フォロワーを表示
ひとつずつ、方法を確認していきましょう。
①Twitterのフォロワーデータを収集
まず、Twitterで公式PigDataのフォロワーページ一覧から、適当なフォロワーページを開き、どのデータを収集するか確認します。
今回は
- Twitter名
- 所在地
- フォロワー数
を収集しました。
Twitterからのデータ収集方法はスクレイピングツールを用いたり、データ収集代行をすることで、ひとつひとつコピー&ペーストを手で行うことなく手軽に収集できます。
公式PigDataのフォロワーデータは以下のように収集できました!

②データを整形
日本地図にフォロワーの位置を表示するためには住所ではなく、その住所が示す「緯度経度」の数値が必要となります。 収集した所在地データは東京都○○区のような住所になっていることが多いため、緯度経度に変換する必要があります。
pythonの活用
収集したデータから、住所を緯度経度に変換します。所在地データが空白のもの、表記が住所ではないものはスキップするようになっています。
⇒使用したpythonコードはこちら

(整形し、アウトプットしたデータ)
③フォロワーをマッピング!
整形したデータを用いて、フォロワーマップを作製していきます。フォロワー数ごとに色分けすることで、インフルエンス力の強さを可視化してみました。
フォロワー数10,000以上:赤
フォロワー数3,000以上9,999以下:ピンク
フォロワー数2,999以下:青
⇒使用したpythonコードはこちら
実際に公式PigDataには339名のフォロワーがいたのですが(2019年1月20日時点)、データ整形していくことで実際マッピングできたのは258名分のデータのみになりました。(表示されていないフォロワーさん、すみません) また、「東京都中央区」のように詳細でない所在地を記載の場合は他のフォロワーさんとかぶってしまって見えなくなっています。
フォロワーマップでSNS戦略を
マップをじっくり見ていくと、今後のSNSマーケティングの戦略がみえてきます。
【東京近郊】

【大阪近郊】

結果、公式PigDataのフォロワーさんは東京の方が多かったのですが、フォロワー数10,000以上のフォロワーさんの割合は大阪の方が多そうだということがわかりました。これから公式PigDataがSNSマーケティングをしていく上で、大阪方面に関わることを発信するとマーケティング効果が高いかもしれませんね!
まだまだ公式PigDataのフォロワー数は400弱で、精査するとさらに少なくなっています。この少ないデータだけで確実なSNSマーケティング戦略を考える!とまではいきませんが、多くのデータを収集することで、より正確なデータ分析につながるでしょう。まずは様々なところからより多くのデータを収集するとよいでしょう。
しかし、データを「スクレイピング(クローリング)」で収集する際には法律面など様々な注意点があるので、始める前にチェックしておきましょう。
使用したpythonのコード
所在地データを緯度経度にするためのPythonコード
import requests import xlrd import csv from tqdm import tqdm from bs4 import BeautifulSoup from time import sleep from pprint import pprint 定数 # ファイル名情報 INPUT_FILE_NAME_EXCEL = 'followermap_data.xlsx' # [読込ファイル名] OUTPUT_FILE_NAME = 'output.csv' # [出力ファイル名] 読込ファイル情報 HEADER_NO = 0 # [ヘッダー];1行目 START_ROW_NO = 1 # [データ始まり行];2行目 ADDRESS_ROW_NO = 1 # [住所];2列目 SHEET_NO = 0 # [シートNo];1シート目 API情報 API_INTERVAL = 5 # [API使用間隔]: 5秒. 小さい程APIアクセス制限にかかりやすい. # 1. 住所列のデータ取得Funtion def get_address(): BOOK = xlrd.open_workbook(INPUT_FILE_NAME_EXCEL) SHEET = BOOK.sheet_by_index(SHEET_NO) address_list = [] for row_index in range(START_ROW_NO, SHEET.nrows): try: row = SHEET.row_values(row_index) address = row[ADDRESS_ROW_NO] address_list.append(address) except: print("-- Error --")return address_list
2. 緯度経度取得Funtion def get_lat_lon_address(address_list): url = 'http://www.geocoding.jp/api/' lat_lon = [] for address in tqdm(address_list): # 住所が存在する場合 if address: sleep(API_INTERVAL) print("address : " + address) payload = {'q': address} r = requests.get(url, params=payload) ret = BeautifulSoup(r.content, "html.parser") if ret.find('error'): print(f"Invalid address submitted. {address}") lat_lon.append(["", ""]) else: try: lat = ret.find('lat').string lon = ret.find('lng').string except: print("---- APIアクセス制限にかかりました. 6時間後に実行できます. 確認URL: http://www.geocoding.jp/api/ ----") lat_lon.append([lat, lon]) # 住所が空の場合 else: lat_lon.append(["", ""]) return lat_lon 3. 出力ファイル書き出しFuntion def write_output(lat_lon_address): BOOK = xlrd.open_workbook(INPUT_FILE_NAME_EXCEL) SHEET = BOOK.sheet_by_index(SHEET_NO) output_data = [] for row_index in range(START_ROW_NO, SHEET.nrows): row = SHEET.row_values(row_index) row.extend(lat_lon_address[row_index-START_ROW_NO]) output_data.append(row)with open(OUTPUT_FILE_NAME, 'w', newline='', encoding="SHIFT-JIS", errors="ignore") as f: writer = csv.writer(f, lineterminator='\n') header = SHEET.row_values(HEADER_NO) header.extend(["緯度", "経度"]) # ヘッダー書込 writer.writerow(header) # 緯度経度書込 writer.writerows(output_data)
if name == 'main': print("-- 緯度経度取得START. --") # 1. 住所列のデータ取得 address_list = get_address() # 2. 緯度経度取得 lat_lon_address = get_lat_lon_address(address_list) # 3. 出力ファイル書き出し write_output(lat_lon_address) print("-- outputファイルに出力されました. --")
整形したデータで日本地図にマッピングするためのPythonコード
import folium import pandas as pd # 定数 ################################################################################################### # ファイル名情報 INPUT_FILE_NAME_CSV = 'output.csv' # [読込ファイル名(緯度経度)] OUTPUT_FILE_NAME_HTML = 'map.html' # [出力ファイル名] # 読込ファイル情報 NAME_ROW_NO = 0 # [マーカー表示名];1列目 FOLLOWER_ROW_NO = 2 # [フォロワー数];3列目 LATITUDE_ROW_NO = 3 # [緯度];4列目 LONGITUDE_ROW_NO = 4 # [経度];5列目 # フォロワーマップ LOW_FOLLOWER_COLOR = 'blue' # [フォロワー数:小]: 青色 MIDDLE_FOLLOWER_COLOR = 'purple' # [フォロワー数:中]: 紫色 HIGH_FOLLOWER_COLOR = 'red' # [フォロワー数:大]: 紫色 MIDDLE_FOLLOWER_NUM = 3000 # [フォロワー数:中]: 3000人以上 HIGH_FOLLOWER_NUM = 10000 # [フォロワー数:大]: 10000人以上 MAP_ZOOM = 6 # [マップズームの大きさ]: 大きい程、ズームする MAP_CIRCLE_WEIGHT = 1 # [フォロワーマップサークル重み]: 大きい程、円は大きい CIRCLE_COLOR = '#3186cc' # [サークルの色] JAPAN_LATITUDE = 35.6792 # [日本基準緯度] JAPAN_LONGITUDE = 139.7406 # [日本基準緯度] ################################################################################################### # フォロワーマップ作成Funtion def create_follower_map(): # 緯度経度含むフォロワーデータ読込 follower_data = pd.read_csv(INPUT_FILE_NAME_CSV, encoding="SHIFT-JIS") # カラム名抽出 NAME_ROW_NAME = follower_data.columns[NAME_ROW_NO] FOLLOWER_ROW_NAME = follower_data.columns[FOLLOWER_ROW_NO] LATITUDE_ROW_NAME = follower_data.columns[LATITUDE_ROW_NO] LONGITUDE_ROW_NAME = follower_data.columns[LONGITUDE_ROW_NO] # 地図の基準, 地図ズーム follower_map = folium.Map(location=[JAPAN_LATITUDE, JAPAN_LONGITUDE], zoom_start=MAP_ZOOM) for i, data in follower_data.iterrows(): # 空データがある場合はスキップ if not isNaN(data[NAME_ROW_NAME]) or not isNaN(data[FOLLOWER_ROW_NAME]) or not isNaN(data[LATITUDE_ROW_NAME]) or not isNaN(data[LONGITUDE_ROW_NAME]): continue # フォロワー数毎に作成 follower_num = int(data[FOLLOWER_ROW_NAME].replace('.', '').replace(',', '')) # フォロワー数: 小 if follower_num < MIDDLE_FOLLOWER_NUM: folium.Marker(location=[data[LATITUDE_ROW_NAME], data[LONGITUDE_ROW_NAME]], popup=data[NAME_ROW_NAME]+" "+str(follower_num), icon=folium.Icon(color=LOW_FOLLOWER_COLOR)).add_to(follower_map) # フォロワー数: 中 elif follower_num < HIGH_FOLLOWER_NUM: folium.Marker(location=[data[LATITUDE_ROW_NAME], data[LONGITUDE_ROW_NAME]], popup=data[NAME_ROW_NAME]+" "+str(follower_num), icon=folium.Icon(color=MIDDLE_FOLLOWER_COLOR)).add_to(follower_map) # フォロワー数: 大 elif follower_num >= HIGH_FOLLOWER_NUM: folium.Marker(location=[data[LATITUDE_ROW_NAME], data[LONGITUDE_ROW_NAME]], popup=data[NAME_ROW_NAME]+" "+str(follower_num), icon=folium.Icon(color=HIGH_FOLLOWER_COLOR)).add_to(follower_map) # フォロワーの数に対応するサークルマーカーを置く #folium.Circle( # location = [data[LATITUDE_ROW_NAME], data[LONGITUDE_ROW_NAME]], # radius = follower_num*MAP_CIRCLE_WEIGHT, # popup = data[NAME_ROW_NAME], # color = CIRCLE_COLOR, # fill_color = CIRCLE_COLOR, # #fill = True #).add_to(follower_map) follower_map.save(OUTPUT_FILE_NAME_HTML) # 空データ判定 def isNaN(num): return num == num if __name__ == '__main__': print("-- フォロワーマップ作成START. --") # フォロワーマップ作成 create_follower_map() print("-- htmlファイルに出力されました. --")