メルカリをpythonでスクレイピングして商品データをCSVで保存する方法

メルカリ

以前の記事でハンドメイド作品をメルカリで売るための分析を行いましたが、分析の元となる40,000件近い商品データはpythonを使って機械的に収集しました。

メルカリはスクレイピング対策が豊富で慣れている僕でも結構面倒だったので、苦労しているお仲間も多い事かと思いデータ収集システムの完成に必要な知見を書き残しておきます。

仕上がりはこんな感じ

とりあえず完成品の動作はこちら。

約1,200件の商品データを収集するのに5分くらいかかる感じ。早くはないけどTorを経由している事、メルカリのレスポンスを確認⇔待機のループ処理が入っている事を考えれば及第点かなーと思います。

メルカリのスクレイピングは難易度高め

メルカリはスクレイピングに敏感で機械的なデータ収集を妨げるための施策が随所に施されています。

例えば、

  • 商品情報はhtmlの読み込み後、javascriptで動的に表示している
  • ページに表示されるアイテム件数は約120件だがアクセス毎にランダムに増減する
  • カテゴリやページ数を指定するURLパラメータがアクセス毎に移動する
  • id/class名がアクセス毎にランダムで変わる

などなど僕が気付いた部分だけでもこれだけありまして、繰り返しの回数を固定したりclass名で決め打ちの正規表現など、入門書レベルの施策ではスクレイピングは難しいっすね。

その分取得したデータは大きな価値も持つと言えます。APIも用意されていませんしライバルの少ない仕入れ先・販売先をいち早く抑えるできたり、僕のように統計から自身の商売に生かしたり夢が広がります(‘ω’)

やるなら自己責任でやりましょう

メルカリほど規模の大きいサービスがAPIを用意していないって事は商品データを使ってごにょごにょして欲しくないって事です。利用規約云々の話もあるようですが、やってほしくない事をやっているわけですから怒られても不思議はないですよね。

僕としては自身の経験から得た技術的な知見を業界発展のために共有したい、と思ってこの記事を書いてますんで悪用しちゃダメですよ、本当ですよ。

このスキルだけでたぶん稼げる

ランサーズなどの業務委託サイトではメルカリのリサーチ依頼・データ収集など、当スクリプトを使えば一発で確認できる事が繰り返し発注されています。メルカリはAPIが準備されていないので簡単に機械化できないデータ収集を人海戦術で突破するための依頼だと思われます。

そういったニーズを持っている方に当ツールで得られるデータを提供できたとしたら、十分に商売が成り立つと思います。メルカリではないですが実際に僕も月額でいくつかのデータ収集業務を請けさせて貰っておりまして、仕事の実態はデータ収集ボタン押して出力されたCSVをメールに添付して相手方に送るだけのめっちゃ簡単な内容で5,000円以上は貰えてますんで、お小遣いを稼ぎたい方はぜひチャレンジしてみて下さい。

頂いたお金は最近ハマっているレザークラフトの材料に投資させて頂いております、投下資金の回収はいつになるか不明です笑

レシピ

メルカリデータ取集ツールを作るのに必要な素材はこちら。

  • python
  • selenium(ライブラリ)
  • Tor(アプリ)

pythonの本体とpythonの中でブラウザを起動させるselenium、あとは匿名通信を経由するためのtorアプリケーションだけです。もちろん全部無料です。

python・seleniumのインストール/準備についてはネット上にいくらでも参考文献が転がっているので、各自準備すべし。「python インストール」とか「selenium インストール」で出ます。

Torのインストールや使い方は「【スクレイピングブロック対策】IPアドレスを分散・自動変更する方法まとめ」こっちの記事に書いてますんで、準備できてない方は参考にして下さい。

実際に作ってみる

必要なレシピを確認した所でお次は実際にツールを構築してみます、ここではメルカリデータ収集ツールを作っている際に僕が気付いた重要ポイントを工程別にまとめてみました。上から順番に見ていけば同等のツールは作れるようになると思います。

