Python(十九)实现各大跨境船公司物流查询数据处理优化

一、前言

之前已经实现了常用 跨境物流船司 基础信息查询功能,如下所示

  • 实现各大跨境船公司[COSCO/ZIM/MSK/MSC/ONE/PIL]的物流信息查询:
  • https://blog.csdn.net/Makasa/article/details/145484999?spm=1001.2014.3001.5501

然后本章在其基础上做了一些优化,以及新增之前未实现的功能

1)实现【提单号】单个自动分配查询船司信息

  • 方法:assign_shipper(bill_number)

2)实现【提单号】批量查询

  • 方法:search_tracking_info_by_list(bl_list)

3)获取最新【ETA(预计到港时间)】时间并自动写入excel

  • 方法:get_latest_eta_time_and_insert_excel()

会在原有excel文件中原【ETA】列后添加一行,再将新的【ETA】填写进去
不直接替换原因便于对比;在这里插入图片描述

4)自动将船司查询到的信息按规定表头写入excel表格【单条】

  • insert_ship_detail_to_excel(“NGZN50041300”)
  • 注意:表头格式我这边按实际需求限定死了,可以按你们自己需求进行更改

在这里插入图片描述
在这里插入图片描述

5)批量将船司查询到的信息按规定表头写入excel表格【list】

  • insert_ship_detail_list_to_excel(bl_list) # 批量插入数excel数据

未来需优化:

  • 1)PIL船司返回的前端,解析目前还存在一点问题,需要兼容各种返回情况的产生;
    目前默认只解析拿到第一个集装箱的数据;
  • 2)ZIM接口不稳定,需排查问题
  • 3)OOCL / CMA /HPL 加密 尝试找方法破解


二、代码实现

  • 上篇blog已经实现了基础查询功能,按船司分开写的,这里我就不统一分开了,直接贴整个代码
