2021年9月 クロネコヤマトの荷物問い合わせシステムのページレイアウトが変更になって、送り状番号から荷物の配送状況を取得していたバッチがエラーとなった。
APIを使用すればよいという話だが、ヤマトビジネスメンバーズは他部署で契約&契約した人は退職で詳細不明になっている。まあ、面倒なので、PHPで問い合わせページから荷物状況を取得するプログラムを書いた。
実際はベタ書きしているが、かっこ悪いので、公開に当たりクラス化。1回テスト確認はしたが、使っていないので、エラーがああるかも。PHP5.4だが、7でも8でも動くと思う。取得した状況には「▶」が付いているので不要であれば消して使ってください。
内部でサイト解析のためDOMDocument使っているので使える環境のこと。以下、使い方とクラス。
使い方
$truckNumbers = array( '444486855064', '4444-9099-9090', '444490999101', ); // いくつでも可 $objYamato = new clsGetYamatoDeliverStatus(); $aryDelivStatus = $objYamato->getYamatoDeliverStatus($truckNumbers); if ($aryDelivStatus === false){ /* エラー ヤマト追跡番号 状況取得失敗 */ } var_dump($aryDelivStatus); /* 結果は[ (‐無しの追跡番号), (‐有りの追跡番号), 状況日, 配達状況, 問い合わせ時間]の配列 array(3) { [0] => array(5) { [0] => string(12) "444486855064" [1] => string(14) "4444-8685-5064" [2] => string(5) "10/20" [3] => string(16) "▶ 配達完了" [4] => string(19) "2021/10/12 09:59:10" } [1] => array(5) { [0] => string(12) "444490999090" [1] => string(14) "4444-9099-9090" [2] => string(5) "10/20" [3] => string(16) "▶ 配達完了" [4] => string(19) "2021/10/12 09:59:10" } [2] => array(5) { [0] => string(12) "444490999101" [1] => string(14) "4444-9099-9101" [2] => string(5) "10/20" [3] => string(16) "▶ 配達完了" [4] => string(19) "2021/10/12 09:59:10" } } */
荷物状況取得 クラス
<?php class clsGetYamatoDeliverStatus{ // 配列$truckNumbersに指定された追跡番号の状況を全て取得する。$truckNumbersは‐有でも無しでも可。 // 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間] // 戻り値がfalseの場合、エラー(ヤマト追跡サイトにアクセス出来ない) public function getYamatoDeliverStatus($truckNumbers){ // 10件ずつヤマトのサイトからトラッキング情報を取得する。 $numnerCnt = count($truckNumbers); $getTruckNumbers = array(); $aryAllStatusInfo = array(); for ($i=0;$i < $numnerCnt;$i++){ $getTruckNumbers[] = $truckNumbers[$i]; if (count($getTruckNumbers) == 10){ $aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers); $aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus); if ($aryTemp === false){ return false; } $aryAllStatusInfo = $aryTemp; $getTruckNumbers = array(); } } // 残っている場合 if (count($getTruckNumbers) > 0){ $aryRetStatus = $this->getYamatoDeliverStatusCore($getTruckNumbers); $aryTemp = array_merge($aryAllStatusInfo, $aryRetStatus); if ($aryTemp === false){ return false; } $aryAllStatusInfo = $aryTemp; } return $aryAllStatusInfo; } // ヤマトの追跡サイトにアクセスし、10件づつ結果を取得する。 // 引数の$truckNumbersは配列で10件まで。 // 戻り値は次の配列の配列 ["123456781234"(‐無しの追跡番号),"1234-5678-1234"(‐有りの追跡番号),mm/dd 状況日,配達状況,Y/m/d H:i:s 問い合わせ時間] // 戻り値がfalseの場合エラー(サイトにアクセス出来ない場合のみ) private function getYamatoDeliverStatusCore($truckNumbers){ $aryRet = array(); $url = 'https://toi.kuronekoyamato.co.jp/cgi-bin/tneko'; $tnumCnt = count($truckNumbers); $aryPOST = array( 'mypagesession' => '', 'backaddress' => '', 'backrequest' => '', 'number00' => 2 ); $j=0; for ($i=1;$i<=10;$i++){ $postName = 'number'; if ($i < 10){ $postName .= '0' . $i; }else{ $postName .= $i; } if ($j < $tnumCnt){ $aryPOST[$postName] = $truckNumbers[$j]; }else{ $aryPOST[$postName] = ''; } $j++; } $http_build = http_build_query($aryPOST,'','&'); $header = array( "Content-Type: application/x-www-form-urlencoded", "Content-Length: ".strlen($http_build) ); // オプション $opts = array("http" => array( "method" => "POST", "header" => implode("\r\n", $header), "content" => $http_build) ); $scc = stream_context_create($opts); // 読み取り $get_html = file_get_contents($url, false, $scc); if ($get_html === false){ return false; } // staus取得時間 $getTime = Date('Y/m/d H:i:s'); $dom = new DOMDocument(); $ret = $dom->loadHTML($get_html); $divResult = $this->getElementsByClassName($dom, 'tracking-box-area', 'div'); $divLen = count($divResult); for($i=0;$i < $divLen;$i++){ // このdivの下のinputのvalueを取得 $domInputs = $this->getDomNodeFromTagName($divResult[$i], 'input'); if (count($domInputs) == 0){ continue; } $trNum = $domInputs[0]->attributes->getNamedItem('value')->nodeValue; if ($trNum == ''){ continue; } // このdiv以下の日付を取得 $date = ''; $dateNodes = $this->getDomNodeFromClassName($divResult[$i], 'data date'); if (count($dateNodes) == 0){ continue; } $date = $dateNodes[0]->nodeValue; // このdiv以下の状況を取得 $state = ''; $dateStates = $this->getDomNodeFromClassName($divResult[$i], 'data state'); if (count($dateStates) == 0){ continue; } $state = $dateStates[0]->nodeValue; $aryRet[] = array( str_replace('-','',$trNum), // ‐なし $trNum, // ‐あり $date, // 状況になった日付 $state, // 状況 $getTime // 取得時刻 ); } return $aryRet; } private function getElementsByClassName($dom, $ClassName, $tagName=null) { if($tagName){ $Elements = $dom->getElementsByTagName($tagName); }else { $Elements = $dom->getElementsByTagName("*"); } $Matched = array(); for($i=0;$i<$Elements->length;$i++) { if($Elements->item($i)->attributes->getNamedItem('class')){ if(strpos($Elements->item($i)->attributes->getNamedItem('class')->nodeValue, $ClassName) !== false) { $Matched[]=$Elements->item($i); } } } return $Matched; } // 子ノード以下に指定のタグ名を持つdomNodeの一覧を返す。 private function getDomNodeFromTagName($domNode, $tagName){ $aryRet = array(); foreach ( $domNode->childNodes as $chiledNode ){ if ($chiledNode->nodeName == $tagName){ $aryRet[] = $chiledNode; } if ($chiledNode->hasChildNodes()){ $aryTemp = $this->getDomNodeFromTagName($chiledNode, $tagName); foreach($aryTemp as $temp){ $aryRet[] = $temp; } } } return $aryRet; } // 子ノード以下に指定のクラス名を持つdomNodeの一覧を返す。 private function getDomNodeFromClassName($domNode, $className){ $aryRet = array(); foreach ( $domNode->childNodes as $chiledNode ){ if ($chiledNode->hasAttributes()){ $nodAttr = $chiledNode->attributes->getNamedItem('class'); if ($nodAttr != null){ $nodeClasses = $nodAttr->nodeValue; if (strpos($nodeClasses, $className) !== false){ $aryRet[] = $chiledNode; } } } if ($chiledNode->hasChildNodes()){ $aryTemp = $this->getDomNodeFromClassName($chiledNode, $className); foreach($aryTemp as $temp){ $aryRet[] = $temp; } } } return $aryRet; } }