仮想サーファーの波乗り

仮想サーファーの日常

プログラミング・エンジニアのスキルアップ・ブログ運営などに関してよく書く雑記ブログ

➡ Udemyで6/4(木)まで割引セール開催中! 1,500円〜で普段の90%OFFも!

スクレイピングとテキストマイニングで2018年のニュースを効率的に振り返った


f:id:virtual-surfer:20181223160712p:plain

先日、noteでこちらの記事を公開しましたが、スクレイピングとテキストマイニングで1年間のニュースを振り返るの結構効率的だなと思ったので書きます。

note.mu

主にプログラムと使った技術のこと書きます。


ニュース記事のスクレイピングとテキストマイニングの仕組み


ニュース記事振り返り効率化の仕組み

仕組みを簡単に図示したものが以下。

f:id:virtual-surfer:20181223152037p:plain

処理の流れとしては以下。

① スクレイピングできそうなITメディアサイト選出
② サイト別にスクレイピングする(タグ名とか違うからしょうがない)
③ Spread Sheetに出力する
④ テキストマイニング(出現率高い単語で記事名検索)
⑤ 抽出された記事名/リンクをもとに記事書く

UserLocalのテキストマイニングツールは文章をコピペするだけで無料でテキストマイニング→結果をビジュアル化してくれるので有益。


わりと簡単な仕組みでニュースの振り返り作業を効率化できるのでオススメです( ・v・)b

各ニュースメディアからのスクレイピングのコードも載せておきますね。


ニュース記事のスクレイピング

今回は、以下のニュースメディアをスクレイピングしました。

・Tech Crunch
・Yahoo! News IT
・日経XTECH
・techable
・GIZMODE
・GIGAZINE
・POSTD

他にも対象にしたメディアはあったのですが、Ajax通信で次のページを取得しているからスクレイピングをしづらかったり、タグ名が明らかにスクレイピングを嫌っているなというメディアは対象外にしました。


ニュース記事振り返り効率化のプログラム

プログラムはSeleniumを活用しました。

一例として、Tech Crunchのスクレイピング・Spread Sheet書き出しコードがこちら。


techcrunch_news.py

# coding=utf-8
import os
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.keys import Keys


SCOPE_URL = 'https://spreadsheets.google.com/feeds'
CREDENTIAL_FILE_NAME = 'spread_sheet_credential.json'
TEMPLATE_FILE_NAME = 'spread_sheet_credential_template.txt'
SHEET_PROJECT_ID = os.environ['SHEET_PROJECT_ID']
SHEET_PRIVATE_KEY_ID = os.environ['SHEET_PRIVATE_KEY_ID']
SHEET_PRIVATE_KEY = os.environ['SHEET_PRIVATE_KEY']
SHEET_CLIENT_EMAIL = os.environ['SHEET_CLIENT_EMAIL']
SHEET_CLIENT_ID = os.environ['SHEET_CLIENT_ID']
SHEET_CLIENT_X509_CERT_URL = os.environ['SHEET_CLIENT_X509_CERT_URL']


def write_news(sheet, link, max_loop_count):
    driver = webdriver.PhantomJS()
    driver.get(link)
    loop_count = 0
    while loop_count < max_loop_count:
        loop_count += 1
        print('-------------- \{\}回目のページアクセス... --------------'.format(loop_count))
        # Spread Sheet書き込み
        write_techcrunch_news_elements(driver, sheet)
        # 次のページへアクセス
        driver = access_to_next(driver)


def access_to_next(driver):
    next = driver.find_element_by_link_text('次へ')
    # タイムアウトが発生したら新しくページにアクセスする
    page_content = '/page/'
    url = driver.current_url
    splited_url_contents = url.split(page_content)
    next_url = splited_url_contents[0] + page_content + str(int(splited_url_contents[1].split('/')[0]) + 1)
    try:
        next.click()
    except Exception as e:
        print('Timeoutが発生したので新しく「\{\}」にアクセスする。'.format(next_url))
        driver = webdriver.PhantomJS()
        driver.get(next_url)
    return driver


def write_techcrunch_news_elements(driver, sheet):
    # ページが完全に読み込まれるまでの時間を加味して最大10秒間待つ
    driver.set_page_load_timeout(10)
    title_dict = {}
    blocks = driver.find_elements_by_class_name('river-block')
    count = 0
    for block in blocks:
        count += 1
        ad_contain = None
        print('----- \{\}個目のriver-blockは... -----'.format(count))
        try:
            ad_contain = block.find_element_by_class_name('ad-contain')
        except Exception as e:
            try:
                news_title = block.find_element_by_class_name('post-title').find_element_by_tag_name('a').text
                news_time = block.find_element_by_tag_name('time').get_attribute('datetime')
                title_dict[news_title] = news_time
                print('News No.\{\} title:\{\} date:\{\}'.format(count, news_title, news_time))
            except Exception as e:
                print('スポンサー記事だった.'.format(count))
                continue
        if ad_contain is not None:
            print('広告だった.??'.format(count))
    write_to_sheet(sheet, title_dict)