# -*- codeing = utf-8 -*-
# @time: 2025/2/12  14:26
# @Author : Mikasaimport json
import time
from datetime import datetime
import pandas as pd
import requests
from bs4 import BeautifulSoup
from pathlib import Pathdef get_MSK_tracking_info(tracking_number):"""查询MSK船司物流信息:param tracking_number:提单号:return:"""url = "https://api.maersk.com.cn/synergy/tracking/" + tracking_numberparams = {"operator": "MAEU"}headers = {"consumer-key": "UtMm6JCDcGTnMGErNGvS2B98kt1Wl25H",  # 关键认证头 必传"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"}try:response = requests.get(url, params=params, headers=headers, timeout=10)result_json = response.json()print("===========================================")print("MSK船信息:", result_json)return result_jsonexcept requests.exceptions.RequestException as e:print(f"请求失败: {e}")except json.JSONDecodeError:print("响应不是有效的 JSON 格式")def get_MSK_related_time(tracking_number):"""获取MSK船期时间:param tracking_number::return:"""result = get_MSK_tracking_info(tracking_number)location_list = result['containers'][0]['locations']target_terminal = "APAPA PORT"index = 0for i, location in enumerate(location_list):if location["terminal"] == target_terminal:index = i  # 拿到目的地为apapa PORT的数据的indexbreak# todo : 246217454 这个已经到港并且已经完结的提单没有expected_time属性# todo :248440101 未到港,就存在expected_time属性ship_info = location_list[index]['events'][0]event_time_type = ship_info['event_time_type']if event_time_type == "EXPECTED":# 提单还未到港ETA = ship_info['expected_time']elif event_time_type == "ACTUAL":# 提单已到港,拿真实时间ETA = ship_info['actual_time']new_ETA_fomat = deal_MSK_eta_time_format(ETA)print("------------------------------------------")print(f"提单号【{tracking_number}】船期信息如下:")print(f"预计到港时间:{new_ETA_fomat}")return new_ETA_fomatdef deal_MSK_eta_time_format(date):"""处理MSK船司预期时间格式:param date::return:"""dt = datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%f")formatted_str = dt.strftime("%Y/%#m/%#d %H:%M:%S")return formatted_strdef get_ONE_tracking_info_by_bill_number(bill_number):"""根据提单号查询ONE物流信息:param bill_number::return:"""str = "ONEY"if str in bill_number:bill_number = bill_number.replace("ONEY", "")headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36","Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With": "XMLHttpRequest"}url = "https://ecomm.one-line.com/ecom/CUP_HOM_3301GS.do?" \"sord=asc" \"&f_cmd=121" \"&search_type=B" \f"&search_name={bill_number}"response = requests.get(url, headers=headers)result = response.json()print("BL_response:", result)return resultdef get_ONE_tracking_info(bill_number):"""查询ONE船司物流信息:param tracking_number::return:"""result = get_ONE_tracking_info_by_bill_number(bill_number)container_list = result['list'][0]  # 默认取第一个集装箱container_number = container_list['cntrNo']cop_no = container_list['copNo']bkg_no = container_list['bkgNo']url = "https://ecomm.one-line.com/ecom/CUP_HOM_3301GS.do"headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36","Content-Type": "application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With": "XMLHttpRequest"}payload = {"f_cmd": "125","cntr_no": container_number,"bkg_no": bkg_no,"cop_no": cop_no}response = requests.post(url, headers=headers, data=payload)if response.status_code == 200:data = response.json()print("===========================================")print("ONE船信息:", response.json())return datadef get_ONE_related_time(bill_number):"""查询ONE船期时间:param bill_number::return:"""data = get_ONE_tracking_info(bill_number)track_info_list = data['list']eta = "Arrival at Port of Discharging"actual_pod = "POD Berthing Destination"discharge_cargo = "Port of Discharging"print("------------------------------------------")print(f"提单号【{bill_number}】船期信息如下:")for track_info in track_info_list:statusNm = track_info['statusNm']if eta in statusNm:print(f"预期到港时间:", track_info['eventDt'])elif actual_pod in statusNm:print(f"实际抵达卸货港时间:", track_info['eventDt'])elif discharge_cargo in statusNm:print(f"实际卸货时间:", track_info['eventDt'])return track_info['eventDt']  # 直接返回的最新一套信息的时间def get_PIL_cookie():"""获取PIL的cookie:return:"""# 生成北京当前时间戳 单位为mstimestamp_ms = int(time.time() * 1000)url = "https://www.pilship.com/wp-content/themes/hello-theme-child-master/pil-api/common/get-n.php?timestamp=" + str(timestamp_ms)headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36","Referer": "https://www.pilship.com/digital-solutions/?tab=customer&id=track-trace&label=containerTandT&module=TrackTraceBL&refNo=NGRI50097000"}response = requests.get(url, headers=headers)return responsedef get_PIL_tracking_info_by_bill_number(bill_number):"""根据提单号查询PIL物流信息:param bill_number::return:"""response = get_PIL_cookie()  # 拿到nn = response.json()['n']timestamp = str(int(time.time() * 1000))referer = "https://www.pilship.com/digital-solutions/?tab=customer&id=track-trace&label=containerTandT&module=TrackTraceBL&refNo=" + bill_numberheaders = {'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",'referer': referer,'X-Requested-With': "XMLHttpRequest",'cookie': "CookieConsent={stamp:%27LUQ2eOuWASANePCHNSXS2yeSAiOmJoQhXm7pEbeVpMEr7kRRRhczfw==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cmethod:%27explicit%27%2Cver:1%2Cutc:1734771385816%2Cregion:%27ng%27}; _ga=GA1.1.1137581632.1734770854; pil_session_cookie=!WFtN5njOPpQS1hf8xTD27kmUREcR4A5xQFGbSdeZWh6xne7uf6cynXyQzICRX7DAVtXpZHsVQFbXaA==; wp-wpml_current_language=en; TSPD_101=08c4718e7dab2800c57f82cee5e846814246783a4ea9308f585af084e53e57c851f4d7af7507b6ba01b43d68d07a6fc9084f292334051800b0bdc60b7433c56f989c7ca0dcd64b787093688cd95badc6; TS00000000076=08c4718e7dab28007707032a2a3c087717436b105e0ea27086f21be845e6238e426c659eed97e9faac24d9498ea8e2f90825bede2809d000f4ff876b60b5d60819d6e9be3e6f3d6f2d0ad9cc544d6ba809938284a1f852c824efa411a4094ef683ea56f2d383a67379539aa5b28a9a52823dde83af710cc1065a8cb738359c2102062f1f41799313cc887fe2f7f84015edf73a4443f90343838461044c8dbccae6660ca3322e787bb229d3ae25557ce32af1669c3ddeb451eb967fe99ac32cc0ef550af1567da52efbed7d17b811159ef5b40416eb7178abca86b4f273868b80aee1cd0ab7a38515f98e7b91baf133b22894982a220077e68b7c314d8ca5c0f89a4bbc55a09b538e; TSPD_101_DID=08c4718e7dab28007707032a2a3c087717436b105e0ea27086f21be845e6238e426c659eed97e9faac24d9498ea8e2f90825bede28063800dd525af8f96ac9cff1388ccc906e36023328029cfae4893759356223de54c62e94a0c306a33859e46108198cac5061a89c8fa7c14267e4fc; TS7ff733e9077=08c4718e7dab280085f60f04ba264a2e0ac0875b36b59c53defab055f5fea0c7c7ce7b40ed9afcf756781ecc450e1b470807626b7517200050d650fcfa6773405f5a17954cff4628d908f416f4234200dbaa3cc3ec46b52a; _ga_M39J8YZNDE=GS1.1.1738786704.56.0.1738786704.0.0.0; TS015cd57e=011698f8e78ca3bc42cad4dd0e721fdcb048e98d12c3529eecbcc961ce0855aa8b6eef2788cf1b3cc8bbe190c65fcf3d0666de4efd377ed9d22d0f951ed126bf67a554e95c; TS7ff733e9029=08c4718e7dab2800e6a5d0f6faeca1dc19685dfff567b4dd1369f1b4b696f84893905e24cb58bc50ffb1a716a0e029fa; TS97a2723b027=08c4718e7dab2000fd4b11416d6e7e1c3da097165f9bd7b084e973e022fa2ec9d671050f9f2f2966081a278b7c1130004fb6fbf91b1d6cb4cd98e6bcb7bd8410d5bed0a0f4b4e1e764e4f4d75f4d5666b6a1bb124f33758104b457ee4acaf2c2"}# 否则根据提单号查询url = f"https://www.pilship.com/wp-content/themes/hello-theme-child-master/pil-api/trackntrace-containertnt.php?" \f"module=TrackTraceBL" \f"&refNo={bill_number}" \f"&n={n}" \f"&timestamp={timestamp}"response = requests.get(url, headers=headers)print("url:", response.url)  # 查看最终请求的 URLif response.status_code == 200:response_json = json.loads(response.text)result_json = analysis_PIL_response_by_bill_number(response_json)  # 解析return result_jsonelse:print("通过【提单号】查询PIL船司信息失败")def get_PIL_tracking_info(bill_number):"""查询PIL船公司物流信息(完整工作流)- 包含动态Token获取和Cookie管理- 必传校验:n | 经过多次测试,发现cookie只穿"""response = get_PIL_cookie()  # 拿到nn = response.json()['n']timestamp = str(int(time.time() * 1000))referer = "https://www.pilship.com/digital-solutions/?tab=customer&id=track-trace&label=containerTandT&module=TrackTraceBL&refNo=" + bill_numberheaders = {'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",'referer': referer,'X-Requested-With': "XMLHttpRequest",'cookie': "CookieConsent={stamp:%27LUQ2eOuWASANePCHNSXS2yeSAiOmJoQhXm7pEbeVpMEr7kRRRhczfw==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cmethod:%27explicit%27%2Cver:1%2Cutc:1734771385816%2Cregion:%27ng%27}; _ga=GA1.1.1137581632.1734770854; pil_session_cookie=!WFtN5njOPpQS1hf8xTD27kmUREcR4A5xQFGbSdeZWh6xne7uf6cynXyQzICRX7DAVtXpZHsVQFbXaA==; wp-wpml_current_language=en; TSPD_101=08c4718e7dab2800c57f82cee5e846814246783a4ea9308f585af084e53e57c851f4d7af7507b6ba01b43d68d07a6fc9084f292334051800b0bdc60b7433c56f989c7ca0dcd64b787093688cd95badc6; TS00000000076=08c4718e7dab28007707032a2a3c087717436b105e0ea27086f21be845e6238e426c659eed97e9faac24d9498ea8e2f90825bede2809d000f4ff876b60b5d60819d6e9be3e6f3d6f2d0ad9cc544d6ba809938284a1f852c824efa411a4094ef683ea56f2d383a67379539aa5b28a9a52823dde83af710cc1065a8cb738359c2102062f1f41799313cc887fe2f7f84015edf73a4443f90343838461044c8dbccae6660ca3322e787bb229d3ae25557ce32af1669c3ddeb451eb967fe99ac32cc0ef550af1567da52efbed7d17b811159ef5b40416eb7178abca86b4f273868b80aee1cd0ab7a38515f98e7b91baf133b22894982a220077e68b7c314d8ca5c0f89a4bbc55a09b538e; TSPD_101_DID=08c4718e7dab28007707032a2a3c087717436b105e0ea27086f21be845e6238e426c659eed97e9faac24d9498ea8e2f90825bede28063800dd525af8f96ac9cff1388ccc906e36023328029cfae4893759356223de54c62e94a0c306a33859e46108198cac5061a89c8fa7c14267e4fc; TS7ff733e9077=08c4718e7dab280085f60f04ba264a2e0ac0875b36b59c53defab055f5fea0c7c7ce7b40ed9afcf756781ecc450e1b470807626b7517200050d650fcfa6773405f5a17954cff4628d908f416f4234200dbaa3cc3ec46b52a; _ga_M39J8YZNDE=GS1.1.1738786704.56.0.1738786704.0.0.0; TS015cd57e=011698f8e78ca3bc42cad4dd0e721fdcb048e98d12c3529eecbcc961ce0855aa8b6eef2788cf1b3cc8bbe190c65fcf3d0666de4efd377ed9d22d0f951ed126bf67a554e95c; TS7ff733e9029=08c4718e7dab2800e6a5d0f6faeca1dc19685dfff567b4dd1369f1b4b696f84893905e24cb58bc50ffb1a716a0e029fa; TS97a2723b027=08c4718e7dab2000fd4b11416d6e7e1c3da097165f9bd7b084e973e022fa2ec9d671050f9f2f2966081a278b7c1130004fb6fbf91b1d6cb4cd98e6bcb7bd8410d5bed0a0f4b4e1e764e4f4d75f4d5666b6a1bb124f33758104b457ee4acaf2c2"}result_json = get_PIL_tracking_info_by_bill_number(bill_number)container_number = result_json['data']['table2'][0]['Container #']container_number = container_number[:11]  # 截取固定bl固定长度11位print("container_number:", container_number)if container_number != '':# 箱号存在,就根据箱号查询url = f"https://www.pilship.com/wp-content/themes/hello-theme-child-master/pil-api/trackntrace-containertnt-trace.php?" \f"module=TrackTraceBL" \f"&reference_no={bill_number}" \f"&cntr_no={container_number}" \f"&n={n}" \f"&timestamp={timestamp}"response = requests.get(url, headers=headers)# print("url:", response.url)  # 查看最终请求的 URLif response.status_code == 200:response_json = json.loads(response.text)resule_json = analysis_PIL_response_by_container_number(response_json)  # 解析return resule_jsonelse:print("通过【箱号】查询PIL船司信息失败")def analysis_PIL_response_by_container_number(response_json):"""解析通过【提单号+箱号】查询PIL船信息response的前端属性:param bill_number::param container_number::return:"""html_data = response_json["data"]  # 提取data里的htmlsoup = BeautifulSoup(html_data, "html.parser")  # 解析# 提取表头header_row = soup.find("tr", class_="bg-lightblue text-fc-black text-fw-bold")headers = [th.text.strip() for th in header_row.find_all("td")]# 提取数据行data_rows = soup.find_all("tr", class_="bg-lightblue text-fc-black")table_data = []for row in data_rows:cells = row.find_all("td")row_data = {headers[i]: cells[i].text.strip() for i in range(len(headers))}table_data.append(row_data)# 构建最终的 JSON 结构result = {"success": response_json["success"],"data": table_data}return resultdef analysis_PIL_response_by_bill_number(response_json):"""解析通过【提单号】查询PIL船信息response的前端属性- 因为通过【提单号】/【箱号】返回的dom树结构不一样,所以解析方法不一致- 注意:解析不兼容,提单号里面没有箱号树的情况 例如:TAJM40625400:param bill_number::return:"""html_data = response_json["data"]  # 提取htmlsoup = BeautifulSoup(html_data, "html.parser")  # 解析 HTMLbl_number = soup.find("b").texttable1 = soup.find("table")table1_data = []headers = [th.text for th in table1.find("tr", class_="table-header").find_all("th")]rows = table1.find_all("tr", class_="resultrow")for row in rows:cells = row.find_all("td")row_data = {headers[i]: cells[i].get_text(separator=" ").strip() for i in range(len(headers))}table1_data.append(row_data)# 提取第二个表格的数据table2 = soup.find("table", class_="table")table2_data = []headers = [td.text for td in table2.find("thead").find_all("td")]rows = table2.find("tbody").find_all("tr")for row in rows:# todo: 解析还存在点问题 没有适配所有情况 例如: TAJM40625400 就报错cells = row.find_all("td")row_data = {headers[i]: cells[i].get_text(separator=" ").strip() for i in range(len(headers))}table2_data.append(row_data)# 构建最终的 JSON 结构result_json = {"success": response_json["success"],"data": {"bl_number": bl_number,"table1": table1_data,"table2": table2_data}}return result_jsondef get_PIL_related_time(bill_number):"""获取PIL船期时间- 传container_number 原因是为了得到dom树里面更具体的时间;- 只根据提单查询的话,container_number传空字符串'':param bill_number::param container_number::return:"""result_json = get_PIL_tracking_info(bill_number)print("result_json:", result_json)dom_list = result_json['data']for dom in dom_list:# 根据箱号查询的dom树jsonif dom['Event Name'] == "Vessel Discharge" and dom['Event Location'] == "LAGOS (APAPA SEAPORT)":new_eta_format = deal_PIL_eta_date_format(dom['Event Date'])print("------------------------------------------")print(f"提单号【{bill_number}】,船期信息如下:")print("预期到港时间:", new_eta_format)return new_eta_formatdef deal_PIL_eta_date_format(date_str):"""处理PIL日期格式: 将 18-Jan-2025 18:46:00 转化为 2025/1/18 18:46:00- 这里需要加个判断:因为根据箱号查询返回的日期前面会有个前缀,【* 】,我们需去除后再进行转换:param date_str::return:"""try:if "* " in date_str:date_str = date_str.lstrip("* ").strip()dt = datetime.strptime(date_str, "%d-%b-%Y %H:%M:%S")import platformif platform.system() == "Windows":formatted_date = dt.strftime("%Y/%#m/%#d %H:%M:%S")else:formatted_date = dt.strftime("%Y/%-m/%-d %H:%M:%S")return formatted_dateexcept ValueError as e:print("日期时间格式错误:", e)def get_ZIM_access_token():session = requests.session()api_url = "https://www.goldstarline.com/#/track_ship"session.get(api_url)cookies_dict = session.cookies.get_dict()headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36','cookie': "; ".join([f"{k}={v}" for k, v in cookies_dict.items()]),}payload = {'country_code': 'HK'}url = "https://www.goldstarline.com/react-rest//access_token"response = session.post(url, data=payload, headers=headers)return responsedef get_ZIM_tracking_info(tracking_number):"""获取ZIM船司物流信息:param tracking_number:提单号权限校验:必传cookie & authorization- 格式:Bearer 52851984- ZIM接口经常不稳定,当一次调用不成功,可重复再调用即可:return:"""# todo: GOSUNGB20416758 bug调试access_response = get_ZIM_access_token()time.sleep(1)token = access_response.json()['token']cookie = access_response.cookies.get_dict()['ci_session']cookie = "ci_session=" + cookie + ";"authorization = "Bearer " + token# print("cookie:", cookie)# print("authorization:", authorization)session = requests.Session()api_url = "https://www.goldstarline.com/#/track_shipment"session.get(api_url)url = "https://www.goldstarline.com/react-rest//get_track_shipment_val"payload = {"containerid": tracking_number}# cookie 通过测试,只需要拿到ci_session即可headers = {'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",'Content-Type': "application/x-www-form-urlencoded",'cookie': cookie,'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",'authorization': authorization,'accept': "application/json, text/plain, */*",'Cache-Control': "no-cache",}try:response = session.request("POST", url, data=payload, headers=headers)if response.status_code == 200:data = response.json()print("===========================================")print("ZIM船信息:", data)if data['status'] == '0':print("请检查Cookie或authorization是否有效")returnreturn dataelse:print("请求失败,请重新再试")except requests.exceptions.RequestException as e:return f"请求错误: {str(e)}"def deal_ZIM_eta_time_format(date):"""处理ZIM船司ETA时间格式- 将 04-Mar-2025 转换为 2025/3/4:param date::return:"""dt = datetime.strptime(date, "%d-%b-%Y")formatted_date = f"{dt.year}/{dt.month}/{dt.day}"return formatted_datedef get_ZIM_related_time(tracking_number):"""获取ZIM相关船期时间通过观察ZIM查询逻辑: 如果POD里面获取不到值就去取arrivalDateTz - dateTime我们加一层判断:param tracking_number::return:"""data = get_ZIM_tracking_info(tracking_number)try:if data['status'] == '0':print("ZIM船司-提单号查询返回数据为空,请检查提单号是否有误,或者船司还未同步更新")else:estimated_time_of_arrival = data['message']['pod']['estimated_time_of_arrival']if estimated_time_of_arrival == '':arrivalDateTz = data['message']['response']['blRouteLegs'][0]['arrivalDateTz']['dateTime']print("------------------------------------------")print(f"提单号【{tracking_number}】的船期信息如下:")print("预计到港时间:", arrivalDateTz)return arrivalDateTzelse:new_eta_format = deal_ZIM_eta_time_format(estimated_time_of_arrival)print("------------------------------------------")print(f"提单号【{tracking_number}】的船期信息如下:")print("预计到港时间:", new_eta_format)return new_eta_formatexcept requests.exceptions.RequestException as e:return f"请求数据失败: {str(e)}"def get_MSC_tracking_info(tracking_number):"""查询MSC船公司信息:param tracking_number: 提单号/预约号:param tracking_number: 为0根据提单号查询;为1根据预约号查询这里后端直接加个判断:如果传进来的tracking_number的前面4位数不以[MEDU]开头则为置为1;否则默认为0:return: 具体船期时间"""session = requests.Session()api_url = "https://www.msc.com/api/feature/tools/TrackingInfo"headers = {"Content-Type": "application/json","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36","X-Requested-With": "XMLHttpRequest",}if tracking_number[:4] == "MEDU":tracking_mode = "0"  # 提单号查询else:tracking_mode = "1"  # 预约号查询payload = {"trackingNumber": tracking_number,"trackingMode": tracking_mode}try:response = session.post(api_url, json=payload, headers=headers)if response.status_code == 200:data = response.json()print("===========================================")print("MSC船信息:", data)return dataexcept requests.exceptions.RequestException as e:return f"请求错误: {str(e)}"def deal_MSC_ETA_date_format(date):""":param Pod_eta_date: MSC返回日期为15/02/2025 转为2025/2/15:return:"""date_obj = datetime.strptime(date, "%d/%m/%Y")new_date_str = date_obj.strftime("%Y/%m/%d")return new_date_strdef get_MSC_related_time(tracking_number):"""根据单号获取MSC相关时间:param tracking_number: 提单号/预约号:return:"""data = get_MSC_tracking_info(tracking_number)if data is not None:# time.sleep(5)actualShipment_info = data['Data']['BillOfLadings'][0]['ContainersInfo']print("actualShipment_info:", actualShipment_info)PodEtaDate = actualShipment_info[0]['PodEtaDate']if PodEtaDate == '':# 若ETA为空,则输出最近一条物流消息print("===========================================")print(f"该提单【{tracking_number}】目前暂无ETA信息")latest_event = actualShipment_info[0]['Events'][0]  # MSC是倒序的,所以取第一条date = latest_event['Date']PodEtaDate = deal_MSC_ETA_date_format(date)  # 日期格式转换EquipmentHandling = latest_event['EquipmentHandling']['Name']print(f"其最新一条物流消息如下:\n日期:{PodEtaDate},位于 {EquipmentHandling};")else:PodEtaDate = deal_MSC_ETA_date_format(PodEtaDate)  # 日期格式转换print("------------------------------------------")print(f"提单号【{tracking_number}】的船期信息如下:")print("预计到港时间:", PodEtaDate)return PodEtaDateelse:print("请求存在延迟,返回数据为None,需要再次调用")def get_COSCO_tracking_info(tracking_number):"""根据提单号查询COSCO船司物流信息:param bill_of_lading: 目前只能根据提单号查询;预约号无法查询到:return: 返回"""tracking_number = deal_COSCO_bill_number(tracking_number)  # 处理前缀COSUsession = requests.Session()headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36","Accept": "application/json","X-Requested-With": "XMLHttpRequest"}try:# 假设发现的API端点(需要实际分析)api_url = f"https://elines.coscoshipping.com/ebtracking/public/bill/{tracking_number}"print("api_url:", api_url)response = session.get(api_url, headers=headers)if response.status_code == 200:data = response.json()print("===========================================")print("COSCO船信息:", data)return dataelse:print("COSCO船信息查询失败")print("错误返回:", response)returnexcept Exception as e:return f"请求失败: {str(e)}"def deal_COSCO_bill_number(bill_number):"""处理COSCO提单号:COSU6407335642 过滤掉单号前4位前缀因为COSCO只能根据数值查询,添加了前缀无法查询:param bill_number: 提单号:return:"""remove_substring = "COSU"if remove_substring in bill_number:new_bill_number = bill_number.replace(remove_substring, "")else:new_bill_number = bill_numberreturn new_bill_numberdef get_COSCO_related_time(tracking_number):"""获取COSCO单号相关时间:param tracking_number: 提单号:return:"""data = get_COSCO_tracking_info(tracking_number)# COSCO查询逻辑actualShipment_list = data['data']['content']['actualShipment']print("actualShipment_list:", actualShipment_list)len_actualShipment_list = len(actualShipment_list)if len_actualShipment_list == 0:# 若里面只有一条数据,就取这里面的第一条actualShipment_info = actualShipment_list[0]print("actualShipment_info:", actualShipment_info)estimatedDateOfArrival = actualShipment_info['estimatedDateOfArrival']actualArrivalDate = actualShipment_info['actualArrivalDate']actualDischargeDate = actualShipment_info['actualDischargeDate']else:# 否则取list中最后条数据actualShipment_info = actualShipment_list[len_actualShipment_list - 1]print("actualShipment_info:", actualShipment_info)estimatedDateOfArrival = actualShipment_info['estimatedDateOfArrival']actualArrivalDate = actualShipment_info['actualArrivalDate']actualDischargeDate = actualShipment_info['actualDischargeDate']print("------------------------------------------")print(f"提单号【{tracking_number}】的船期信息如下:")print("预计到港时间:", estimatedDateOfArrival)print("实际到港时间:", actualArrivalDate)print("实际卸货时间:", actualDischargeDate)return estimatedDateOfArrival# todo : COSCO船司网站崩了
# get_COSCO_tracking_info("COSU6407335642")def assign_shipper(tracking_number):"""分配对应公司查询 COSCO | ZIM | MSK | MSC | ONE | PIL- 使用前缀判断:param tracking_number::return:"""if type(tracking_number) != "str":tracking_number = str(tracking_number)if "COSU" in tracking_number or tracking_number[0] == '6' or tracking_number[0] == '9':print("===========================================")print("【COSCO船司物流信息查询】")eta_time = get_COSCO_related_time(tracking_number)elif tracking_number[0] == '2':print("===========================================")print("【MSK船司物流信息查询】")eta_time = get_MSK_related_time(tracking_number)elif "GOSU" in tracking_number:print("===========================================")print("【ZIM船司物流信息查询】")eta_time = get_ZIM_related_time(tracking_number)elif "ONEY" in tracking_number:print("===========================================")print("【ONE船司物流信息查询】")eta_time = get_ONE_related_time(tracking_number)elif "MEDU" in tracking_number or tracking_number[0] == '1':print("===========================================")print("【MSC船司物流信息查询】")eta_time = get_MSC_related_time(tracking_number)else:print("===========================================")print("【PIL船司物流信息查询】")eta_time = get_PIL_related_time(tracking_number)return eta_timedef search_tracking_info_by_list(bl_list):"""遍历list查询物流信息:param bl_list: 提单列表:return: eta_time_List 预计到港时间列表"""eta_time_List = []for tracking_number in bl_list:eta_time = assign_shipper(tracking_number)eta_time_List.append(eta_time)return eta_time_Listdef get_latest_eta_time_and_insert_excel():"""1、读取excel里指定船公司的bill2、将通过bill查询返回的各大船司[预期到港时间]组成一个list3、在原excel里新增【new_ETA】列,并将最新查询到的【预期到港时间】写入- ZIM查询暂不稳定,所以先不写进去:return:"""sheet_name = "Sheet1"file_path = "./update_eta_time_client_list/update_eta_time_list.xlsx"df = pd.read_excel(file_path, sheet_name=sheet_name)bill_list = df["B/L"].to_list()print("bill_list", bill_list)eta_time_list = search_tracking_info_by_list(bill_list)print("eta_time_list:", eta_time_list)new_column_name = "new_ETA"  # 新增新ETA列new_loc = df.columns.get_loc('ETA') + 1df.insert(loc=new_loc, column=new_column_name, value=eta_time_list)  # 插入print("df:", df)df.to_excel(file_path, index=False)def get_local_time():"""获取本地日期 - 格式:2025/2/10:return:"""local_time = datetime.now()formatted_date = local_time.strftime('%Y/%#m/%#d')return formatted_datedef get_MSK_shipment_detail(bill_number):"""获取MSK物流详情:param bill_number::return: 返回符合excel表头的数据list"""response = get_MSK_tracking_info(bill_number)date = get_local_time()shipping = "MSK"index = 1bl_number = bill_numberorigin = response['origin']['city']destination = response['destination']['city']eta = get_MSK_related_time(bill_number)container_list = response['containers']telex_status = "N"data_list = []for container in container_list:print("container:", container)container_number = container['container_num']container_size = container['container_size']container_size_status = 1if container_size == '20':data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", container_size_status, origin, destination]elif container_size == '40':data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", container_size_status, "", origin, destination]data_list.append(data_total)index = index + 1print(f"MSK船司:共插入【{len(data_list)}】条数据")print("MSK船司新插入数据list:", data_list)return data_listdef get_COSCO_shipment_detail(bill_number):"""获取COSCO物流跟踪详情:param bill_number::return: list | 尺寸未填写"""response = get_COSCO_tracking_info(bill_number)date = get_local_time()content = response['data']['content']shipping = "COSCO"index = 1bl_number = bill_numberorigin = content['actualShipment'][0]['portOfLoading']destination = content['actualShipment'][0]['portOfDischarge']eta = get_COSCO_related_time(bill_number)telex_status = "N"data_list = []container_list = content['cargoTrackingContainer']for container_info in container_list:container_number = container_info['cntrNum']# print("container_info:", container_info)# print("ContainerNumber:", container_number)data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", "", origin, destination]data_list.append(data_total)index = index + 1print(f"COSCO船司:共插入【{len(data_list)}】条数据")print("COSCO船司新插入数据list:", data_list)return data_listdef get_MSC_shipment_detail(bill_number):"""获取MSC船司物流详细信息:param bill_number::return: list"""response = get_MSC_tracking_info(bill_number)bill_of_ladings = response['Data']['BillOfLadings'][0]print(bill_of_ladings)date = get_local_time()shipping = "MSC"index = 1bl_number = bill_numberorigin = bill_of_ladings['GeneralTrackingInfo']['ShippedFrom']destination = bill_of_ladings['GeneralTrackingInfo']['ShippedTo']eta = get_MSC_related_time(bill_number)container_list = bill_of_ladings['ContainersInfo']telex_status = "N"data_list = []for container_info in container_list:container_number = container_info['ContainerNumber']container_size = container_info['ContainerType']container_size_status = 1print("container_info:", container_info)print("ContainerNumber:", container_number)if container_size == "20' HIGH CUBE":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", container_size_status, origin, destination]elif container_size == "40' HIGH CUBE":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", container_size_status, "", origin, destination]data_list.append(data_total)index = index + 1print(f"MSC船司:共插入【{len(data_list)}】条数据")print("MSC船司新插入数据list:", data_list)return data_listdef get_ONE_shipment_detail(bill_number):"""获取ONE船司物流详情:param bill_number::return:"""response = get_ONE_tracking_info_by_bill_number(bill_number)bill_of_ladings = response['list']date = get_local_time()shipping = "ONE"index = 1bl_number = bill_number# origin = bill_of_ladings['GeneralTrackingInfo']['ShippedFrom']destination = bill_of_ladings[0]['placeNm']eta = get_ONE_related_time(bill_number)telex_status = "N"data_list = []for container_info in bill_of_ladings:container_number = container_info['cntrNo']container_size = container_info['cntrTpszNm']container_size_status = 1if container_size == "20'DRY HC.":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", container_size_status, "", destination]elif container_size == "40'DRY HC.":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", container_size_status, "", "", destination]data_list.append(data_total)index = index + 1print(f"ONE船司:共插入【{len(data_list)}】条数据")print("ONE船司新插入数据list:", data_list)return data_listdef get_ZIM_shipment_detail(bill_number):"""获取ZIM船司物流详情:param tracking_number::return: list"""response = get_ZIM_tracking_info(bill_number)blRouteLegs = response['message']['response']['blRouteLegs']container_list = response['message']['result']print("blRouteLegs:", blRouteLegs)date = get_local_time()shipping = "ZIM"index = 1bl_number = bill_numberorigin = blRouteLegs[0]['portNameFrom']destination = blRouteLegs[0]['portNameTo']eta = get_ZIM_related_time(bill_number)telex_status = "N"data_list = []for container_info in container_list:str = container_info['container']parts = str.split()  # 自动合并多个空格/制表符等空白字符part1, part2 = parts[0], parts[1]container_number = part1container_size = part2# print("container_number:", container_number)# print("container_size:", container_size)container_size_status = 1if container_size == "HC20":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", container_size_status, origin, destination]elif container_size == "HC40":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", container_size_status, "", origin, destination]data_list.append(data_total)index = index + 1print(f"ZIM船司:共插入【{len(data_list)}】条数据")print("ZIM船司新插入数据list:", data_list)return data_listdef get_PIL_shipment_detail(bill_number):"""获取PIL船司物流详情# todo: 解析方法需优化,适应各种情况;目前解析只默认拿取第一条柜子数据:param tracking_number::return: list"""response = get_PIL_tracking_info_by_bill_number(bill_number)print("PIL船信息:", response)data = response['data']date = get_local_time()shipping = "PIL"index = 1bl_number = bill_numbercontainer_list = data['table2']origin = container_list[0]['Place']str = data['table1'][0]['Next Location']parts = str.split()  # 自动合并多个空格/制表符等空白字符part1, part2 = parts[0], parts[1]destination = part1eta = get_PIL_related_time(bill_number)telex_status = "N"data_list = []for container_info in container_list:str = container_info['Container #']parts = str.split()  # 自动合并多个空格/制表符等空白字符part1, part2 = parts[0], parts[1]container_number = part1container_size = container_info['Size/Type']print("container_number:", container_number)print("container_size:", container_size)container_size_status = 1if container_size == "20GP":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", "", container_size_status, origin, destination]elif container_size == "40GP":data_total = [index, date, "", shipping, "", bl_number, "", container_number, date, eta,telex_status, "", "", "", container_size_status, "", origin, destination]data_list.append(data_total)index = index + 1print(f"PIL船司:共插入【{len(data_list)}】条数据")print("PIL船司新插入数据list:", data_list)return data_listdef insert_ship_detail_to_excel(tracking_number):"""将提单物流详情自动插入到excel中:param data_total: 数据list:param headers: 表头 - 这里我按需求写死就行:return:"""if type(tracking_number) != "str":tracking_number = str(tracking_number)if "COSU" in tracking_number or tracking_number[0] == '6' or tracking_number[0] == '9':print("===========================================")data_total = get_COSCO_shipment_detail(tracking_number)elif tracking_number[0] == '2':print("===========================================")data_total = get_MSK_shipment_detail(tracking_number)elif "GOSU" in tracking_number:print("===========================================")data_total = get_ZIM_shipment_detail(tracking_number)elif "ONEY" in tracking_number:print("===========================================")data_total = get_ONE_shipment_detail(tracking_number)elif "MEDU" in tracking_number or tracking_number[0] == '1':print("===========================================")data_total = get_MSC_shipment_detail(tracking_number)else:print("===========================================")data_total = get_PIL_shipment_detail(tracking_number)headers = ["Index", "Date", "Client", "Shipping","Goods", "B/L", "提单确认", "Container No.", "COPY件","ETA", "电放", "电放日期", "B/L2", "正本接收时间","40Q", "20Q", "Discharge", "Destination"]file_path = "./update_eta_time_client_list/auto_insert_new_bill_info.xlsx"if not Path(file_path).exists():pd.DataFrame(columns=headers)  # 不存在文件, 创建表头else:df = pd.read_excel(file_path)  # 读取现有数据new_df = pd.DataFrame(data_total, columns=headers)combined_df = pd.concat([df, new_df], ignore_index=True)  # 合并新旧数据combined_df.to_excel(file_path, index=False)  # 不保留索引列print(f"数据已追加至 {file_path}")def insert_ship_detail_list_to_excel(bl_list):"""批量插入物流信息到excel:param bl_list::return:"""for bill_number in bl_list:insert_ship_detail_to_excel(bill_number)# assign_shipper("GOSUNGB20516257") # 单个查询eta时间 - 自行判断哪个船司
# assign_shipper("COSU6404907671")
# assign_shipper("COSU9500786880")# get_latest_eta_time_and_insert_excel()  # 获取最新eta时间并写入excelbl_list = ["COSU6405220122","COSU6405220123","COSU6405220124"
]
insert_ship_detail_list_to_excel(bl_list)  # 批量插入数excel数据
# insert_ship_detail_to_excel("COSU6405220124")  # 自动将船司查询到的信息按规定表头写入excel表格
# insert_ship_detail_to_excel("COSU6405220124")  # 自动将船司查询到的信息按规定表头写入excel表格# bl_list = [
#     "MEDUKT219129",
#     "MEDUKT219137",
#     "MEDUKT297182"
#
# ]
# search_tracking_info_by_list(bl_list)  # 批量查询eta时间,传list# OOCL / CMA /HPL 加密,暂时无法破解;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/895354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于微信小程序的博物馆预约系统的设计与实现

hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…

深度学习框架TensorFlow怎么用?

大家好呀,以下是使用 TensorFlow 的详细步骤,从安装到构建和训练模型: 一、安装 TensorFlow 安装 Python:TensorFlow 基于 Python,确保已安装 Python(推荐 Python 3.8 及以上版本)。可通过 Pyt…

如何在华为harmonyOS上调试软件

1、设置-》关于手机-》HarmonyOS 版本连按多下,输入锁屏密码。显示开发者模式已打开。 2、设置-》搜索“开发人员选项”-》开启“开发人员选项”选项。 3、在 开发者选项 中找到 “USB 调试” 并开启。 4、开启 “仅充电时允许 ADB 调试”。 5、设置中开启 &quo…

fpga系列 HDL:Quartus II JTAG 间接配置文件 Indirect Configuration File (.jic) AS模式烧录

先编译生成pof文件 File->Convert Programming Files 转换文件 Tools->Programer 烧录

Python:凯撒密码

题目内容: 凯撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法,它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符,对应关系如下: 原文:A B C D E F G H I J K L M N O P Q R …

【大模型知识点】什么是KV Cache?为什么要使用KV Cache?使用KV Cache会带来什么问题?

1.什么是KV Cache?为什么要使用KV Cache? 理解此问题,首先需理解自注意机制的计算和掩码自注意力机制,在Decoder架构的模型中,每生成一个新的token,便需要重新执行一次自注意力计算,这个过程中…

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式 在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置 前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作 这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统 【STM32】通过…

