ヌルポインター親衛隊

社内でひとりエンジニアやってます。

Python学習14日め(スクレイパー)

本日は、『独学プログラマー』第20章のウェブ・スクレイパー製作を行います。

ウェブ・スクレイパー

WWWに公開されている情報は、その殆どがHTMLです。Hyper Text Markup Language、要はテキストを超えるテキスト。今では、HTMLだけで動画の再生だってできます。
HTMLは、タグを使って記述します。ウェブブラウザは、一定の規則に従ってタグを解釈することによって、ブラウザ上にウェブサイトを表示します。
このHTMLから、必要な情報だけを抜き出すのがウェブ・スクレイパーです。HTMLのタグは、それぞれ特定の意味を持っているので、例えば記事のタイトルだけを抜き出す、といったことが可能です。

スクレイピングの流れ

スクレイピングするにあたって、HTMLを取得する必要があります。HTMLを取得するには、サーバーにhttpリクエストを送り、その返答を得る必要があります。

My PC  --request -> Server
My PC  <-response -- Server

そして、そのレスポンスに含まれるHTMLをパース(構文解析)することで、プログラムで扱いやすい形にします。『独学プログラマー』では、requestとパースを行うモジュールとして、urllibとBeautifulSoupが使われています。

(書き直した)コード

『独学プログラマー』の第1版が発行されたのは2018年2月の終わりです。世の中の移り変わりは早いもので、この記事を書いている2019年の3月暮れにはGoogle Newsサイトマップが変わっているようです。
そこで、本書に掲載されているコード(p243)を少し改良して、2019年3月版のGoogle Newsでも動作するように書き直しました。また、書き直しに当たり、urllibではなく、requestsを使うようにしました。理由は以下の通り。HTTPのモジュールについて調べていたら、requestsというHTTPモジュールのドキュメントを発見しました。そこで、「urllibは人間が使うものではない!(意訳)」と書かれていたからです。

リリース v1.0.4. (Installation)

Requestsは、人が使いやすいように設計されていて、Pythonで書かれている Apache2 Licensed ベースのHTTPライブラリです。 Pythonの標準の urllib2 モジュールは、必要とされるほとんどのHTTPの機能を備えていますが、APIがまともに 使えません 。 様々なウェブ用途に合わせて作られていて、何回も修正されてきました。 簡単なことを行う(メソッドの上書きでさえ)のに、 かなり の量の作業が必要になります。 それはPython的ではないので、そんなに複雑にするべきではありません。 (Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation)

さて、以下が書き直したコードです。

# -*- coding: utf-8 -*-
"""
Created on Tue Mar 19 16:04:02 2019
独学プログラマー 20章
@author: ell_Sub2
"""

import requests
from bs4 import BeautifulSoup

class Scraper:
    def __init__(self, site):
        self.site_route = site
        self.articles = []
        self.articles_title = []
    
    def _parsed(self, site):
        """
        requests.get([url]) -> Get Request Object
        Request.text -> Get HTML Data
        """
        #htmlを取得
        r = requests.get(self.site_route)
        #パースする
        parser = "html.parser"
        sp = BeautifulSoup(r.text, parser)
        
        return sp
        
    def get_articles_url(self):
        sp = self._parsed(self.site_route)
        #URLにarticlesが含まれるリンクのみを取得
        for tag in sp.find_all("a"):
            url = tag.get("href")
            if url is None:
                continue
            if "articles" in url:
                self.articles.append(self.site_route + url)
        print(self.articles)
    
url = "https://news.google.jp/"
Scraper(url).get_articles_url()

おわりに

本当は記事の掲載先からタイトルを取得したいなァ、なんて思って書き直し始めたんですが、articlesのリンク先に行くと、どういうわけかリダイレクトされていて上手く取得できませんでした。requestsのドキュメントをちょっとだけ覗いてみると、どうも勝手にリダイレクトは解決してくれるような感じなんですけどね...。もしかして、リダイレクトとは別の自動遷移なのかしら。知識がなくてキツイ。