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

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

ただいまの
回答率

88.65%

pythonスクレイピング:pickleの使い方について

解決済

回答 1

投稿 編集

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

gomasan

score 50

前提・実現したいこと

Pythonでwebサイトのスクレイピングをし、csvに抽出しています。
前回抽出していないものを抽出し、csvに書き込むということをしたいです。
(本来は昨日の日付で抽出したいのですが、数日後に口コミ反映がされる場合が多いため、「抽出したことがないもの」という条件に切り替えています。)

下の内容でいうと、
・全商品の最終投稿日付(last_update_list)を取得
・次プログラムを回すときにlast_update_listを参照して、追記するレビューリストを作成
・last_update_listを保存・読み込み

発生している問題・エラーメッセージ

pickleを使って前回内容を記憶、読み出しをしようと思っているのですが、うまくいきません。。

Traceback (most recent call last):
  File "/Users/myname/Desktop/sample.py", line 136, in <module>
    main(False)
  File "/Users/myname/Desktop/sample.py", line 30, in main
    pickle.load(fl)
_pickle.UnpicklingError: invalid load key, 'f'.


以下にコード全体を記載しますが、エラー箇所は以下の通りです。

line30 
pickle.load(fl)

line136(コードの最終行)
main(False)

該当のソースコード

sample.py

from selenium import webdriver
import time
import csv
import re
from datetime import datetime, date, timedelta
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
import sys
import pickle


def str2date(date_str):
        result = re.search(r'(\d{4})年(\d{1,2})月(\d{1,2})日', date_str)        
        tar_date = datetime(int(result[1]), int(result[2]), int(result[3]))
        return tar_date

def is_bf_yesterday(tar_date, last_update_date):
        # now = datetime.now()
        # yesterday = now - timedelta(days=1)
        last_update_date_str = datetime.strftime(last_update_date, '%Y-%m-%d')
        tar_date_str = datetime.strftime(tar_date, '%Y-%m-%d')

        return tar_date_str > last_update_date_str

def main(is_init):

        # rbモードでファイルを読み込み
        fl = open('pickle.binaryfile','rb')
        pickle.load(fl)
        # csv_file_name
        csv_file_name = "/Users/myname/Desktop/" + "satofuru" + ".csv"
        f = open(csv_file_name, 'a',encoding='cp932', errors='ignore')

        #ファイルへの書き込み
        writer = csv.writer(f, lineterminator='\n')

        #headerの指定
        csv_header = ["商品名", "投稿日","評価", "レビュー"]
        writer.writerow(csv_header)

        # ブラウザを開く
        last_update_dict = {}
        browser = webdriver.Chrome('/usr/local/bin/chromedriver')
        for page in range(1, 4):
                print('webdriver ok')
                # URLを開く
                url = "https://www.xxx.php?=60&p={}".format(page)
                browser.get(url)
                time.sleep(1.0)
                elems = browser.find_elements_by_class_name('ItemList__link')
                contain_review_links = []
                review_links = []
                date_src_list = []
                for elem in elems:
                        #各elemからimgタグのsrcを一覧で出す
                        review_score_src = elem.find_element_by_class_name('ItemList__xxx').find_element_by_tag_name('img').get_attribute('src')
                        if review_score_src == 'https://www.xxx.jp/static/xxx/images/pic_star0.png':
                                pass
                        else:            
                                # リンクを一覧で取得
                                contain_review_links.append(elem.get_attribute('href'))

                for contain_review_link in contain_review_links:
                        time.sleep(1.0)
                        browser.get(contain_review_link)
                        review_list_src = browser.find_element_by_css_selector('.xxx').get_attribute('href')
                        browser.get(review_list_src)
                        time.sleep(2.0)

                        pr_name = browser.find_element_by_css_selector('.xxx').text

                        review_table = browser.find_elements_by_css_selector('.xxx')[2]

                        review_lists = review_table.find_elements_by_css_selector('tr')[1:]
                        for i, review_list in enumerate(review_lists):
                                # review_listで投稿日が昨日であれば、「続きを読む」のリンクを取得してcsvに抽出する
                                review_lists_date = review_list.find_elements_by_css_selector('td')[0].text
                                date_src_list.append(review_lists_date)
                                for date_src in date_src_list:
                                        elem_date = str2date(date_src)
                                if is_init:
                                        if i == 0:
                                                last_update_dict[pr_name] = elem_date
                                                review_link_list = review_list.find_element_by_css_selector('a').get_attribute('href')
                                                review_links.append(review_link_list)         
                                elif is_bf_yesterday(elem_date, last_update_dict[pr_name]):
                                        # 昨日の日付の場合はhrefリンクを取得し、リストにする(review_links)
                                        review_link_list = review_list.find_element_by_css_selector('a').get_attribute('href')
                                        review_links.append(review_link_list)



                for review_link in review_links:
                        print('review_link ok')
                        csv_list = []
                        time.sleep(2.0)
                        browser.get(review_link)
                        elem_product_name = browser.find_element_by_css_selector('.product-name a')
                        product_name = elem_product_name.text
                        review_box = browser.find_element_by_css_selector('.review_info')
                        review_elems = review_box.find_elements_by_css_selector('tr td') 
                        review_date = review_elems[0].text
                        review_score = 0
                        imgs = review_box.find_elements_by_css_selector('.valuation img')
                        for img in imgs:
                                src = img.get_attribute("src")
                                if src == 'https://www.xxx.jp/xxx/images/common/star_a.png':
                                        review_score += 1
                        review = review_box.find_element_by_css_selector('.cxxx').text
                        csv_list.append(product_name)
                        csv_list.append(review_date)
                        csv_list.append(review_score)
                        csv_list.append(review)
                        writer.writerow(csv_list)

        f.close()
        browser.close()