本地生活服务平台开发进入发展热潮

本地生活服务平台:当下的发展热潮 本地生活服务平台开发模式 在当今数字化时代,本地生活服务平台开发已成为人们日常生活中不可或缺的一部分。只需动动手指,打开手机上的 APP,就能轻松满足各类生活需求。像某团、饿XX这样的平台&a…

LSTM变种模型

GRU GRU简介 门控循环神经网络 (Gated Recurrent Neural Network,GRNN) 的提出,旨在更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可学习的门来控制信息的流动。其中,门控循环单元 (Gated Recurrent Unit , GRU) 是…

微服务与网关

什么是网关 背景 单体项目中,前端只用访问指定的一个端口8080,就可以得到任何想要的数据 微服务项目中,ip是不断变化的,端口是多个的 解决方案:网关 网关:就是网络的关口,负责请求的路由、转发…

二分算法篇:二分答案法的巧妙应用

二分算法篇:二分答案法的巧妙应用 那么看到二分这两个字想必我们一定非常熟悉,那么在大学期间的c语言的教学中会专门讲解二分查找,那么我们来简单回顾一下二分查找算法,我们知道二分查找是在一个有序的序列中寻找一个数在这个序列…

C# OpenCV机器视觉:模仿Halcon各向异性扩散滤波