Tor(トーア)でアクセス元を隠す

メルカリは機械的なアクセスに敏感なのでアクセス元の隠匿は必須です、コレをやっとかないと垢バンどころかメルカリにアクセス出来なくなる可能性もあると思ってます。(なので本当に自己責任で!)

以前はbrightdataというプロキシサービスを推奨していましたが、最近は妙に複雑になって使いづらくなってきましたし、料金も高くなってるっぽいのでプロ串はTorでいいと思います。

例えばTor経由でサイトにアクセスするにはこんな感じ。

#! /usr/bin/env python3

import subprocess
from subprocess import PIPE
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Torのポートを設定
PROXY = "socks5://localhost:9050"

# SeleniumのChromeオプション
options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server=%s' % PROXY)
options.add_argument('--start-maximized')
#options.add_argument('--headless')

try:
    # torの起動
    tor = subprocess.Popen(["tor"], shell=True, stdout=PIPE, stderr=PIPE)

    # サイトにアクセス
    driver = webdriver.Chrome(ChromeDriverManager().install(), chrome_options=options)
    wait = WebDriverWait(driver, 60)
    wait.until(EC.presence_of_all_elements_located)
    url = 'https://www.cman.jp/network/support/go_access.cgi'
    print(url)
    driver.get(url)
            
except Exception as e:
    print("エラー!")
    print(e)
    if driver is not None:
        driver.close()
        driver.quit()
finally:
    # torプロセスを終了させる
    if tor is not None:
        print('torプロセスを終了')
        tor.kill()

IPを表示するサイトにアクセスするとプロキシが刺さっている事が確認できますね。ちなみにTorはアプリの起動毎にIPアドレスが変わる仕様なので、Torをオン・オフする仕組みを仕込めばアクセス毎にIPアドレスをローテーションする機能も実装できると思います。

ただ良い事ばかりではなく、Torを経由する事でサイトへのアクセスが著しく遅くなる場合が多々あります。物理的に世界中のサーバーを経由するわけですからこれは仕方ないですね、データ収集に時間が掛かる事は容認せざるを得ません。

さらに気を付けて欲しいのは、時間が掛かるからと言って並列処理で複数のプロセスを走らせるなどゴリゴリにデータ収集する方法はオススメしません。

並列処理は相手サイトへの負担が大きいのでスクレイピングでやるべきじゃないと僕は考えています、仮に100プロセスとか走らせたら同時接続100件ですからね、DDoSと勘違いされる危険性すらあると思ってます。

seleniumからChrome経由でメルカリにアクセス

当項目の参考スクリプトは上記項目と一緒なので、上にスクロールして見ながら読み進めると理解が深まると思います。

pythonにはブラウザを操作するseleniumという便利なライブラリがあるので、こいつでメルカリを表示させるブラウザを操作します。ブラウザの設定や細かい動作なんかも簡単に記述できるので重宝してます。

今回はTorを経由するのでブラウザオプションに、

options.add_argument('--proxy-server=%s' % PROXY)

を追加するのを忘れずに。自分のIP丸出しになっちゃいますからね。さらにブラウザをバックグラウンドで操作するheadlessというオプションもオススメです。

options.add_argument('--headless')

ブラウザをバックグラウンドで動作させる事のメリットは、

  • 画面表示が不要なので処理が早くなる
  • ブラウザが開かれないのでキーボード/マウスを間違って押しちゃった系の誤操作が起こらない

などのメリットがあります。headlessオプションは設定しなくてもデータ収集できますが、結構早くなったり安定したりするのでスクリプトが安定稼働したら試してみるといいですよ。

あとはメルカリがブラウザ上で開かれるまで待機する、

wait = WebDriverWait(driver, 60)
wait.until(EC.presence_of_all_elements_located)

なんかも必須ですね、Tor経由だとデータの反映がかなり遅いので待機時間はたっぷり60秒にしてみました。環境や時間帯などで待機時間が足りなさそうな場合は安定して読み込みされるまで、時間を延ばしてみて下さい。

