pythonと楽天APIで楽天市場の特定ジャンルに属する商品データをCSV化する。

#流れ
1. スクレイピングでshopCodeを取得
2. shopcodeを使って楽天APIを叩いて結果を取得
3. CSV化して保存。

#工夫した所
- 楽天市場のサイトは150ページまでしか開けない仕様になっているので、検索結果が(45件/ページ×150ページ=）6750件を下回るように工夫した。
- 楽天APIは一回につき最大3000件（1ページにつき最大30件で10ページまで可能）までしかデータを取得できないので、該当製品の数が3000件を下回るように工夫した。


#スクレイピングでshopCodeを取得
```python:shopcode.py
#必要なライブラリを取得
from urllib import request
from bs4 import BeautifulSoup
import time
import csv
import requests
import urllib.parse
import time
import pickle
url_list=[]
number='検索結果のうちもっとも高額な商品の値段を50で割った値を代入してください。'
for n in range(number):
    m=50*n
    M=50*(n+1)
    for num in range (1,151):
        url = "https://search.rakuten.co.jp/search/mall/"+str(urllib.parse.quote('調べたい言葉'))+"/ジャンルId/?max="+str(M)+"&min="+str(m)+"&p="+str(num)
        #print(url)
        try:
            html = request.urlopen(url)
            time.sleep(2)
            try:
                soup = BeautifulSoup(html, "html.parser")
                if num!=1 and soup.find('a',class_="item -previous previousPage") == None:
                    print('途中で終わった。')
                    break
                else:
                    for i in soup.find_all('h2'):
                            if 'redirect_rpp' not in i.find('a').get('href'):
                                url_list.append(i.find('a').get('href'))
                                print(url)
                                print(len(url_list))
            except:
                print("error")
        except: 
            print("ページが見つかりません")
            print(url)
print('url_listの長さ')
print(len(url_list))

#取得したリストを保存しておきます。
f = open('url_list.txt', 'wb')
pickle.dump(url_list, f)
```
以上のコードを`python shopcode.py`で実行してください。

#作った商品URLリストを処理する。
```python:shori.py

import pickle
import collections

#まず、先ほど作ったURLリストを取り出す。
f = open("./url_list.txt","rb")
url_list = pickle.load(f)

re_list=[]

for i in url_list:
    result = re.sub('https://item.rakuten.co.jp/', "", i)
    pattern = "(.*)/(.*)"
    d = re.search(pattern, result[:-1])
    re_list.append(d.group(1))
    #print(d.group(2))
re_list1=list(set(re_list))
c = collections.Counter(re_list)
print('店舗ごとの商品数')
print(c)
print('もっとも商品が多いshopとその商品数')
print(max(c.values()))
print('重複を除く前のshop数')
print(len(re_list))
print('重複を除いたshop数')
print(len(re_list1))

#re_list.txtとして重複を除いたshopのリストを保存しておきます。
f = open('re_list.txt', 'wb')
pickle.dump(re_list1, f)
```
#楽天APIを叩いて情報を取得
```python:api.py
import csv
import sys
import codecs
import math
import random
import requests
from time import sleep
import time
import re
url = 'https://app.rakuten.co.jp/services/api/IchibaItem/Search/20170706'
doc_list=[]#最終的な結果はここに入る。
keywords=["美味しい","辛い","しょっぱい"]
type_dic={1003741:'軟水',1003742:'中硬水',1003743:'硬水'}
for shop in re_list:
    #page=1
    payload = {
        'applicationId': 'アプリケーションIDを入力',
        'hits': 30,  # 一度のリクエストで返してもらう最大個数（MAX30)
        "genreId":'欲しいジャンルのIDを入力',
        'shopCode': shop,  # ショップID
        'page': 1,  # 何ページ目か
       
    }
    try:
        r = requests.get(url, params=payload)
        time.sleep(1.5)
        resp = r.json()
        total = int(resp['count'])
        Max = int((total-0.1)//30 +1)
        for i in resp['Items']:

            doc_dic={'name':"","url":"","item_code":"","image":"","price":"","shopname":"","tags":"","explanation":"","feature":""}
            inkeywords=[]
            
            typelist=[]
            
            
            item = i['Item']
            doc_dic["name"]= item['itemName']
            doc_dic['url']=item['itemUrl']
            doc_dic['item_code']=item['itemCode']
            doc_dic['image']=item['mediumImageUrls'][0]['imageUrl']
            doc_dic['price']=item['itemPrice']
            doc_dic['shopname']=item['shopName']
            for word in keywords:
                if word in item["itemCaption"]:
                    inkeywords.append(word)
            if len(inkeywords) > 0:
                doc_dic['feature']=inkeywords
            doc_dic['explanation']=item["itemCaption"]
            doc_dic['tags']=item['tagIds'] 
            #タイプ（タグから要素を取り出すイメージ）
            for tag in item['tagIds']:
                if tag in list(type_dic.keys()):
                    typelist.append(type_dic[tag])
            if len(typelist) > 0:
                doc_dic['type'] = typelist
            
            doc_list.append(doc_dic)
            print(len(doc_list))
    except:
        print('無理だった！！！！')
    
    #pageが2以上
    if Max>1:
        for pagenum in range(2,Max+1):
            payload = {
            'applicationId': 'アプリケーションIDを入力',
            'hits': 30,  # 一度のリクエストで返してもらう最大個数（MAX30)
            "genreId":'欲しいジャンルのIDを入力',
            'shopCode': shop,  # ショップID
            'page': pagenum,  # 何ページ目か
            }
            try:
                r = requests.get(url, params=payload)
                time.sleep(1.5)
                resp = r.json()
                total = int(resp['count'])
                Max = int((total-0.1)//30 +1)
                
                for i in resp['Items']:
                    doc_dic={'name':"","url":"","item_code":"","image":"","price":"","shopname":"","tags":"","explanation":"","feature":""}
                    inkeywords = []
                    typelist = []

                    
                    item = i['Item']
                    doc_dic["name"] = item['itemName']
                    doc_dic['url'] = item['itemUrl']
                    doc_dic['item_code'] = item['itemCode']
                    doc_dic['image'] = item['mediumImageUrls'][0]['imageUrl']
                    doc_dic['price'] = item['itemPrice']
                    doc_dic['shopname'] = item['shopName']
                    for word in keywords:
                        if word in item["itemCaption"]:
                            inkeywords.append(word)
                    if len(inkeywords) > 0:
                        doc_dic['feature'] = inkeywords
                    doc_dic['explanation'] = item["itemCaption"]
                    doc_dic['tags'] = item['tagIds']

                    #タイプ
                    for tag in item['tagIds']:
                        if tag in list(type_dic.keys()):
                            typelist.append(type_dic[tag])
                    if len(typelist) > 0:
                        doc_dic['type'] = typelist

                    doc_list.append(doc_dic)
                    print(len(doc_list))
            except:
                print('無理だった！！！')

#CSV化する。
import pandas as pd
data=pd.DataFrame(doc_list)
data=data.drop_duplicates(subset='item_code')
data.to_csv('info.csv',encoding='utf_8_sig')

```

#参考文献
- [楽天市場商品検索APIの公式ページ](https://webservice.rakuten.co.jp/api/ichibaitemsearch/)
- [楽天市場商品検索APIの公式テストフォーム](https://webservice.rakuten.co.jp/explorer/api/IchibaItem/Search/)
- [【楽天API】ショップIDを入力に商品タイトル，価格，URL，キャプション，JANコードを取得](https://qiita.com/shutokawabata0723/items/d136a04b715de59ade57)
- [pandas.DataFrame, Seriesの重複した行を抽出・削除](https://note.nkmk.me/python-pandas-duplicated-drop-duplicates/)
- [Pythonの例外処理！try-exceptをわかりやすく解説！](https://www.sejuku.net/blog/23044)
- [PythonのCounterでリストの各要素の出現個数をカウント](https://note.nkmk.me/python-collections-counter/)
- [Pythonで末尾のn文字を削除する](https://www.relief.jp/docs/python-remove-last-n-characters-from-string.html)
- [pythonで特定文字（：）より後ろを取り出したい(正規表現)](https://teratail.com/questions/61914)
- [楽天市場のジャンル一覧をツリー形式で作成する（Python）2019年版](https://srlt-tech.com/%E6%A5%BD%E5%A4%A9%E5%B8%82%E5%A0%B4%E3%81%AE%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%AB%E4%B8%80%E8%A6%A7%E3%82%92%E3%83%84%E3%83%AA%E3%83%BC%E5%BD%A2%E5%BC%8F%E3%81%A7%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B/)
- [pythonでlistをファイルに保存し、読み込む方法(numpyも同様！)](https://www.robotech-note.com/entry/2016/10/01/180840)