質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.80%

scrapyを使用したスクレイピングのデータ整形

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 183

kajimaru315

score 22

前提・実現したいこと

scrapyを利用して下記の階層構造のWebページをクローリング、スクレイピングしています。

hoge.com/results/2020-08-01
┣ /AAA.html
┣ /BBB.html

AAA,BBB..の各ページは成績表になっており、

  • タイトル(文字列)
  • 実施日(文字列)
  • 順位(リスト)
  • 順位ごとの名前や年齢(リスト)

のように文字列とリストで取得をおこなっているため、CSVでは

Title Date Rank Name
AAA 2020-08-01 1,2,3,4 A,B,C,D
BBB 2020-08-01 1,2,3,4 A,B,C,D

上記のようにURLごとにレコードとして出力されます。

このテーブルを下記のように配列をカラムとして
列に並ぶように出力したいのですが対応方法がわからず、、
有識者の方にご助言を頂けますと幸いでございます。

Title Date Rank Name
AAA 2020-08-01 1 A
AAA 2020-08-01 2 B
AAA 2020-08-01 3 C
AAA 2020-08-01 4 D
BBB 2020-08-01 1 A
BBB 2020-08-01 2 B
BBB 2020-08-01 3 C
BBB 2020-08-01 4 D

該当のソースコード

class RankSpider(scrapy.Spider):
    name = 'rank'
    allowed_domains = ['hoge.com']
    start_urls = ['http://hoge.com/results/2020-08-01']

    def parse(self, response):
         for url in response.css('a[class="XXX"]::attr("href")').extract():
             yield scrapy.Request(response.urljoin(url), self.parse_item)

    def parse_item(self, response):
        item = RankingItem()
        item['Title'] = response.css('h1::text').get()
        item['Date'] = response.css('div.date::text').get()
        item['Rank'] = response.css('li h3.number::text').getall()
        item['Name'] = response.css('li h4.name::text').getall()
        yield item
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

CSV部分の分割でしたらこれでいけます。

import pandas as pd
from io import StringIO

s = '''"Title","Date","Rank","Name"
"AAA","2020-08-01","1,2,3,4","A,B,C,D"
"BBB","2020-08-01","1,2,3,4","A,B,C,D"'''
df = pd.read_csv(StringIO(s))

df = (df.set_index(['Title', 'Date'])
    .stack()
    .str.split(',', expand=True)
    .stack()
    .unstack(-2)
    .reset_index(-1, drop=True)
    .reset_index()
)

print(df)

'''
  Title        Date Rank Name
0   AAA  2020-08-01    1    A
1   AAA  2020-08-01    2    B
2   AAA  2020-08-01    3    C
3   AAA  2020-08-01    4    D
4   BBB  2020-08-01    1    A
5   BBB  2020-08-01    2    B
6   BBB  2020-08-01    3    C
7   BBB  2020-08-01    4    D
'''

追記
動作確認はできてないのですが、scrapy.Item継承クラスと辞書型は相互変換ができるらしいので、
それを利用してpandasを利用するのが楽だと思います。

import pandas as pd

rs = RankSpider()

#scrapy.Item継承クラスを辞書型に変換
drs = dict(rs)
#辞書型をpandas.DataFrameに変換
df = pd.DataFrame.from_dict(drs, orient='index').T
#pandas.DataFrameを辞書型に変換
drs = df.to_dict()
#辞書型をscrapy.Item継承クラスに変換
rs = RankSpider(drs)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/18 13:16

    yureighostさん

    ご回答ありがとうございます!
    pandsを使用しての処理があるんですね。

    無知で申し訳ないのですが、こちらは
    スクレイピング用のスパイダーとは別にpands用のファイルを作成して
    実行するのでしょうか?

    1.スクレイピングスパイダーで情報取得

    2.取得した情報を別のpyファイルにインポート

    3.インポートしたファイルを整形

    4.CSVに生成

    このようなイメージですか?

    キャンセル

  • 2020/08/18 14:34

    yureighostさん
    ありがとうございます。
    頂いた内容で一度試してみます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る