tech

【まとめ】勉強のために、各社の社説を収集してデータベース化する

はじめに

企画が終了したので、後々の利便性も考えてまとめておきます。

この投稿は、pythonで各新聞社の社説を楽して収集するために作成したプログラムの作成過程をまとめています。

簡単な仕様要求と設計のまとめから実際に実装する流れを、追うことができます。

具体的な実装はseleniumを利用したブラウザ経由で動的なページ情報を収集する版と、requests・BeautifulSoupdeで静的なページを収集する版の2つを作成しました。

作成中のエラーで躓いた部分なども書いたので、興味がある方は参考にしてみてください。

データベースと銘打っていますが、実際はデータベースへの格納をいつでもできる形にして、格納自体をするようには実装しませんでした。

理由はテキストで保存していたほうが、取り回しがよかったからです。

当初の仕様や設計と最終成果物が異なる部分や、途中で追加が必要になった機能など個人製作ならではの変遷もご覧いただければと思います。

それでは連載一覧をどうぞ!

連載一覧

勉強のために、各社の社説を収集してデータベース化する

久々にプログラムネタで行きます。 データベースを操作する練習と、時事の勉強を兼ねて新聞社の社説を毎日取得してデータベースに格納するプログラムを目指します。 前の連載はどうしたって?、思った以上に難しい ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その2

