コピペでスクレイピング④ クラウドワークスの新着情報を収集

# 今回の内容
前回の[コピペでスクレイピング③ クラウドワークスの新着情報を収集](https://qiita.com/Gen2423/items/6ec4695549a707a44ec8)で取得したデータを過去のデータと比較し、新着があれば抽出します。
### 前回作ったコードを出力
前回、下記のようなコードを書きました
これに追記してテスト用に出力できるようにします。
```python:scray_data.py
# 旧リスト保存用ファイル
path = os.getcwd()
data_file = os.path.join(path, 'crowdworks.txt')
# メール送信用データ保存先
send_file = os.path.join(path, 'send.txt')

def new_data_get(target_url):
    res = st.source_code_get(target_url)
    soup = BeautifulSoup(res, "lxml")
    # 1
    title_tags = soup.select(
        '#result_jobs > div.search_results > ul > li > div > div >'
        ' div.item_body.job_data_body > div.job_data_row > div.job_data_column.summary > h3 > a')
    list_datas = []
    # print(title_tags)

    for title_tag in title_tags:
        # 2
        item_name = title_tag.text
        # 3
        item_url = main_url + title_tag.get('href')
        # urlの末尾の番号がユニークなのでIDとして取得
        # 4
        item_id = item_url.split('/')[-1]

        # 5 データをまとめる
        list_datas.append([item_id, item_name, item_url])
    return list_datas
```
コードの下部に下記のコードを追加します。
```python:scray_data.py
# 1
if __name__ == '__main__':
    # URL ==============================================================================
    main_url = 'https://www.crowdworks.jp'
    keyword = 'スクレイピング'
    target_url = f'https://crowdworks.jp/public/jobs/search?keep_search_criteria=' \
                 f'true&order=score&hide_expired=false&search[keywords]={keyword}'
    # ==================================================================================
# 2
    list_datas = new_data_get(target_url)
    for list_data in list_datas:
        print(list_data)
```
```python
# 1
if __name__ == '__main__':
```
#1
これは、scray_data.pyファイルが直接読み込まれたときだけ実行するという意味です。
詳しく書くと長くなりますが、import ~ で他のファイルから読み込んだ時は実行されません。
これを今回は動作確認用として使用します

#2
[コピペでスクレイピング② クラウドワークスの新着情報を収集](https://qiita.com/Gen2423/items/6a300adb5867970c9f77)で作ったキーワードを合成したURL
```python
    keyword = 'スクレイピング'
    target_url = f'https://crowdworks.jp/public/jobs/search?keep_search_criteria=' \
                 f'true&order=score&hide_expired=false&search[keywords]={keyword}
```
をtarget_urlに代入して関数の引数として与えます。
```python
# 2
    list_datas = new_data_get(target_url)
    for list_data in list_datas:
        print(list_data)
```
では、scray_data.pyを実行してみます。
コマンドプロンプト若しくはターミナルで<b>scray_data.pyを保存したディレクトリに移動</b>し、
Windwosであれば
```
py scray_data.py
```
Linun、Macであれば
```
python scray_data.py 
または
python3 scray_data.py
```
で実行できると思います

```python
====== WebDriver manager ======
Current google-chrome version is 105.0.5195
Get LATEST chromedriver version for 105.0.5195 google-chrome
Driver [C:\Users\fujitsu\.wdm\drivers\chromedriver\win32\105.0.5195.52\chromedriver.exe] found in cache
['8105422', 'pythonを利用したスクレイピング機能の開発', 'https://www.crowdworks.jp/public/jobs/8105422']
['7850193', '【Ruby on Rails】でのスクレイピングシステムの追加・修正', 'https://www.crowdworks.jp/public/jobs/7850193']
['7705652', 'スクレイピング作業', 'https://www.crowdworks.jp/public/jobs/7705652']
['8157210', '【GAS】4つのWebサイトからスクレイピングしスプレッドシートに保存', 'https://www.crowdworks.jp/public/jobs/8157210']
['8053755', '楽天、YAHOO！、Amazonの購入履歴情報の収集ツール作成', 'https://www.crowdworks.jp/public/jobs/8053755']
['8156120', '「スクレイピング・データ収集」のお仕事のご相談（建設・不動産）', 'https://www.crowdworks.jp/public/jobs/8156120']
['8144592', 'ヤフオク特定商品の価格取得', 'https://www.crowdworks.jp/public/jobs/8144592']
['8093442', 'SNS媒体でのスクレイピング抽出プログラムの構築【応募方法参照・外国人不可・テンプレート応募不可】', 'https://www.crowdworks.jp/public/jobs/8093442']
['8157251', '指定したサイトの企業一覧をスプレッドシートかエクセルに吐き出してほしい', 'https://www.crowdworks.jp/public/jobs/8157251']
['8118501', '指定サイトの情報を収集するツール', 'https://www.crowdworks.jp/public/jobs/8118501']
['8113262', 'eBay用API・スクレイピングによる新規出品システムの不具合修正', 'https://www.crowdworks.jp/public/jobs/8113262']
['8128463', '指定サイトからデータ収集し、弊社サイトへの反映', 'https://www.crowdworks.jp/public/jobs/8128463']
['8098855', '情報サイトの情報移転', 'https://www.crowdworks.jp/public/jobs/8098855']
['8159871', 'Googleスプレッドシートに入力したデータをワードプレスにコピぺ可能にする仕組み化を募集します。', 'https://www.crowdworks.jp/public/jobs/8159871']
['8109911', '0907_企業情報を収集して、共有するGoogleスプレッドシートに集めていただきたいです！_MF_3020', 'https://www.crowdworks.jp/public/jobs/8109911']
['8159265', '0916_企業情報を収集して、共有するGoogleスプレッドシートに集めていただきたいです！__3020', 'https://www.crowdworks.jp/public/jobs/8159265']
.......
```
このように一連のデータが取得できれば成功です。

## 新着（差分）データを抽出する
```python:scray_data.py
# 差分比較
def diff_data_get(list_datas):
    # 返り値用のリスト
    diff_datas = []
    # 旧データの比較用に数値のみ抽出するリスト
    old_datas_num = []

    # 旧データを取得
    with open(data_file, 'r') as f:
        reader = f.readlines()
        old_list_datas = [row for row in reader]
        # 　旧リストの番号だけ取得
        for old_list_data in old_list_datas:
            old_datas_num.append(old_list_data.replace('\n', ''))

    # 新リストの番号が旧リストになければdiff_dataリストにして値を返す
    for list_data in list_datas:
        if list_data[0] not in old_datas_num:
            diff_datas.append(list_data)
            # print(list_data)
        else:
            pass
    for diff_data in diff_datas:
        with open(send_file, 'a', encoding='utf-8') as f:
            f.write(f'{diff_data[0]}  {diff_data[1]}  {diff_data[2]}\n')

    return diff_datas
```
上記が新着を抽出する関数です。
```python
def diff_data_get(list_datas):
    # 返り値用のリスト
    diff_datas = []
    # 旧データの比較用に数値のみ抽出するリスト
    old_datas_num = []
```
まず、差分抽出関数を『diff_data_get(list_datas):』とし、
先程、『new_data_get(target_url)』で得られた返り値をlist_datasに代入します。
diff_datas = [] は新着のデータを格納します。
old_data_num = [] は前回の案件のデータを格納するのですが、案件の番号のみを保存しています。
<font color="Blue">案件の番号さえわかれば比較できるし、文字列同士を比較するより精度が良いので過去データには番号のみ保存しています。</font>保存の部分については後述します。

テキストファイルの読み込み元
```python
# 旧リスト保存用ファイル
path = os.getcwd()
data_file = os.path.join(path, 'crowdworks.txt')
# メール送信用データ保存先
send_file = os.path.join(path, 'send.txt')
```
コメント通りなのですが、data_file とsend_fileのパスを指定しています。

```python
    # 旧データを取得
    with open(data_file, 'r') as f:
        reader = f.readlines()
        old_list_datas = [row for row in reader]
        # 　旧リストの番号だけ取得
        for old_list_data in old_list_datas:
            old_datas_num.append(old_list_data.replace('\n', ''))
```
data_file(中身はcrowdworks.txt)ファイルを呼び出し、配列としてold_list_datasに格納しています。そして、old_list_datasから、番号だけを抜き出し、改行記号”/n"を削除してold_data_numに格納しました。
削除記号はテキストファイルに保存するときに勝手に入るので、これを削除しないと新旧データの比較ができなくなります。そのために削除しました。

#### データ比較
```python
    # 新リストの番号が旧リストになければdiff_dataリストにして値を返す\
    #3
    for list_data in list_datas:
        if list_data[0] not in old_datas_num:
            diff_datas.append(list_data)
            # print(list_data)
        else:
            pass
    #4
    Gmail用ファイルに出力
    for diff_data in diff_datas:
        with open(send_file, 'a', encoding='utf-8') as f:
            f.write(f'{diff_data[0]}  {diff_data[1]}  {diff_data[2]}\n')

    return diff_datas
```
#3
for文でlist_datasから配列で格納されている１案件分のデータを抜き出し、lidt_data[0]で、案件の番号のみを抜き出して、if ~ in で、old_data_numと比較しています。
old_data_numに含まれていない場合はdiff_datasに配列として格納します。
格納する時はlist_data[0]などと、要素を限定していないのでデータ全部が格納されます。
elseの場合、
新データ番号が旧データ番号に含まれている場合は処理をスキップします。

#4
上記の処理でdiff_datasに格納されたデータはGmail送信用ファイルとしてsend_fileに保存します。
send_fileに保存するときに
```python
f.write(f'{diff_data[0]}  {diff_data[1]}  {diff_data[2]}\n')
```
としているのは配列などで格納すると、Gmailメッセージとして送信する時、余計な記号が入るのを防止するためです。上記のように値を指定して保存し、最後に改行を入れておくと、Gmailで送信したときに読みやすくなります。



# まとめ
今回は新旧データの差分比較を行いました。
ポイントは文字列抽出だと時々誤作動が起こるため、一意の数字を見つけて比較するところです。
一意の数字は、URLに含まれている場合と、WEBページの何処かにある場合、そのサイトによって色々あると思います。クラウドワークスの場合はURLに含まれているため、抽出が容易でした。
このあたりが差分比較のポイントになると思います。


上記のコードでは旧データのファイルが無い場合の処理が入っていません。
また、この段階では新データは保存していませんが、それはまとめて次回の処理で行います。
また次回もよろしくお願いいたします。