59667


当方初心者であり、申し訳ないのですが皆様のお知恵を拝借したいです。

いろいろ検索したのですが、良い方法（どこから手をつけるべきか）がわからず、  
困っております。アドバイスいただけますと幸いです。

ページをスクレイピングして、CSVに下記のようなデータを書き出すプログラムを作りました。

[code]

    a,b,d,x,テスト 
    3354,test,あいうえ,いろはに,12345
    
[/code]

この時、複数のページをスクレイピングして同様の情報を取得するプログラムを実行し、  
行を追加して値を記載していくプログラムを作りたいと考えています。

ただ、ページ毎にカラム名が同じ場合もあれば、異なる場合もあります。

もし、スクレイピングした2ページ目がこのような値の場合は、

[code]

    a,b,d,y,テスト 
    3399,TEST,あいうえ,ほへと,12345
    
[/code]

CSVに書き出す場合は下記のように記載したいと考えています。

[code]

    a,b,d,x,テスト,y 
    3354,test,あいうえ,いろはに,12345
    3399,TEST,あいうえ,,12345,ほへと
    
[/code]

※基本的には順に行追加で値が記載されていき、  
新しい項目の場合は一番右側に列が追加されそこに記載されていく、、という流れです。

複数ページに順にアクセスして、それぞれのページの情報を取得するコードはできたのですが、各項目に希望通りにCSVに書き出す部分が不明です。

この場合、どのようなプログラムが適切でしょうか？  
何か参考になる具体的なコード、サイトなどありましたら、  
教えて頂けませんでしょうか。。。。。

【追記】  
質問を頂きありがとうございます。  
カラムにある記載を事前に予想できないことがネックとなります。

取得する情報は1ページ毎に商品のスペック情報のようなものです。  
Aという商品にはa,b,c,dというスペックの項目があり（上記で言うカラムにある記載）、  
それぞれに対して、あ,い,う,えという値が取得できます。

Bという商品を取得すると、Aという商品と同じスペック項目はあるケースもありますが（ただし値は異なる）、Bにしかないスペック項目もあります。

毎度取得する情報はスペック項目と値のセットとなりますので、

[code]

    a,b,d,x,テスト,y 
    3354,test,あいうえ,いろはに,12345
    3399,TEST,あいうえ,,12345,ほへと
    
[/code]

取得する毎に上記のように行が追加され、且つ以前取得したスペック項目が無い場合は、  
新しく追加されていくことが理想です。

【追記2】  
現在のコードは下記のようにしておりまして、  
ページを読み込む→データを取得→CSVに書き込むというのをfor文で作成しています。

[code]

    for num in range(1,xxx):
        urlclick = driver.find_element_by_xpath('xxxxxxxxxxx'.format(num))
        urlclick.click()
        driver.window_handles
        driver.switch_to.window(driver.window_handles[1])
        currentURL = driver.current_url
        html = urlopen(currentURL)
        bsObj = BeautifulSoup(html, "html.parser")
        table = bsObj.findAll("table", {"class":"xxxxxxxxxxx'"})[0]
        rows = table.findAll("tr")
        data = []
        for row in rows:
            csvRow = []
            for cell in row.findAll(['td', 'th']):
                csvRow.append(cell.get_text(strip=True))
            data.append(csvRow)
    
        col1 = [a[:2] for a in data]
        col2 = [a[2:] for a in data]
    
        col1.extend(col2)
    
        row1 = [a[:1] for a in col1]
        row2 = [a[1:] for a in col1]
    
        row1 = sum(row1, [])
        row2 = sum(row2, [])
    
        array2d = [row1, row2]
    
[/code]


CC BY-SA 4.0
2019-10-12T10:56:00.847
59664
2019-10-13T07:00:45.283
2019-10-13T04:50:07.133
23420
23420
question
python csv web-scraping beautifulsoup
Python スクレイピング　同じ項目は同じ列に記載、異なる項目は新しく列を追加して記載する方法


**質問追記2のソースを基に追記**

ループ中の最後の `array2d = [row1, row2]`
の行を、以下どちらかの処理を呼び出しに変えて、ループ前後に対応した処理およびCSVファイル作成を行えば良いでしょう。

**`pandas`使用案**  
ループ前：pandas importとリスト用編集初期化

[code]

    import pandas as pd
    result = []
    
[/code]

ループ中：辞書化とリスト追加

[code]

    dic = dict(zip(row1, row2))
    result.append(dic)
    
[/code]

ループ後：DataFrame化とCSV作成

[code]

    df = pd.io.json.json_normalize(result)
    df = df.fillna('')
    
    df.to_csv('data.csv', header=True, index=False)
    
[/code]

**素のPythonで処理する案**  
ループ前：csv importとリスト用編集初期化、関数定義

[code]

    import csv
    refkeys = []
    refkeyslen = len(refkeys)
    result = []
    
    def accumulate(newkeys, newvalues):
    # 以下省略
    
[/code]

ループ中：関数呼び出し

[code]

    accumulate(row1, row2)
    
[/code]

ループ後：CSV作成

