毎日テキストマイニング

180日間、毎日テキストマイニングをするブログです

2018/8/2【40日目】AKBの歌詞をまずはタイトルをスクレイピングする

著作権のこともわかりましたので、スクレイピングをしていきます。

対象のURLはこちらです。

http://artists.utamap.com/fasearchkasi.php?artistfid=1003855_1

さっそく新しいプロジェクトを作成して、どんどんやっていきましょう。

$ scrapy startproject akb_songs
$ cd akb_songs

スクレイピングを開始する前に

スクレイピングを開始する前に、スクレイピングをする速度を設定します。

昨日の著作権の話の中で、相手のサーバーに負荷をかけないようにする必要があることがわかりましたが、 この設定は[settings.py]でできるようです。

相手のサーバーに負荷をかけるような行為は、偽計業務妨害又は電子計算機損壊等業務妨害に該当する可能性があります。 https://www.bengo4.com/internet/b_608252/

[settings.py]を開いて、DOWNLOAD_DELAY = 2を設定しています。スクレイピングの速度を1回ごとに2秒あけるように設定します。普通は1秒でいいらしいですけど、念のため。

DOWNLOAD_DELAY = 2

ページ全体を読み込む

とりあえずチュートリアルで勉強したコードのURLとClass名、name名だけを修正してサンプルの通りに実行してみます。

import scrapy

