Webクロール」タグアーカイブ

Python3.6.2をインストールして、Wikipediaをクロールするスクリプトを書く

前提

本記事は以下の環境で動作確認済み

要素 バージョン
Debian 9.1
pyenv 1.1.3-7
python 3.6.2

概要

Pythonを使ったことのない私が、pyenvを用いて現在の最新版であるPython3.6.2をインストールし、Wikipediaの特定ページからランダムにリンクを辿り続けるスクリプトを書いたお話。

Python3.6.2のインストール

pyenvについて

pyenvは、複数のPythonのバージョンを容易にインストール、切り替えを行うツール。Rubyのrbenvや、nodeのnvm,nなどと同じようなもの。

pyenvのインストール

以下ページをそのまま参考にした。
PyenvによるPython3.x環境構築(CentOS, Ubuntu) – Qiita

3.6.2のインストール

前述のページだと若干古いPythonを使っているので、pyenvを用いて以下のように3.6.2をインストール

$ pyenv install -v 3.6.2
$ pyenv global 3.6.2

確認

$ python --version
Python 3.6.2

Python3入門

初Pythonだったのでまずは基本的な使い方を確認。以下のページが広く浅くしっかり丁寧だったので大変お世話になった。
Python3基礎文法 – Qiita

Wikipediaをクロールするスクリプト作成

概要

Pythonの基本を学んだので、手始めに、以下のような簡単なWebクロールスクリプトを書いてみる。

  • 特定のWikipediaのページを開始地点とする
  • 記事内の、他の単語へのリンク一覧を取得する
  • ノイズとなるリンクを排除し、関連する単語へのリンクのみを取り出す
  • 残ったリンク一覧から、ランダムに1件選出し、そのリンク先へ移動する
  • リンク先のページタイトルを標準出力し、同様の操作を繰り返す

Wikipediaを使った連想ゲームのような物?

使用ライブラリ

調べてみると、PythonにはWebクロールのためのフレームワーク/ライブラリも充実してるようだが、今回はPythonのお試しも含んでいるので、HTTPにrequests、HTML/XMLパーサにBeautifulSoupというライブラリを用いることにする。

実装

そんなに長くないので先に全文

BASEURL = 'https://ja.wikipedia.org'

# 指定したWikipediaページ内のaタグからランダムで一つ戻す
def get_wiki_a_tag(url):
  response = requests.get(url)
  soup = BeautifulSoup(response.text, 'lxml')
  print(soup.find('h1').text)
  a_tags = filter(
    lambda a: 'href' in a.attrs and
               a.text and
               a.attrs['href'].startswith('/wiki') and
               a.attrs['href'].find('Wikipedia') and
               a.text.find('年') == -1,
    soup.select('.mw-parser-output p a')
  )
  a_tags_list = list(a_tags)
  if not len(a_tags_list):
    print("end")
    sys.exit()
  return random.sample(a_tags_list, 1)[0]

# 開始地点を設定
word  = 'Python'
url   = f"{BASEURL}/wiki/{word}"
# ランダムにWikipediaのリンクを飛び続ける
while True:
  a_tag = get_wiki_a_tag(url)
  url  = f"{BASEURL}{a_tag.attrs['href']}"
  time.sleep(1)

まず、以下の部分で特定URLのHTTPレスポンスを取得する。BeautifulSoupを用いることで、レスポンスのXML/HTMLをparseできるようにする。

response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')

関連する単語へのリンクは概ねdiv.mw-parser-output配下のp要素に含まれているので、その中のaタグの一覧を取得する。あくまで概ねなので正確ではない。

soup.select('.mw-parser-output p a')

ノイズとなるリンクはpythonのfilter関数で排除する。排除対象は以下の通り

  • href属性が設定されてないaタグ
  • テキストが設定されていないaタグ
  • リンク先が/wiki で始まらないaタグ
  • リンク先にWikipediaが含まれるaタグ(要出典、検証可能性など)
  • テキストに「年」が含まれるaタグ(‘2010年’などの記事が頻出するため)
lambda a: 'href' in a.attrs and
           a.text and
           a.attrs['href'].startswith('/wiki') and
           a.attrs['href'].find('Wikipedia') and
           a.text.find('年') == -1,

randomパッケージのsample関数を用いて、aタグの一覧からランダムに一つ戻す。RubyだとArrayにsampleメソッドがあるのでちょっと不便だと思った。

return random.sample(a_tags_list, 1)[0]

動作確認

Pythonスタートでやってみたところ、初めのうちは良い感じだったが、トレインチャンネルあたりからおかしくなった。

もう一度。Googleから何故サウナ風呂につながるのか‥‥。
※確認した所、社内にサウナがあるらしい

試しに開始地点を阿部寛にしたらやたらとグローバリゼーションになった。阿部寛だしそんなものか。

所感

  • とりあえず何かを作ることを通じてPythonに世界に飛び込んでみたが、まだまだ魅力は掴めてない。インデントブロックがPythonの特徴の一つだが、これもまだ見やすいとは思えない(インデント幅を4字にしたらまた違うかも)
  • Rubyに慣れてて、Rubyが一番好きな分、Pythonとはウマが合わない可能性もあるが、もっと使ってみて自分に合うとこを見つけていきたい。
  • 今回作ったスクリプト、ずっと眺めてられそう。