ちなみにデータ収集処理を行う際はアクセスが集中しそうな時間帯は避けた方が無難です。これはメルカリに限った話ではありませんが、仮に相手方のサーバーが虚弱だった場合アクセスを繰り返す事で閲覧者全員の応答が遅くなって迷惑をかける可能性もありますし、同時接続が多そうな時間帯はデータ収集のレスポンスも遅くなりがちです。

僕はデータ収集を行う時間を大体決めていまして、平日の午前中 / 深夜~早朝など一般的に人が少なそうな時間にやるようにしています。目を付けられる可能性をちっとでも減らしたいので((+_+))

取得可能な最大アイテム件数

現状メルカリのカテゴリページは最大100ページまでしか表示されない仕様になっているので、1つのカテゴリから収集できるアイテム件数は最大でおおよそ、約120件/ページ×100ページ=約12,000件となります。

メルカリのカテゴリページのURLはページ数を指定するパラメータを持っていて、例えば「ハンドメイド > ファッション/小物 > バッグ(女性用)」カテゴリのURLはこんな感じ。

https://jp.mercari.com/search?category_id=908&page_token=v1%3A100

この内「page_token=v1%3A100」の部分、A以下の数値がページ数を指定するパラメータです。ページ数は0-99まで入力可能なのでスクレイピングの際はここをforとかで繰り返す感じで使う事になると思います。

ちなみに上記でも書きましたが、この「page_token」パラメータはアクセス毎にURL内で位置が微妙に変化するのでコーディングの際はご注意です。具体的には、

https://jp.mercari.com/search?page_token=v1%3A99&category_id=908
https://jp.mercari.com/search?category_id=908&page_token=v1%3A98

こんな感じでパラメータの位置が前後します、僕が確認した以外のパターンもあるかもしれないので何かエラーが出たらコレかも?って頭の片隅に入れとくといいかもしれません。

また、カテゴリページはソートの種類で取得できる件数が変わります。サンプル件数をなるべく多く取得するなら新しい順・おすすめ順にすべし。ていうかデフォルトがおすすめ順なのでソートを変えると取得できるアイテム件数が減るかも、と覚えておけばOK。

javascriptの読み込み遅延対策

メルカリはサイトに表示されている情報のほとんどをjavascriptで後から読み込みしているので、htmlの読み込み完了後、数秒空けてからソースコードを取得しないと商品データが反映されません

ページ内のすべての商品データがもれなく表示し終わったか、を確認してからソースコードの取得をするためには商品リストのコンテナより下にある、ページネーションの「次へ」が表示されているかをソースコード上でサーチして、あれば商品データの抜き出し処理へ移行、なければ数秒待機して再度ソースコードからページネーションの確認処理をループ、みたいな感じで僕は安定稼働しています。

例えばこんな感じで工夫するといいかも。

#javascriptの読み込み遅延対策
i = 0
for i in range(20):
    print(i)
    sleep(5)
    html = driver.page_source
    res = re.search("次へ", html)
    end = re.search("出品された商品がありません", html)
    if res:
        break
    elif end:
        print('最終ページ')
        break
    else:
        print('まだだよ')
else:
    print('全部失敗')

xpathで取得したい要素を指定する

javascriptの読み込み遅延さえ対策できれば、後は普通のスクレイピングと同様に正規表現とかxpathで欲しい要素を抜き出して保存するだけです。今回は確認・修正が簡単なxpathで要素の抜き出しを行っています。

僕はchrome以外使った事がないので他のブラウザは分かりませんが、デベロッパーツールでソースコードからxpathのパターンをコピペするのは簡単です。例えばカテゴリページの商品データを含むすべてのコンテナを抜き出すxpathはこんな感じで探します。

