scrapyを使用してyahoo知恵袋からデータを収集してみた

Nuco Advent Calendar 5日目の記事です。

今回はDeepLearningの自然言語処理において、会話応答できるものを作成した際の、会話データ収集方法を記事にしました。

#概要
まず、会話応答するものを作ろうとすると、大量の会話データが必要です。
どのくらいのデータが必要かというと最低限100万程度の会話データセットが必要かと思います。
（もちろん多ければ多いほど良）
ただ、会話データって探しても全然ないんですね。
友達とのLINE会話を使おうともしましたが、全然足らず。。。

ということでyahoo知恵袋から「質問」と「ベストアンサー」のセットをクローリングして収集することにしました。
これを一問一答の会話として学習させてみようとなったわけです。

#scrapyを使ってみる
###pipでインストール
```
$ pip install scrapy
```

###プロジェクト作成
```
$ scrapy startproject scrapy(任意のプロジェクト名)
```

そうすると下記のようなフォルダ、ファイルが自動で生成されます。

```
scrapy/
    scrapy.cfg            
    scrapy/             
        __init__.py
        items.py          
        pipelines.py      
        settings.py       
        spiders/          
            __init__.py
```

###抽出するものの定義
items.pyで何のデータを抽出するかを決めます。
私の場合は下記のようになります。

```python:items.py
import scrapy


class ScrapySchItem(scrapy.Item):
    question = scrapy.Field()
    answer = scrapy.Field()
    url = scrapy.Field()
```

###スパイダーの作成
スパイダーを作成します。
scrapy genspider [name] [サイトのdomein]

```
$ scrapy genspider yahoo chiebukuro.yahoo.co.jp
```

ファイルが生成されます。

```python:scrapy/spiders/yahoo.py
# -*- coding: utf-8 -*-
import scrapy

class YahooSpider(scrapy.Spider):
    name = "yahoo"   
    allowed_domains = ["chiebukuro.yahoo.co.jp"] 
    start_urls = (
        'https://chiebukuro.yahoo.co.jp/'
    )

    def parse(self, response): 
        pass
```
これを編集していきます。

```python:scrapy/spiders/yahoo.py
# -*- coding: utf-8 -*-
import scrapy
import logging
from scrapy_sch.items import ScrapySchItem


class YahooSpider(scrapy.Spider):
    name = 'yahoo'
    allowed_domains = ['chiebukuro.yahoo.co.jp']
    start_urls = ['https://chiebukuro.yahoo.co.jp/']

    def parse(self, response):
        domein = "https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/" # yahoo知恵袋のトップ画面ではなく、質問、回答があるページの共通URL
        for url in response.css("a::attr('href')").extract():
            if not url.startswith('https://'):
                continue
            if domein not in url:
                yield scrapy.Request(url, callback=self.parse)
            yield scrapy.Request(url, callback=self.parse_dir_contents)

    
    def parse_dir_contents(self, response): # response に、ウェブサイトの情報が入っている
        items = ScrapySchItem() # items.pyで指定したクラス
        items["question"] = ''.join(response.css('div.sttsRslvd div.ptsQes p::text').extract()) # responseのHTMLを辿って欲しい情報を取得する
        items["answer"] = ''.join(response.css('div.mdPstdBA div.ptsQes p::text').extract())
        items['url'] = response.url
        return items
    
```

#pipelines.pyを編集
yahoo.pyで取得したデータを整え、conversation.txtファイルに書き出します。
今見返すとかなり冗長ですね。

```python:piplines.py
import logging

class ScrapySchPipeline(object):
    def process_item(self, item, spider): #itemに値が入ってます。
        if item['answer'] is None or item['answer'] == '':
            return item
        with open("conversation.txt", "a") as f:
            question = ''.join(item['question'].split('\r\n'))
            question = ''.join(question.split('\n'))
            question = question.replace('　', '')
            question = question.replace(' ', '')
            question = question.replace('\t', '')

            answer = ''.join(item['answer'].split('\r\n'))
            answer = answer.replace('　', '')
            answer = answer.replace(' ', '')
            answer = answer.replace('\n', '')
            answer = answer.replace('\t', '')
            logging.debug('hoge %s', answer)
            f.write(question)
            f.write('\n')
            f.write(answer)
            f.write('\n')
            f.write('\n')

            f.close
        return item
```