在一个充满创意与挑战的图像处理工作室里,阿强是一位热情的图像魔法师。他总是在追求更加出色的图像效果,然而,传统的图像处理方法有时候并不能满足他的需求。 有一天,阿强听说了 Halcon 中的各向异性扩散滤波功能,它…

实现:多活的基础中间件

APIRouter : 路由分发服务 API Router 是一个 HTTP 反向代理和负载均衡器,部署在公有云中作为 HTTP API 流量的入口,它能识别 出流量的归属 shard ,并根据 shard 将流量转发到对应的 ezone 。 API Router 支持多种路由键&am…

DeepSeek本地化部署

DeepSeek本地化部署 本教程为一键式部署,适合于mac、ubuntu、windows。【开源地址】 环境要求 nodejs > 18Python > 3.10.12 步骤一:安装ollama客户端 官网直接安装,ollama官网。安装完成后使用命令:ollama -h&#xf…

大数据与大模型:数字时代的共生力量

引言:大数据与大模型的崭新时代 在数字化浪潮汹涌澎湃的当下,大数据与大模型无疑是最为耀眼的两颗明星 ,深刻地改变着我们的生活、工作和思维方式。大数据,作为信息时代的宝藏,蕴含着无尽的价值。从电商平台的海量交易…

[2025年最新]2024.3版本idea无法安装插件问题解决

