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

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

ただいまの
回答率

88.59%

Python, Scrapyによる抽出結果CSVの行列を整列させたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,514

fukazume

score 75

■質問

複数URLからの抽出結果を出力したCSVファイルの行列が、抽出する度に変化して不安定です。出力前にこの行列を安定させる(=整列させる)ことは可能でしょうか?

※なお各行のレベルでデータの整合性は問題ないです。
※以下イメージをご参照ください。

【CSV出力結果イメージ】
1回目
|   date   |   time   |   name    | 
| page2のA1 | page2のB1 | page2のC1 |
| page2のA2 | page2のB2 | page2のC2 |
| page2のA3 | page2のB3 | page2のC3 |
| page1のA1 | page1のB1 | page1のC1 |
| page1のA2 | page1のB2 | page1のC2 |
| page1のA3 | page1のB3 | page1のC3 |

2回目
|   name   |   date   |   time    | 
| page1のC1 | page1のA1 | page1のB1 |
| page1のC2 | page1のA2 | page1のB2 |
| page1のC3 | page1のA3 | page1のB3 |
| page2のC1 | page2のA1 | page2のB1 |
| page2のC2 | page2のA2 | page2のB2 |
| page2のC3 | page2のA3 | page2のB3 |

$ scrapy crawl example_spider -o test01.csv
class ExampleSpider(scrapy.Spider):
    name = 'example_spider'
    allowed_domains = ['example.com']
    start_urls = [
        'https://example.com/page1.html',
        'https://example.com/page2.html'
    ]

    def start_requests(self):
        for url in self.start_urls:
            yield SplashRequest(url, self.parse,
                args={'wait': 0.5},
            )
    def parse(self, response):      
        table_rows = response.xpath('//table/tbody/tr')
        for table_row in table_rows:
            item = TutorialItem()

            item['date'] = table_row.xpath('td[3]/a/text()').extract_first()
            item['time'] = table_row.xpath('td[4]/a/text()').extract_first()
            item['name'] = table_row.xpath('td[5]/a/text()').extract_first()
            yield item
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

setting.pyに

FEED_EXPORTERS = {
    'csv': 'your.custom.Exporter'
}


と設定を書くと
your.custom.Exporter を利用してcsv出力できるようになりますので、
公式リファレンス を参考に自分でExporterを書くことで自由に挙動は変えられます。

例えば

from scrapy.exporters import CsvItemExporter


class Exporter(CsvItemExporter):
    def __init__(self, file, include_headers_line=True, join_multivalued=',', **kwargs):
        super().__init__(file, include_headers_line, join_multivalued)

    def start_exporting(self):
        self.items = []

    def export_item(self, item):
        self.items.append(item)

    def finish_exporting(self):
        # ここでself.itemsをソートする
        # たとえば self.items.sort(key=lambda x: x["name"])

        if self.include_headers_line:
            values = [x for x in self.items[0]]
            self.csv_writer.writerow(values)

        for item in self.items:
            values = [item[x] for x in item]
            self.csv_writer.writerow(values)


このように。

これは特にsort等はしていませんが
finish_exporting
にてitemsをsortしてさらにはitemをキーでsortすることで
出力を整形できるかと思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/11/15 17:14 編集

    >コードに一部ミス
    いえいえ、とんでもございません。とても有益なアドバイスでした。

    >ソートの方法は https://nwpct1.hatenablog.com/entry/python-lambda などが参考に
    拝読した上で大変恥ずかしいのですが、ご提示頂いたソースコードのfinish_exporting部に具体的にどうこのlambda方式で記述するのか、理解に時間がかかりそうです。finish_exporting内のコードの各所にself.items.sort(key=lambda x: x["name"])を当てはめたりしていじくり倒しましたが、都度エラーが発生し期待したレコードをソートできませんでした。

    もし可能であれば恐縮ですが、上記のコードで、例えば、"date", "time", の特定列だけを選択して出力するサンプルコードなどヒントを頂けるとありがたいのですが。

    >spider側でpipelineにoutputで指定した値を渡すようにするなど、使用感としては異なるものになる
    わかりやすいご解説ありがとうございます。理解できました!

    キャンセル

  • 2018/11/16 08:30

    エラーが出る際はエラーメッセージ等を記載いただかないと何が起こっているのかはわかりかねますね。

    特定列のみの出力は
    if 内の1行を values = ["date", "time"] に変更し
    for 内の1行を values = [item["date"], item["time"]] に変更すると
    "date"と"time"
    にすれば"date"と"time"の列だけ出力するようにできるかと思います。

    キャンセル

  • 2018/11/16 12:14

    エラーメッセージの件、様々試して都度エラーになるので、コメント欄の限られたスペースに中途半端に書くと断片的になり返って手がかりどころか混乱を招くと思ったので詳細を記載いたしませんでした。主に基本文法を知らない私の凡ミスによるエラーです。申し訳ございませんでした。

    特定列の出力の件、正常に動作しました!大変、参考になりました。日にちを跨いできめ細やかにお教えくださいまして誠にありがとうございました!

    キャンセル

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

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

関連した質問

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