命名って難しい

変数、関数、クラスなどなど実装より命名に毎回悩むタイプの人間による技術についてのメモ。

各ECモールの商品カテゴリ/商品分類を技術を使って一致させたい(マスターデータをつくる)

ECの商品カテゴリは各ECによって粒度が違ったり表現が違ったり色々ですよね。

例えば商品を複数のECマーケットに出品したいなと思ったとき、商品カテゴリの設定は売れ行きにも影響するのでそんな雑にはできないかと思います。

【ここのECの〇〇というカテゴリは別のECの△△というカテゴリと同じ】と紐付けできたら便利だと思うんですよね。 そこで、技術でどうにかできないかなと調べてみました。

長くなるのでまずは今回はマスターデータを作るところ。

商品カテゴリを探す

とりあえず思いついた下記サイトから取ってくることとします。

Yahoo!ショッピング

shopping.yahoo.co.jp

au Pay マーケット

wowma.jp

商品カテゴリのマスターデータを作る

Googleはファイルを提供していますが、他2つは公開されていないのでカテゴリーIDとカテゴリー名のペアの簡単なマスターデータをホームページ上のデータから作ってみます。

Yahoo!ショッピング

カテゴリー一覧ページ

f:id:NotShown:20211018180836p:plain
Yahoo!ショッピング カテゴリー一覧ページ
shopping.yahoo.co.jp

一番大きなカテゴリーとその一つ下のカテゴリー(カテゴリー1、カテゴリー2とします)までこのページから取得できます。

ファッション
https://shopping.yahoo.co.jp/category/13457/recommend

レディースファッション
https://shopping.yahoo.co.jp/category/13457/2494/recommend

URLの数字の部分がカテゴリーIDであり、階層構造になっていると推測します。

カテゴリー2のページ

f:id:NotShown:20211018181019p:plain
カテゴリー2のページ

https://shopping.yahoo.co.jp/category/13457/2494/recommend

左カラムのカテゴリー絞り込みのURL「コート、アウター」
https://shopping.yahoo.co.jp/category/13457/2494/37019/recommend?sc_i=shp_pc_cate-rcmd-2494_mdSideChildCategory_37019

こちらはカテゴリー2の下のカテゴリー(カテゴリー3とします)の情報を取得できます。

au Payマーケット

トップページ

f:id:NotShown:20211018181823p:plain
au Pay マーケット

wowma.jp

レディースファッション
https://wowma.jp/category/51?spe_id=top_nav_ctg_1
アウター
https://wowma.jp/category/5103?spe_id=top_nav_ctg_2

au Payマーケットはトップ画面の「カテゴリから探す」にすべて記載があります。 URLの 51 がレディースファッション、 5103 がレディースファッション>アウターのカテゴリーIDと思います。 それぞれカテゴリー1,2とします。

カテゴリー2のページ

f:id:NotShown:20211018182448p:plain
au Payマーケット カテゴリー2のページ

ウインドブレーカー
https://wowma.jp/category/510302/itemlist?spe_id=c_dw02

データの取得

今回は python + selenium でやりました。 カテゴリーを下記のようなクラスで表現し、階層構造を表現してみました。

class Category:
    def __init__(self,url,cate_id,cate_name):
        self.url = url
        self.id = cate_id
        self.name = cate_name
        self.parent = None
        self.children = []
        
    def get_path(self):
        if self.parent != None:
            return ">".join([self.parent.get_path(), self.name])
        else:
            return self.name
    
    def append_child(self, cat):
        cat.parent = self
        self.children.append(cat)
def conv_a2category(a):
    url = a.get_attribute('href')
    return  Category(url, conv_url_to_id(url), a.get_attribute("innerText"))

def conv_url_to_id(url):
    conv_url_to_id.pattern = re.compile(r'[0-9]+')
    return conv_url_to_id.pattern.search(url).group(0)

今回はjupyter notebookでちまちま編集しながら実行してデータを取ったので適切にクラス化、メソッド化をしてませんが、以下がwowmaのカテゴリー取得のロジックです。