背景 随着大模型的持续发展,特别年前年后deepseek的优异表现,编程过程中,需要解决ai来辅助编程,因此需要安装一些大模型插件 问题描述 在线安装插件的时候会遇到以下问题: 1.数据一直在加载,加载的很满 2.点…

自动驾驶---如何打造一款属于自己的自动驾驶系统

在笔者的专栏《自动驾驶Planning决策规划》中,主要讲解了行车的相关知识,从Routing,到Behavior Planning,再到Motion Planning,以及最后的Control,笔者都做了相关介绍,其中主要包括算法在量产上…

三角拓扑聚合优化器TTAO-Transformer-BiLSTM多变量回归预测(Maltab)

三角拓扑聚合优化器TTAO-Transformer-BiLSTM多变量回归预测(Maltab) 完整代码私信回复三角拓扑聚合优化器TTAO-Transformer-BiLSTM多变量回归预测(Maltab) 一、引言 1、研究背景和意义 在现代数据科学领域,时间序列…

Jenkins+gitee 搭建自动化部署

Jenkinsgitee 搭建自动化部署 环境说明: 软件版本备注CentOS8.5.2111JDK1.8.0_211Maven3.8.8git2.27.0Jenkins2.319最好选稳定版本,不然安装插件有点麻烦 一、安装Jenkins程序 1、到官网下载相应的版本war或者直接使用yum安装 Jenkins官网下载 直接…

AI 编程开发插件codeium Windsurf(vscode、editor) 安装

1、vscode中安装: 2、vscode中使用 3、输入注册的账号密码,就可以使用。 4、或者直接下载editor 5、安装editor 下一步,下一步,直到安装成功,中间可以改下安装位置,如果C盘空间不够。 同样提示注册或者登录…