PC内を整理していると2年前に書いた、メルカリの商品情報をWebサイトからスクレイピングして取得するPHPスクリプトが出てきた。登録したキーワードで検索し、取得した情報はテキストデータとして保存。以降取得データと過去データを突き合わせて新しいものがあればメールで送信。まだ動いたため記録しておく。
サンプルプログラムではkeywordに「雑誌」、sort_orderに「新着」を設定している。xampp環境で実行したがsendmailを使う場合、下記サイトが設定に役立った。
レンタルサーバーを借りているならcronで動かせばいいが試していない。下記プログラムではwhile文を使い無限ループで定期実行させている。
スクレイピング処理はphpQueryという外部ライブラリを使用した。
<?php /** * メルカリからHTMLをスクレイピング */ require_once("phpQuery-onefile.php"); ini_set("max_execution_time", 0); $url = "https://www.mercari.com/jp/search/"; // 基本のURL $sortOrder = "?sort_order=created_desc"; // 安い順:price_asc、高い順:price_desc、新着:created_desc、いいね:like_desc $keyword = "&keyword=雑誌名等"; // 検索ワード $categoryRoot = "&category_root="; // 親カテゴリ $categoryChild = "&category_child="; // 子カテゴリ $priceMin = "&price_min="; // 最低価格 $priceMax = "&price_max="; // 最高価格 $parameter = urldecode($sortOrder . $keyword . $categoryRoot . $categoryChild . $priceMin . $priceMax); $filename = "cache.json"; const LIMIT_ITEMS = 5; while (true) { // スクレイピング開始 $jsonScrRes = scraping($url . $parameter); // キャッシュの読み込み if(file_exists($filename)) { $jsonCache = mb_convert_encoding(file_get_contents($filename), 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); $jsonCacheRes = json_decode($jsonCache, true); // 今回と前回の取得アイテムの比較 checkNewItem($jsonScrRes, $jsonCacheRes, $filename); } else { // 新規データをセーブファイルにJSON形式で保存 file_put_contents($filename, json_encode($jsonScrRes, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); chmod($filename, 0666); } // 5分おき sleep(5 * 60); } /** * スクレイピング処理 */ function scraping($url) { $agent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0)"; $header = array( 'Accept-Encoding: deflate', 'Accept-Language: ja,en-US;q=0.9,en;q=0.8', ); $conn = curl_init(); curl_setopt($conn, CURLOPT_URL, $url); curl_setopt($conn, CURLOPT_RETURNTRANSFER, true); curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($conn, CURLOPT_USERAGENT, $agent); curl_setopt($conn, CURLOPT_HTTPHEADER, $header); $res = curl_exec($conn); curl_close($conn); $doc = phpQuery::newDocument($res); foreach ($doc["section"]->find(".items-box") as $entry){ $link = pq($entry)->find("a")->attr("href"); $title = pq($entry)->find("h3")->text(); // jsonデータとして保持 $jsonData[] = ['link' => $link, 'title' => $title]; } return array_slice($jsonData, 0 , LIMIT_ITEMS); } /** * スクレイピング結果とキャッシュを比較し、新規アイテムのみ記録及びメールで通知。 */ function checkNewItem($JsonScr, $jsonCache, $filename) { // 最初に新着アイテムをコピーし、キャッシュと重複するものは削除する $addItems = $JsonScr; $scrMax = count($JsonScr); $cacheMax = count($jsonCache); for ($i = 0; $i < $scrMax; $i++) { for ($j = 0; $j < $cacheMax; $j++) { // URLから出品商品のユニークIDの抜き出し $scrItemId = str_replace("/jp/items/", "", substr($JsonScr[$i]['link'], 0, strpos($JsonScr[$i]['link'], "/?"))); $cacheItemId = str_replace("/jp/items/", "", substr($jsonCache[$j]['link'], 0, strpos($jsonCache[$j]['link'], "/?"))); if (strcmp($scrItemId, $cacheItemId) == 0) { // 添字で配列を削除 unset($addItems[$i]); continue; } } } // 歯抜けになった添字を詰める $addItems = array_values($addItems); if (0 < count($addItems)) { $tmp = array_reverse(array_merge(array_reverse($jsonCache), array_reverse($addItems))); $jsonWritedata = array_slice($tmp, 0, LIMIT_ITEMS); // 新規データをセーブファイルにJSON形式で保存 file_put_contents($filename, json_encode($jsonWritedata, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); sendMail($addItems); } } function sendMail($items) { $mercariUrl = 'https://www.mercari.com'; $to = "メール送信先のアドレス"; $subject = "メルカリで新着あり"; $message = ""; $max = count($items); for($i = 0; $i < $max; $i++) { $message .= "新着:" . ($i + 1) . "\n"; $message .= $items[$i]["title"] . "\n"; $message .= $mercariUrl . $items[$i]["link"] . "\n\n"; } $headers = "From: メール送信先のアドレス"; mb_language("Japanese"); mb_internal_encoding("UTF-8"); if (mb_send_mail($to, $subject, $message, $headers)) { echo "送信成功"; } else { echo "送信失敗"; } } ?>
「退屈なことはPythonにやらせよう ―ノンプログラマーにもできる自動化処理プログラミング」を読んでpythonでも同じスクリプトを書いたが紛失。BeautifulSoup4を使ったと思う。