上記動画の通り、カテゴリページの内「//[@id=”item-grid”]/ul/div[*]/li/a」に該当するコンテナに、タイトル・価格・soldフラグなど欲しいデータが詰まっていたので今回はここを取得しています。

さらに要素の中に詰まっている商品情報を分解するために、商品名・SOLDフラグ・価格・サムネイル・ページURLをそれぞれget_attributeで属性を指定すればOKです、これらをまとめるとこんな感じのコードになりました。

#xpathで要素抜き出し
for element in driver.find_elements_by_xpath('//*[@id="item-grid"]/ul/div[*]/li/a'):
    link = element.get_attribute('href')
    title = element.find_element_by_tag_name('mer-item-thumbnail').get_attribute('alt').replace('のサムネイル', '')
    sold_flug = element.find_element_by_tag_name('mer-item-thumbnail').get_attribute('sticker')
    price = element.find_element_by_tag_name('mer-item-thumbnail').get_attribute('price')
    thumb = element.find_element_by_tag_name('mer-item-thumbnail').get_attribute('src')

これで商品情報の抜き出し完了です、ここまで来ればスクリプトは完成したようなもんですね。

CSVに出力する

後は普通にCSVライブラリをインポートして出力するだけです、.txtファイルでも.xlsでも何でもいいですが僕は環境問わず汎用性の高いCSVにしました。

出力する項目は[‘title’, ‘sold_flug’, ‘price’, ‘thumb’, ‘link’]となっており、titleからキーワードを抜き出せは流行や売れ筋が把握できますし、sold_flugとpriceで売れやすい金額帯・売れにくい金額帯を把握したりと僕は活用しています。

CSVに保存した商品データをスプレットシートに取り込むとこんな感じ。

これにてメルカリ商品データ収集システム完成です、僕の場合は商品データを集計・分割・統合など分析するのはスプレットシートでやってます。上手な方は高度な分析もpythonでできると思いますが、取得した商品データの使い方はそれぞれ違うとと思うので今回はそこまで作り込みしませんでした。

スクリプト完成後も定期的にメンテナンスが必要かも

メルカリはスクレイピング対策にかなり力を入れているので、今後も定期的にUIが変更される可能性があると思います。URLなどサイト構造自体が大幅に変更されるというよりは、末端の商品ページの一部が変更されるような小改善はいつあってもおかしくなさそうな感じ。

とはいえデベロッパーツールでxpathを確認すればいいだけな気がするので、修正に必要な時間はそれほど多くないんじゃないかなと楽観してます。

コピペコードはこちら

ここまでなるべく丁寧に解説したつもりですが「イマイチ分からん・作ってみたけど動かない・時間が無い」、という方のために実際に僕が使っているコピペで動くスクリプトコードと運用上のポイントなどをまとめてご紹介します。

ただしメルカリは規約上データ収集に対するリスクがありますんで、当スクリプトで発生したいかなる責任も僕は負いません、下記のスクリプトをご利用された場合この旨に同意したとみなします。

あくまで技術的知見を共有する目的ですのでご理解頂いたうえご利用下さいm(_ _)m

----▽ ここから有料です ▽----

決済方法 料金 返金 アカウント登録
note.com ¥990 – 購入する 24時間以内 不要
paypal
いつでも 必要

以降のコンテンツは閲覧パスワードを購入された方限定で公開しております。必要な方は上記の購入ボタンより各サイトにてお支払い手続きをお願いしますm(_ _)m

【返金保証付き】記事の内容に不満があれば全額返金します!

記事の内容に満足頂けない場合はお支払い頂いた料金をすべてお返しします。

  • noteよりご購入された場合:note.comにてご確認下さい。
  • paypalよりご購入された場合:お問い合わせより「購入日」と「paypalの取引ID」をご連絡下さい。

有料記事に関するNG行為

  • パスワードを第三者に譲渡する行為
  • 不正に入手したパスワードで有料記事を閲覧する行為

上記のルールを違反した場合は筆者が怒りますので、お気を付けください(゚Д゚;)