def conv_a2category(a):
    url = a.get_attribute('href')
    return  Category(url, conv_url_to_id(url), a.get_attribute("innerText"))

def conv_url_to_id(url):
    conv_url_to_id.pattern = re.compile(r'[0-9]+')
    return conv_url_to_id.pattern.search(url).group(0)

# ドライバーの設定
driver_path = R"~\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)

driver.get('https://wowma.jp/')

# このページのカテゴリーを取得する。
# 大カテゴリー(cat1)→中カテゴリー(cat2)まで
cats = []
cat_area_list = driver.find_elements_by_css_selector('#wowma-ui-top-sidemenu>aside>div>div:nth-child(2)>ul>li')
for cat_area in cat_area_list:
    for top_cat_link in cat_area.find_elements_by_css_selector('div>a[href*="ctg_1"]'):
        cat1 =  conv_a2category(top_cat_link)
        cats.append(cat1)
        for c2link in cat_area.find_elements_by_css_selector('div>a[href*="ctg_2"]'):
            cat1.append_child(conv_a2category(c2link))

# 中カテゴリー(cat2)の各ページの小カテゴリー(cat3)までを取得する。
for cat1 in cats:
    for cat2 in cat1.children:
        cat2.children = [] # リセット
        driver.get(cat2.url)
        # cat2と同一のIDを持つリンクもあるのでそれは除外する(金額レンジとか)
        cat3_links = driver.find_elements_by_css_selector('.sideMenulistItemLinks li a[href*="category"]')
        for link in filter(lambda x:conv_url_to_id(x.get_attribute('href')) != cat2.id, cat3_links):
            cat2.append_child(conv_a2category(link))
        time.sleep(1) # アクセス負荷がかかるので待つ

# 終了
driver.close()

import csv
#CSVに出力する。
with open('auPayMarket_category.csv', 'w',encoding='utf-8',newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['CategoryId','CategoryName','CategoryPath','ParentId'])
    for cat1 in cats:
        writer.writerow([cat1.id,cat1.name,cat1.get_path(),None])
        for cat2 in cat1.children:
            writer.writerow([cat2.id,cat2.name,cat2.get_path(),cat2.parent.id])
            for cat3 in cat2.children:
                writer.writerow([cat3.id,cat3.name,cat3.get_path(),cat3.parent.id])

取得データ

データはこんな感じになりました。

au PAY マーケット

CategoryId,CategoryName,CategoryPath,ParentId
51,レディースファッション,レディースファッション,
5101,その他レディースファッション,レディースファッション>その他レディースファッション,51
510102,制服・作業服,レディースファッション>その他レディースファッション>制服・作業服,5101
510101,その他,レディースファッション>その他レディースファッション>その他,5101
5102,まとめ売り・福袋,レディースファッション>まとめ売り・福袋,51
5103,アウター,レディースファッション>アウター,51
510302,ウインドブレーカー,レディースファッション>アウター>ウインドブレーカー,5103
・・・

Yahoo! ショッピング

CategoryId,CategoryName,CategoryPath,ParentId
13457,ファッション,ファッション,
2494,レディースファッション,ファッション>レディースファッション,13457
37019,コート、アウター,ファッション>レディースファッション>コート、アウター,2494
37052,ジャケット,ファッション>レディースファッション>ジャケット,2494
36861,トップス,ファッション>レディースファッション>トップス,2494
36913,ボトムス、パンツ,ファッション>レディースファッション>ボトムス、パンツ,2494
36887,ワンピース、チュニック,ファッション>レディースファッション>ワンピース、チュニック,2494
36903,オールインワン、セットアップ,ファッション>レディースファッション>オールインワン、セットアップ,2494
48888,ジャージ、スウェット,ファッション>レディースファッション>ジャージ、スウェット,2494
1729,シューズ,ファッション>レディースファッション>シューズ,2494
・・・

次回は一致させる作業を考えていきたいと思います。