#実行
実行してみましょう！

```
$ scrapy crawl YahooSpider
```

conversation.txtを見てみましょう。
＝＝＝
質問
回答
改行
＝＝＝
で1セットとなってます。

```conversation.txt
新潟ジャンプステークス2017に登録しているグッドスカイには、誰が騎乗する予定なのでしょうか。
もりかずまきしゅ。

早速新潟2R、⑮クルクルミラクルの複勝的中しました！武藤は怖いですね(^-^;
馬の仕上がりも良かったですね(ФωФ)ヒモには入れましたが…(꒦໊ྀʚ꒦໊ི)

横山義行はもう復帰は厳しいでしょうか？かれこれ3～4年は騎乗してません。
たまに気になって調べますが音沙汰なし…厳しいでしょうねただ引退したところで何もないし、少ない可能性に賭けるしかないでしょうね…

今日の藤田の勝った後の、戸崎どうですか？教育上とか世知辛いこと言わずに平気でやるのがJRAの太っ腹ですよね！もちろん取りました。Histoirfemme(六枠九番)
その通りですね。JRAは、太っ腹ですね。

トラジさんにリクエストでドスル！アトサキ勝者プレゼント貴方の中での最強馬は何ですか？（日本の馬限定で）私の中での最強馬はシンボリルドルフです良く言われているディープやオルフェは2004年から移住した関係で競馬から離れていたのでタイムリーにレースを見ていませんし生でレースを見たことが無いので評価する事ができないのですこの質問は、さんに回答をリクエストしました。
強いのはディープ二度と現れないのがオルフェのような気がします。=(^.^)=

今週の武豊はどうでしょうか？先週は惨々な結果でしたが。
今回は人気薄の馬ばかりですね。３着以内→３頭で１勝出来ればいいと思います。m(._.)m

例のリクエスト（100％忘れてた甲子園分）リーディング・ジョッキー争い（200勝は微妙）3名が170～180勝のライン辺りでしょうか①戸崎圭太114－81－56－328勝率.197連対率.3373連複率.434②デムーロ104－66－58－202勝率.242連対率.3953連複率.530③ルメール99－81－68－245勝率.201連対率.3653連複率.503大嫌いな戸崎GⅢ4勝のみですねん(笑)デムーロGⅠ2勝GⅡ5勝GⅢ3勝ルメールGⅠ3勝GⅡ3勝GⅢ2勝外国人に捲ってほしい気分･･･この質問は、さんに回答をリクエストしました。
リクエストどうもです。リーディングジョッキー争い極予想１位→戸崎２位→ルメール３位→ミルコミルコはルメールに抜かれると思います。メイン以外の戸崎は強すぎます。(^^;

松田大作はもう燃え尽きてしまいましたか？一時期の勢いが完全に無くなった感があります。
一般企業なら懲戒解雇処分だよね。馬主さんは皆さん企業の代表者だったりするから、企業倫理を重んじる馬主さん主導では騎乗依頼は難しい。ファンも各馬主さん＆各陣営も呆れてるから、メインだった西山氏(ニシノ)がどの程度手を差し伸べてくれるか？、、燃え尽き以前に、まず再点火しないとね。

実はこっそりというかなんというか札幌記念の123.13のワイドボックスだけ参考にさせていただきました！ありがとうございます！来週の競馬も予想お願いします！この質問は、さんに回答をリクエストしました。
お疲れ様てす、リクエストありがとうございます。ワイド的中、おめでとうございました。(^^)予想はしますが…来週はハズレますよ（笑）来週の競馬もお互いに頑張りましょう〜！(__).｡o○

本日の競馬成績は？
撃沈でした。。_(:3」∠)_
```

上記のようなもので200万行程書き込むことができました。

使えるのかコレ。。。

#感想
結果的には長すぎる文章も多々あり、うまく学習はできませんでしたが、良い経験になりました。

良い日本語会話データセットどこかにないかな。。。

以上、ありがとうございました。