[code]

    with open('data.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(refkeys)
        writer.writerows(result)
    
[/code]

* * *

**以下は質問追記前の詳細**

結果がこの形では無くて、データの少ない1行目の最後にもカンマが付いても良いなら、以下の様な処理で出来るでしょう。

質問での希望形(データが少ない時でも後ろにカンマを補完しない)

[code]

    a,b,d,x,テスト,y 
    3354,test,あいうえ,いろはに,12345
    3399,TEST,あいうえ,,12345,ほへと
    
[/code]

全ての行は同じ数の列を持っているものとする(データが少ない時は後ろにカンマを補完)

[code]

    a,b,d,x,テスト,y 
    3354,test,あいうえ,いろはに,12345,
    3399,TEST,あいうえ,,12345,ほへと
    
[/code]

スクレイピングの結果を1件毎にJSON(辞書)の形にして、それをリストに蓄積しておきます。  
`pandas`の`json_normalize()`でデータフレームにして、欠落値部分のNaNを`fillna()`で空文字列に変換してから、`to_csv()`でファイルに書き出す形です。

[code]

    import pandas as pd
    
    # 質問のデータを基に、以下の様なJSON(辞書)のリストが出来たものとする
    result = [{"a":"3354","b":"test","d":"あいうえ","x":"いろはに","テスト":"12345"},
              {"a":"3399","b":"TEST","d":"あいうえ","y":"ほへと","テスト":"12345"}]
    
    df = pd.io.json.json_normalize(result)
    df = df.fillna('')
    
    df.to_csv('data.csv', header=True, index=False)
    
[/code]

結果は上記2つ目のCSVデータになります。

参考記事  
[pandasのjson_normalizeで辞書のリストをDataFrameに変換](https://note.nkmk.me/python-
pandas-json-normalize/)  
[pandasで欠損値NaNを除外（削除）・置換（穴埋め）・抽出](https://note.nkmk.me/python-pandas-nan-
dropna-fillna/)  
[pandasでcsvファイルの書き出し・追記（to_csv）](https://note.nkmk.me/python-pandas-to-csv/)

* * *

ちなみにJSON(辞書)のリストを作るのは、以下の様な手順とかが考えられます。

[code]

    result = []    # リストの初期化
    
    # 1件のスクレイピング結果を2次元リストの形で作成したものとする
    data = [["a","b","d","x","テスト"],
            ["3354","test","あいうえ","いろはに","12345"]]
    
    # 2次元リストの各行を取り出して key, value とし、辞書化する
    # スクレイピング結果を直接以下の変数に格納しても良い
    keys = data[0]
    values = data[1]
    dic = dict(zip(keys, values))
    result.append(dic)  # リストに追加
    
    # 2件目も同様に書いたが、ループで回せば良いはず。
    data = [["a","b","d","y","テスト"],
            ["3399","TEST","あいうえ","ほへと","12345"]]
    
    keys = data[0]
    values = data[1]
    dic = dict(zip(keys, values))
    result.append(dic)
    
[/code]

参考記事  
[Python:
キーと値のリストから辞書を作成する](https://qiita.com/aoksh/items/aaf0850898a25a43cb6f)

* * *

**追記**  
`pandas`ではなく、素のPythonで処理できないか考えてみました。  
以下のように`header`部分と`data`部分を分けて処理すれば良さそうです。  
これだと、質問での希望した形でCSV出力が出来ます。

[code]

    import csv
    
    refkeys = []
    refkeyslen = len(refkeys)
    result = []
    
    def accumulate(newkeys, newvalues):
        global refkeys
        global refkeyslen
        global result
    
        for key in newkeys:
            if key not in refkeys:
                refkeys.append(key)
    
        refkeyslen = len(refkeys)
    
        newrow = [''] * refkeyslen
        for pos in range(len(newkeys)):
            curkey = newkeys[pos]
            index = refkeys.index(curkey)
            newrow[index] = newvalues[pos]
    
        result.append(newrow)
    
    keys = ["a","b","d","x","テスト"]
    values = ["3354","test","あいうえ","いろはに","12345"]
    accumulate(keys, values)
    
    keys = ["a","b","d","y","テスト"]
    values = ["3399","TEST","あいうえ","ほへと","12345"]
    accumulate(keys, values)
    
    with open('data.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerow(refkeys)
        writer.writerows(result)
    
[/code]

参考記事  
[リストに指定した値と同じ要素が含まれているか確認する](https://www.javadrive.jp/python/list/index10.html)  
[Pythonのリスト（配列）を任意の値・要素数で初期化](https://note.nkmk.me/python-list-initialize/)  
[Python 何度も調べてしまうリスト操作をまとめてみた](https://www.yoheim.net/blog.php?q=20150801)  
[PythonでCSVファイルを読み込み・書き込み（入力・出力）](https://note.nkmk.me/python-csv-reader-
writer/)


CC BY-SA 4.0
2019-10-12T13:25:26.210
59667
2019-10-13T07:00:45.283
2019-10-13T07:00:45.283
26370
26370
59664
answer