if __name__ == "__main__":
        if len(sys.argv) >= 2:
                if sys.argv[1] == 'init':
                        main(is_init=True)
        else:
                main(False)

pickle.binaryfile

fl = open('pickle.binaryfile','wb')
pickle.dump(last_update_dict, fl)
fl.close

試したこと

pythonファイルのmain関数でまず読み込みをし、その結果をpickle.binaryfileで保存(したつもりです..)

pickleを今回初めて使いまして、使い方が全然違うかもしれません...
アドバイスいただけますと幸いです。よろしくお願いいたします。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • gomasan

    2020/09/10 14:34

    必要であれば画面もお送りいたします。

    キャンセル

  • nto

    2020/09/10 14:48

    掲載頂いているものはバイナリファイルを生成する為の実行コードであり、それはバイナリファイルではありません。
    pickleによるバイナリファイルとは、pickle.dump()を実行する事によってファイルが生成されるものであり、通常はコードが記述されているものではありません。

    キャンセル

  • gomasan

    2020/09/10 15:35

    なるほど、、失礼いたしました。バイナリファイルの中身の確認方法が分からないのですが、sample.pyファイルのエディタ画像をお送りすればよろしいでしょうか?

    キャンセル

回答 1

checkベストアンサー

+1

最終的にはこの様な構造でコーディングしていけば良いと思います。
コードを実行する時に、pickleを読み取るかどうかはgomasan様の中のルールや条件というものがあるでしょうから、それに従ってif文などで処理してください。(初回起動である場合にも読み取る必要ない)

そしてコードを動かし終わり最後に収集したデータをpickle化する場合にpickle_save()で保存するという形です。

file_name = 'pickle.binaryfile'
def main(is_init):
    if pickleを読み取るか読み取らないかの条件式:
        last_update_dict = pickle_read()

    # 中略 ここに行いたい処理

    # 最後に今回抽出&上書きしたデータをdumpする。
    pickle_save(last_update_dict)

def pickle_read():
    with open(file_name, 'rb') as f:
        p = pickle.load(f)
    return p

def pickle_save(data):
    with open(file_name, 'wb') as f:
        pickle.dump(data, f)
    print('Done!')

追記

思っている通りの動作になっているかはわかりかねますが
以下で、正常にpickle化・非pickle化しコードを正常に運転いただけるかと思います。
コマンドライン引数にinitで初回はpickleの読み込みをしない仕様になっております。
次回以降は引数を与えない事で、初めに非pickle化され、データが追記されていきます。

from selenium import webdriver
import time
import csv
import re
from datetime import datetime, date, timedelta
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
import sys
import pickle



def main(is_init):
    if not is_init:
        last_update_dict = pickle_read()
    else:
        last_update_dict = {}
    print(last_update_dict)

    csv_file_name = "satofuru" + ".csv"
    f = open(csv_file_name, 'a',encoding='cp932', errors='ignore')

    #ファイルへの書き込み
    writer = csv.writer(f, lineterminator='\n')
    csv_header = ["商品名", "投稿日","評価", "レビュー"]
    writer.writerow(csv_header)

    # 中略 ここに行いたい処理
    for i in range(3, 4):
        page_scraping(i, is_init, last_update_dict, writer)

    f.close()
    browser.close()

    # 最後に今回抽出&上書きしたデータをdumpする。
    pickle_save(last_update_dict)