class AKB_Spider(scrapy.Spider):
    name = "akb"

    def start_requests(self):
        urls = [
            'http://artists.utamap.com/fasearchkasi.php?artistfid=1003855_1',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

実行。

$ scrapy crawl akb

とりあえず実行はできました。

シェルの実行

ひとまず、どういった構成にするかシェルを実行して確認していきます。

$ scrapy crawl akb
$ scrapy shell 'http://artists.utamap.com/fasearchkasi.php?artistfid=1003855_1'

まずは、Hello World的にタイトルをだしてみます。

In [2]: response.css('title')
Out[2]: [<Selector xpath='descendant-or-self::title' data='<title>うたまっぷ歌詞検索/歌手:AKB48/歌詞一覧</title>'>]

ひとまず、スクレイピングできているようです。

スクレイピングができているようなので、Google chomeで検証してサイトの構成を確認します。全体的にtableでできており、TD class=〇〇の中にもろもろと情報が入っています(class名はぼかしてます)。

tableからのデータの取得の書き方は以下のサイトを参考にしました。

How to scrape HTML table with Scrapy

In [16]: table = response.xpath('//*[@class="〇〇"]//a').extract()
In [17]: table

Out[17]: 
['<a href="http://www.utamap.com/showkasi.php?surl=k-180530-084">Teacher Teacher</a>',
 '<a href="http://www.utamap.com/showkasi.php?surl=k-180314-330">ジャーバージャ</a>',
 '<a href="http://www.utamap.com/showkasi.php?surl=k-180124-252">クサイモノだらけ</a>',
 '<a href="http://www.utamap.com/showkasi.php?surl=k-180124-251">キスキャンペーン</a>',
 '<a href="http://www.utamap.com/showkasi.php?surl=k-180124-250">天国の隠れ家</a>',
(以下、省略)

response.xpathのところに出てきたxpathとは、wikiによると場所を指定するためだけの言語のようです。

XML Path Language (XPath(エックスパス)) は、マークアップ言語 XML に準拠した文書の特定の部分を指定する言語構文である。

書き方としましては、//で区切って場所を指定するそうです。今回の場合は、class="〇〇”属性の中の(//)aタグを集める、という意味らしいです。後ろに付いているextractメソッドは文字を摘出するメソッドだそうです。extractメソッドがないとこんな感じになります。

In [38]: table = response.xpath('//*[@class="〇〇"]//a')
In [39]: table

Out[39]: 
[<Selector xpath='//*[@class="ct140"]//a' data='<a href="http://www.utamap.com/showkasi.'>,
 <Selector xpath='//*[@class="ct140"]//a' data='<a href="http://www.utamap.com/showkasi.'>,
 <Selector xpath='//*[@class="ct140"]//a' data='<a href="http://www.utamap.com/showkasi.'>,

それぞれの歌詞のURLがわかりましたので、hrefを取得したいと思います。hrefの指定の仕方は//aの後ろに/@hrefとするだけです。xpath便利ですね。

In [33]: table = response.xpath('//*[@class="〇〇"]//a/@href').extract()
In [34]: table

Out[34]: 
['http://www.utamap.com/showkasi.php?surl=k-180530-084',
 'http://www.utamap.com/showkasi.php?surl=k-180314-330',
 'http://www.utamap.com/showkasi.php?surl=k-180124-252',

とりあえず各歌詞のURLは取得完了です。

タイトルを出してみる

URLが取得できましたので、38日目にやったサンプルに適用させてみましょう。

dailytextmining.hatenablog.com

import scrapy

class AKB_Spider(scrapy.Spider):
    name = "akb"
    start_urls = [
        'http://artists.utamap.com/fasearchkasi.php?artistfid=1003855_1',
    ]

    def parse(self, response):
        for title in response.css('title'): #response.css('div.quote'):
            yield {
                'title': title.css('title').extract(),
            }

        next_pages = response.xpath('//*[@class="〇〇"]//a/@href').extract() #response.css('li.next a::attr(href)').extract_first()
        for next_page in next_pages:
            if next_page is not None:
                next_page = response.urljoin(next_page)
                yield scrapy.Request(next_page, callback=self.parse)

nextpageをfor文にして、1つ1つのURLを読み込ませています。 まだページ移行後のページ構成を確認していませんので、yieldにはタイトルだけを指定しています。

これで実行してみます。データの保存形式はjsonです。

$ scrapy crawl akb -o quotes.json

実行結果。

[
{"title": "<title>\u3046\u305f\u307e\u3063\u3077\u6b4c\u8a5e\u691c\u7d22/\u6b4c\u624b:AKB48/\u6b4c\u8a5e\u4e00\u89a7</title>"}
{"title": ["<title>\u3046\u305f\u307e\u3063\u3077\u6b4c\u8a5e\u691c\u7d22/\u6b4c\u624b:AKB48/\u6b4c\u8a5e\u4e00\u89a7</title>"]}
{"title": ["<title>\u3046\u305f\u307e\u3063\u3077\u6b4c\u8a5e\u691c\u7d22/\u6b4c\u624b:AKB48/\u6b4c\u8a5e\u4e00\u89a7</title>"]}

む、ユニコードが日本語に設定されていないですね。 文字の設定は、[setting.py]で設定できるようです。

FEED_EXPORT_ENCODING='utf-8'

改めて、実行結果です。

[
{"title": ["<title>うたまっぷ歌詞検索/歌手:AKB48/歌詞一覧</title>"]},
{"title": ["<title>ランナーズハイ AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
{"title": ["<title>シュートサイン AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
{"title": ["<title>願いごとの持ち腐れ AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
{"title": ["<title>あの頃の五百円玉 AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
{"title": ["<title>イマパラ AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
{"title": ["<title>前触れ AKB48 歌詞情報 - うたまっぷ 歌詞無料検索</title>"]},
(以下、省略)

各サイトに回れているようです。4時近くになってしまいましたので、とりあえず今日はここまでにします。

今日の結果

今日のAKBメンバーによる呟きは82件でした。 ランクインコンサート、感謝祭、誕生祭、総選挙感謝祭、結構呼び方がばらけていますが、大きいイベントがあったようです。流石に感謝祭というだけあって「ありがとう」という言葉がかなり目立ちますね。 f:id:rimt:20180803040642p:plain

今日勉強したこと

  • xpath
  • scrapyの設定の仕方
  • extractメソッド