def write_to_sheet(sheet, dict):
    keys = list(dict.keys())
    values = list(dict.values())
    titles = sheet.col_values(1)
    start_row_num = len(titles) + 1
    start_row = str(len(titles) + 1)
    end_row = str(len(keys) + start_row_num)
    # Spread Sheetに書き込み
    update_cells_with_list(sheet, 'A'+start_row, 'A'+end_row, keys, value_input_option='USER_ENTERED')
    update_cells_with_list(sheet, 'B'+start_row, 'B'+end_row, values, value_input_option='USER_ENTERED')


def access_to_sheet(gid):
    # 書き込み用ファイル
    credential_file = open(CREDENTIAL_FILE_NAME, 'w')
    # テンプレートファイルを読み込み、書き込みファイルに書き込み
    template_file = open(TEMPLATE_FILE_NAME)
    template_file_lines = template_file.readlines()
    line_num = 0
    for line in template_file_lines:
        line_num += 1
        if line_num == 3:
            line = line.format(SHEET_PROJECT_ID)
        elif line_num == 4:
            line = line.format(SHEET_PRIVATE_KEY_ID)
        elif line_num == 5:
            line = line.format(SHEET_PRIVATE_KEY)
        elif line_num == 6:
            line = line.format(SHEET_CLIENT_EMAIL)
        elif line_num == 7:
            line = line.format(SHEET_CLIENT_ID)
        elif line_num == 11:
            line = line.format(SHEET_CLIENT_X509_CERT_URL)
        credential_file.writelines(line)
    template_file.close()
    credential_file.close()
    credentials = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIAL_FILE_NAME, SCOPE_URL)
    client = gspread.authorize(credentials)
    return client.open_by_key(gid)


def update_cells_with_list(sheet, from_cell, to_cell, id_list, value_input_option):
    cell_list = sheet.range('\{\}:\{\}'.format(from_cell, to_cell))
    count_num = -1
    for cell in cell_list:
        count_num += 1
        try:
            val = id_list[count_num]
        except Exception as e:
            continue
        if val is None:
            continue
        cell.value = val
    print('\{\}から\{\}まで書き込むよ'.format(from_cell, to_cell))
    sheet.update_cells(cell_list, value_input_option=value_input_option)


# 書き込みしたいSpread SheetのシートID
sheet_gid = 'xxxxx'
sheet_name = 'Tech Crunch'
target_link = 'https://jp.techcrunch.com/page/149/'
max_loop_count = 50
# Spread Sheetの書き込みできる一番上の行番号を確認する
sheet = access_sheet(sheet_gid).worksheet(sheet_name)
write_news(sheet, target_link, max_loop_count)


spread_sheet_credential_template.txt

{
  "type": "service_account",
  "project_id": "{}",
  "private_key_id": "{}",
  "private_key": "-----BEGIN PRIVATE KEY-----{}-----END PRIVATE KEY-----\n",
  "client_email": "{}",
  "client_id": "{}",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "{}"
}


.env

SHEET_PROJECT_ID=xxxxx
SHEET_PRIVATE_KEY_ID=xxxxx
SHEET_PRIVATE_KEY=xxxxx
SHEET_CLIENT_EMAIL=xxxxx
SHEET_CLIENT_ID=xxxxx
SHEET_CLIENT_X509_CERT_URL=xxxxx

ファイルを作成したら「xxxxx」の箇所を適宜書き換え、必要なライブラリをpipでインストールして、foregoを使ってPythonファイルを動かせば、スクレイピングをじゃんじゃんしてSpread Sheetに書き込みしていってくれます。


f:id:virtual-surfer:20181223161029g:plain


Spread Sheetへの認証方法やPythonのプログラミング方法は、以下のnoteを参考にしてください。

note.mu


他のメディアに関しても、「write_techcrunch_news_elements()」メソッドと「access_to_next()」メソッドの中身の要素の取得方法を書き換えて、URLの指定を変更すればスクレイピングできる。


以上、「スクレイピング × Spread Sheet書き出し × 作業」は、ほんと有益だよという話でした。

まだまだ効率化できる作業は多そうだし、試してみたい。


Pythonをしっかり学びたい方向け

最後に、Pythonをしっかり学びたいという方向けにおすすめの学習ツールを紹介しておきます。


スッキリわかるPython入門 スッキリわかるシリーズ

そもそもPythonの書き方が分からないという方はこちら、安いし分かりやすいのでオススメです。


プログラミング言語 Python 3 入門

f:id:virtual-surfer:20191207103500p:plain

このコースで学べること

  • Pythonの基礎(データ、制御フロー、関数、データ構造、モジュール、例外(エラー)、クラス、入力と出力、標準ライブラリ)
  • ターミナルを使用したPythonの実行
  • PyCharmを使用したPython ファイルの作成・デバッグ実行

Pythonの基礎の基礎が丁寧に開設されていて、Python初心者の方でもPythonへの理解が深まりやすいUdemyのコースです。

Pythonの仕組みを基礎からしっかりと理解しておきたい方にオススメです。


【プログラミング言語 Python 3 入門】をUdemyで見てみる


Pythonで作業の自動化・効率化

Pythonでのプログラミング学習中の方向けに、Noteでより詳細なプログラミングチュートリアルを配信しているので、そちらもチェックしてみてください( ・v・)/

note.mu


では!