前回の続きです。管理人の環境にMySQL環境を作るところから始めます。 Windows上にMySQLの環境をインストールする 前回も紹介したこちら(https://prog-8.com/docs/my ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その3

タイトルの通り、続き物です。企画自体についてはこちらをご覧ください。 前回は、WindowsにMySQL環境を作成して終わりました。今回は実際にPythonからデータベースを操作できるかを試します。 ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その4

更新が滞ってましたが、いい加減に進めようと思いまして、本日更新。 企画はこちらをご覧ください。本日は実際に新聞社から社説をぬきだすところまで記載します。なお、webスクレイピングは各社のwebページに ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その5

一連の投稿(勉強のために、各社の社説を収集してデータベース化する)の続きです。 さて、今回は取得したデータを整形する機能について考えていきます。 「スクレイピング 保存」ざっくりと下調べしたところ、C ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その6

今回は前回に構想立てたテキストからDBへの書き込みを行うところを先に作っていきます。 テキストファイルの作成は順番が前後しますが、次回以降に投稿しようと思います。 とりあえずコード f = open( ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その7

連載企画更新。 前回はこちら。 データベースへの格納はいつでもできるので、とりあえず前半の完成版として投稿します。 Xpathの安定性をしばらく見てみたいと思います。 とりあえず社説を抜いてくるsel ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その8

前回からずいぶん間が空きましたが、連載企画です。 さて、この企画立ち上げてそれなり動くプログラムを作り上げて、これまで安定性を確認してきました。 今回はその結果報告です。以下の目次を見ていただくと概要 ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その9

前回の続き。あまりにもseleniumだと動作が遅いので、スクレイピングプログラムをrequestsとBeautifulSoupを利用した版に書き直してみた。 ということで変更記録と考えたことをつらつ ...

続きを見る

勉強のために、各社の社説を収集してデータベース化する その10

長い期間続けてきた本企画ですが、ほぼ形が整ったので、いったん今回で最終回になります。 課題についての対応状況 課題としてあげていた、日付の自動判定は、標準出力に記事の日付を出力させることで手軽にチェッ ...

続きを見る

ここからはシリーズ完結後の追加編です

勉強のために、各社の社説を収集してデータベース化する 追加編その1

認証付きのページを突破できるように改造したのでシリーズに追加。 シリーズ一覧はこちら ログインしてからスクレイピングできるようにする 社説には、あまり関係ないが各新聞社は会員向けの限定ページを配信して ...

続きを見る

プログラム本体

注意ポイント

実際に利用する場合は各新聞社のスクレイピングの可否とwebページ構造をご自身でご確認いただき、ご利用ください。

基本的にどのような新聞社でも本プログラムをベースとして社説を取得できると思います。

前提条件

一番実行しやすかったGoogle colaboratory上のコードを具体的な新聞社を抜いて貼り付けます。

こちらに記載した通り、Google colaboratory上で外部保存の権限を取得するコードをまず実行してください。

 google colaboratory
!pip install -U -q PyDrive
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials
auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)

requests + BeautifulSoupによる実装(静的ページの取得)

実装の説明は連載記事の9回あたりをご覧ください。

 python
import datetime
import requests
from lxml import html
from bs4 import BeautifulSoup
import time
# 各新聞社用のコンストラクタメンテしやすいように
class Shinbunsya:
  def __init__(self, dt_today, target_file, editorial_url, article1_url_xpath,
         article2_url_xpath, article_title_xpath, article_xpath, article_date_xpath,
         absolute_url):
    self.dt_today = dt_today
    self.target_file = target_file
    self.editorial_url = editorial_url
    self.article1_url_xpath = article1_url_xpath
    self.article2_url_xpath = article2_url_xpath
    self.article_title_xpath = article_title_xpath
    self.article_xpath = article_xpath
    self.article_date_xpath = article_date_xpath
    self.absolute_url = absolute_url
  def get_article(self):
    res = requests.get(self.editorial_url)
    soup = BeautifulSoup(res.content, 'lxml')
    dom = html.fromstring(str(soup))
    article1_url = dom.xpath(self.article1_url_xpath)
    if self.target_file == "toaru":
      article1_url = article1_url[0].attrib['href']
    else:
      article1_url = "https:" + self.absolute_url + article1_url[0].attrib['href']
    print(article1_url)
    self.get_detail(article1_url, True)
    article2_url = dom.xpath(self.article2_url_xpath)
    if self.target_file == "toaru":
      article2_url = article2_url[0].attrib['href']
    else:
      article2_url = "https:" + self.absolute_url + article2_url[0].attrib['href']
    print(article2_url)
    self.get_detail(article2_url, False)
  def get_detail(self, detail_url, first_flag):
    # first_flag means first article or second article
    res_detail = requests.get(detail_url)
    soup_detail = BeautifulSoup(res_detail.content, 'lxml')
    dom_detail = html.fromstring(str(soup_detail))
    print(dom_detail.xpath(self.article_date_xpath)[0].text)
    if first_flag:
      t_file = dt_today + "_" + self.target_file + ".csv"
    else:
      t_file = dt_today + "_" + self.target_file + "_2.csv"
    with open(t_file, 'w', encoding='UTF-8') as f:
      f.write(self.dt_today)
      f.write("\n")
      f.write(self.target_file)
      f.write("\n")
      article_title = dom_detail.xpath(self.article_title_xpath)[0].text
      print(article_title)
      f.write(article_title)
      f.write("\n")
      article_data = dom_detail.xpath(self.article_xpath)
      for p_body in article_data:
        print(p_body.text)
        f.write(p_body.text)
        f.write("\n")
    
    upload_file = drive.CreateFile()
    upload_file.SetContentFile(t_file)
    upload_file.Upload()
if __name__ == '__main__':
  dt_today = datetime.date.today().strftime('%Y%m%d')
  toaru = Shinbunsya(
    dt_today,
    "toaru",
    'https://toaru-shinbun.jp/editorial',
    "/html/body/div[4]/div/div[1]/div/div[1]/div[2]/div[2]/ul/li[1]/a",
    "/html/body/div[4]/div/div[1]/div/div[1]/div[2]/div[2]/ul/li[2]/a",
    "/html/body/div[4]/div/main/div[2]/h1",
    "/html/body/div[4]/div/main/div[6]/p",
    "/html/body/div[4]/div/main/div[2]/div/span/time",
    ""
  )
  toaru.get_article()

seleniumによる実装(動的ページの取得)

seleniumをGoogle colaboratory上で使う場合は、第10回を参照して特別なpipをまず実行してください。

その後以下のプログラムを実行します。実装の説明は例によって、連載をご覧ください。

 python
import datetime
from selenium import webdriver #seleniumをインポート
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome import service as fs
import time
def get_toaru(dt_today):
  chrome_service = fs.Service(executable_path='./chromedriver')
  options = Options()
  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-dev-shm-usage')
  driver = webdriver.Chrome('chromedriver',options=options)
  # chromeドライバーを指定
  driver.implicitly_wait(10)
  # 待機時間10秒
  target_file = dt_today + "_toaru.csv"
  target_file2 = dt_today + "_toaru_2.csv"
  f = open(target_file, 'w', encoding='UTF-8')
  f.write(dt_today)
  f.write("\n")
  f.write("toaru")
  f.write("\n")
  f2 = open(target_file2, 'w', encoding='UTF-8')
  f2.write(dt_today)
  f2.write("\n")
  f2.write("toaru")
  f2.write("\n")
  driver.get('https://toaru-shinbun.jp/editorial')
  article_list = driver.find_element(By.XPATH,"/html/body/div[4]/div/div[1]/div/div[1]/div[2]/div[2]")
  latest_url = article_list.find_element(By.XPATH, 'ul/li[1]/a')
  latest_2_url = article_list.find_element(By.XPATH, 'ul/li[2]/a')
  print(latest_url.get_attribute("href"))
  latest_url = latest_url.get_attribute("href")
  print(latest_2_url.get_attribute("href"))
  latest_2_url = latest_2_url.get_attribute("href")
  time.sleep(3)
  #latest_url.click()
  driver.close()
  chrome_service = fs.Service(executable_path='./chromedriver')
  driver = webdriver.Chrome('chromedriver',options=options) # chromeドライバーを指定
  driver.implicitly_wait(10) # 待機時間10秒
  driver.get(latest_url)
  print(driver.find_element(By.XPATH,'/html/body/div[4]/div/main/div[2]/div/span/time').text)
  article_title = driver.find_element(By.XPATH,'/html/body/div[4]/div/main/div[2]/h1')
  f.write(article_title.text)
  f.write("\n")
  #print(article_title.text)
  article_body = driver.find_element(By.XPATH,'/html/body/div[4]/div/main/div[6]')
  articles = article_body.find_elements(By.XPATH, 'p')
  for p_body in articles:
    f.write(p_body.text)
    f.write("\n")
    #print(p_body.text)
  f.close()
  driver.close()
  upload_file = drive.CreateFile()
  upload_file.SetContentFile(target_file)
  upload_file.Upload()
  chrome_service = fs.Service(executable_path='./chromedriver')
  driver = webdriver.Chrome('chromedriver',options=options) # chromeドライバーを指定
  driver.implicitly_wait(10) # 待機時間10秒
  driver.get(latest_2_url)
  print(driver.find_element(By.XPATH, '/html/body/div[4]/div/main/div[2]/div/span/time').text)
  article_title = driver.find_element(By.XPATH, '/html/body/div[4]/div/main/div[2]/h1')
  f2.write(article_title.text)
  f2.write("\n")
  # print(article_title.text)
  article_body = driver.find_element(By.XPATH, '/html/body/div[4]/div/main/div[6]')
  articles = article_body.find_elements(By.XPATH, 'p')
  for p_body in articles:
    f2.write(p_body.text)
    f2.write("\n")
    # print(p_body.text)
  f2.close()
  driver.close()
  upload_file = drive.CreateFile()
  upload_file.SetContentFile(target_file2)
  upload_file.Upload()
  return
if __name__ == '__main__':
  dt_today = datetime.date.today().strftime('%Y%m%d')
  try:
    print("toaru")
    get_toaru(dt_today)
  except Exception as e:
    print(e)

あとがき

今回は各新聞社の社説をスクレイピングするプログラムを作成しました。

意外と新聞のページを毎日回るのが面倒だったので、一気にとってくれるプログラムがほしいなーと思ったことが始まりです。

一応仕様もどきを定めて作り始めたものの、やはり当初予想通りはうまくいかないものですね。これは実際の現場に出ていてよく味わう経験です。

それでも最終的な成果物は思った以上に便利なものになったので満足しています。

職業柄、普段は要求仕様を考えて設計し、実現方式をひねり出すところまで行い、残りは検証をマネジメントする完全な上流工程の場にいるので、自分でプログラムを書いていかないと勘が鈍ります。

何より作ったものが思った通りに動いてくれるのはやはり楽しいですね。(納期と品質考えなくてもいい、、、

今後も身近なできたらいいなを実装に落とし込みつつ、楽しくスキルアップしたいと思います。

Udemyでのオンライン学習も狙います。セール中はさらに狙い目です。ご興味があればどうぞ



-tech
-, ,