def page_scraping(page, is_init, last_update_dict, writer):
    url = "https://www.satofull.jp/products/list.php?s4=%E5%8C%97%E6%B5%B7%E9%81%93&s3=%E7%B4%8B%E5%88%A5%E5%B8%82&sort=rev_cnt&cnt=60&p={}".format(page)
    browser.get(url)
    time.sleep(1.0)
    elems = browser.find_elements_by_class_name('ItemList__link')
    contain_review_links = []
    review_links = []
    # date_list = []
    date_src_list = []
    for elem in elems:
        #各elemからimgタグのsrcを一覧で出す
        review_score_src = elem.find_element_by_class_name('ItemList__review').find_element_by_tag_name('img').get_attribute('src')
        if review_score_src == 'https://www.satofull.jp/static/master/packages/default/images/pic_star0.png':
            pass
        else:
            # リンクを一覧で取得
            contain_review_links.append(elem.get_attribute('href'))

    for contain_review_link in contain_review_links:
        time.sleep(1.0)
        browser.get(contain_review_link)
        # 商品ページからレビュー一覧へ
        review_list_src = browser.find_element_by_css_selector('.V1808-dReview__head__btn a').get_attribute('href')
        browser.get(review_list_src)
        time.sleep(2.0)

        pr_name = browser.find_element_by_css_selector('.product-name a').text

        # '.table_style_01' = レビュー一覧ページ内のレビューリストの大枠
        review_table = browser.find_elements_by_css_selector('.table_style_01')[2]
        # review_lists = レビューリストの2番目からfor文を回す(1番目は項目のtr)
        review_lists = review_table.find_elements_by_css_selector('tr')[1:]
        for i, review_list in enumerate(review_lists):
            # review_listで投稿日が昨日であれば、「続きを読む」のリンクを取得してcsvに抽出する
            review_lists_date = review_list.find_elements_by_css_selector('td')[0].text
            date_src_list.append(review_lists_date)
            for date_src in date_src_list:
                elem_date = str2date(date_src)
            if is_init:
                if i == 0:
                    last_update_dict[pr_name] = elem_date
                    review_link_list = review_list.find_element_by_css_selector('a').get_attribute('href')
                    review_links.append(review_link_list)         
            elif is_bf_yesterday(elem_date, last_update_dict[pr_name]):
                # 昨日の日付の場合はhrefリンクを取得し、リストにする(review_links)
                review_link_list = review_list.find_element_by_css_selector('a').get_attribute('href')
                review_links.append(review_link_list)

    for review_link in review_links:
        print('review_link ok')
        csv_list = []
        time.sleep(2.0)
        browser.get(review_link)
        elem_product_name = browser.find_element_by_css_selector('.product-name a')
        product_name = elem_product_name.text
        review_box = browser.find_element_by_css_selector('.review_info')
        review_elems = review_box.find_elements_by_css_selector('tr td') 
        review_date = review_elems[0].text
        review_score = 0
        imgs = review_box.find_elements_by_css_selector('.valuation img')
        for img in imgs:
            src = img.get_attribute("src")
            if src == 'https://www.satofull.jp/static/master/packages/default/images/common/star_a.png':
                review_score += 1
        review = review_box.find_element_by_css_selector('.comment_area').text
        csv_list.append(product_name)
        csv_list.append(review_date)
        csv_list.append(review_score)
        csv_list.append(review)
        writer.writerow(csv_list)

def str2date(date_str):
    result = re.search(r'(\d{4})年(\d{1,2})月(\d{1,2})日', date_str)
    tar_date = datetime(int(result[1]), int(result[2]), int(result[3]))
    return tar_date

def is_bf_yesterday(tar_date, last_update_date):
    # now = datetime.now()
    # yesterday = now - timedelta(days=1)
    last_update_date_str = datetime.strftime(last_update_date, '%Y-%m-%d')
    tar_date_str = datetime.strftime(tar_date, '%Y-%m-%d')

    return tar_date_str > last_update_date_str

def pickle_read():
    with open(fine_name,'rb') as f:
        p = pickle.load(f)
    return p

def pickle_save(data):
    with open(fine_name, 'wb') as f:
        pickle.dump(data, f)
    print('Done!')

if __name__ == "__main__":
    fine_name = 'pickle.binaryfile'
    browser = webdriver.Chrome()
    if len(sys.argv) >= 2:
        if sys.argv[1] == 'init':
            main(is_init=True)
    else:
        main(False)

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/09/10 19:10

    改めてサンプルコードを掲載させていただきました。
    また、pickleの書き込み時のオプションが'w'となっていた所を'wb'へ修正致しました。


    サンプルコードの仕様については追記させて頂いております。お確かめ下さい。

    キャンセル

  • 2020/09/10 19:16

    pickleというものに関して私自身も初めて触れたものでしたので勉強になりました。
    以下のページがpickleの使用に関して非常に参考になりました。
    https://techacademy.jp/magazine/15826

    キャンセル

  • 2020/09/11 00:12

    ご丁寧にありがとうございます...(T T)
    参考記事、拝見しました。とてもわかりやすいです。。!
    今回もとても勉強になりました。本当にありがとうございます!!

    キャンセル

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

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

関連した質問

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