山傘のプログラミング勉強日記

プログラミングに関する日記とどうでもよい雑記からなるブログです。

Pythonクローリング&スクレイピング[増補改訂版] ―データ収集・解析のための実践開発ガイドー その9

第4章 実用のためのメソッド

HTTP キャッシュ

Requests を使って Web ページのキャッシュを処理するには、CacheControl を使います。

import requests
from cachecontrol import CacheControl
from cachecontrol.caches import FileCache

session = requests.Session()
cached_session = CacheControl(session, cache=FileCache('.webcache'))

response = cached_session.get('https://yamakasa3.hatenablog.com/')

print(f'from_cache: {response.from_cache}')
print(f'status_code: {response.status_code}')
print(response.text)

第5章 クローリング・スクレイピングの実践とデータの活用

形態要素解析

自然言語処理において日本語は英語と異なり空白スペースによる単語の分割が行われていないので、言語の最小単位に分割するという形態要素解析などを行う必要があります。オープンソースソフトウェアで有名な MeCab を使って形態要素解析を行います。

UbuntuMecab と その IPA 辞書をインストールする必要があります。

sudo apt install -y mecab mecab-ipadic-utf8 libmecab-dev
import MeCab

tagger = MeCab.Tagger()
tagger.parse(' ') # parseToNode() の不具合を回避するため

node = tagger.parseToNode('すもももももももものうち')
while node:
  print(node.surface, node.feature)
  node = node.next

最新の mecab-python3 (0.996.2) では、Wikipedia のデータセットに対してメモリを大量に消費する不具合?があるため、次のバージョンをインストールします。

pip install mecab-python3==0.7

f:id:yamakasa3:20190901022626p:plain

Wikipedia自然言語処理

一部のサイトではデータセットを公開しているので、クローリングをしなくてもデータが手に入るようになっています。

jawiki dump progress on 20190520

PythonWikipedia のデータを扱うためのスクリプトとして、WikiExtractor を使います。

import sys
import logging
from collections import Counter
from pathlib import Path
from typing import List, Iterator, TextIO
import MeCab

tagger = MeCab.Tagger('')
tagger.parse('')

def main():
  """
  コマンドライン引数で指定したファイルを読み込み、頻出単語を出力
  """

  input_dir = Path(sys.argv[1])
  frequency = Counter()

  for path in sorted(input_dir.glob('*/wiki_*')):
    logging.info(f'Processing {path}...')
    with open(path) as file:
      frequency += count_words(file)

  for word, count in frequency.most_common(30):
    print(word, count)


def count_words(file: TextIO) -> Counter:
  """
  WikiExtractorが出力したファイルに含まれる単語の頻度を計算
  """
  frequency = Counter()
  num_docs = 0 # 処理した記事の数
  for content in iter_doc_contents(file):
    words = get_word(content)
    frequency.update(words)
    num_docs += 1

  logging.info(f'Found {len(frequency)} words from {num_docs} documents.')
  return frequency


def iter_doc_contents(file: TextIO) -> Iterator[str]:
  """
  ファイルオブジェクトの <doc ...> 〇〇 </doc> となる 〇〇の部分を抜き出す
  """
  for line in file:
    if line.startswith('<doc '):
      buffer = []
    elif line.startswith('</doc>'):
      content = ''.join(buffer)
      yield content
    else:
      buffer.append(line)


def get_word(content: str) -> List[str]:
  """
  文字列の名詞リストを取得
  """
  words = []
  node = tagger.parseToNode(content)
  while node:
    pos, pos_sub = node.feature.split(',')[:2]
    if pos == '名詞' and pos_sub in ('固有名詞', '一般'):
      words.append(node.surface)
    node = node.next
  
  return words

if __name__ == '__main__':
  logging.basicConfig(level=logging.INFO)
  main()

f:id:yamakasa3:20190902032157p:plain

単語の頻度を見ると、"月" と "日" は日付に紐づいて頻度が高くなっていると推測できます。他の頻出単語は、「時代、昭和、平成、世紀」、「駅、列車、車両、路線、バス」、「世界、東京、アメリカ、ドイツ、都市」、「作品、番組」、「一般、中心、間、主義、他、形」 などのように分類できると思います。

交通機関に関する単語が多いのが意外でした。

感想

形態要素分析という学問的な内容を学びました。題材として Wikipedia が出てきましたが、リンクをネットワークみて影響度の大きいページを分析するということもできるかなと思いました。