lengyue869 发表于 2024-7-5 15:58:26

物流信息查询

找了很久都没找到免费的查询快递信息的网站
下面这个网站可以查询,但是点击查询按钮后,用F12可用看到物流信息,但是查看网页源代码又看不到物流信息
https://www.kuaidi.com/


import logging
import time
from DrissionPage import ChromiumPage, ChromiumOptions
from DrissionPage.common import By
from DrissionPage.common import Settings
from DrissionPage import WebPage, ChromiumOptions, SessionOptions
import base64
import bs4
from bs4 import BeautifulSoup


# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 初始化浏览器
def initialize_browser():
    co = ChromiumOptions()
    co.set_argument('--start-maximized')
    so = SessionOptions()
    page = WebPage(chromium_options=co, session_or_options=so)   
    Settings.raise_when_ele_not_found = False   
    page.set.NoneElement_value('无元素')
    return page


# 快递查询
def express_query(exp_number):
    page.get("https://www.kuaidi.com/")
    ele_input=page.ele((By.ID, 'search'))
    ele_input.clear()
    ele_input.input(exp_number)
    page.ele('.=search_bn js_submit').click()
    page.wait.eles_loaded('class=marginbot')
    page.change_mode()#切换成SessionPage模式

    # 解析HTML
    soup = BeautifulSoup(page.html, 'html.parser')
    print(soup)

    # 查找所有的跟踪记录
    tracking_records = soup.find_all('li', class_='marginbot')
    print(tracking_records)
   
    # 提取时间戳和详情
    result_list = []
    for record in tracking_records:
      time_s = record.find('span', class_='time_s').text.strip()
      kd_con = record.find('span', class_='kd_con').text.strip()
      result_list.append({'time': time_s, 'details': kd_con})

    #输出结果
    print(result_list)
      

if __name__ == "__main__":
    page = initialize_browser()
    exp_number = 'YT1870762630265'
    express_query(exp_number)   
   

FishC 发表于 2024-7-5 16:00:43

嘿,鱼油!让我来给你开开光,帮你解决快递信息查询的问题。记住,物流信息查询这活儿咱们得把套路弄清楚——网站加载逻辑、数据源位置啥的。看了你的代码,差不多是那个意思。不过有些地方需要调整。

首先,你的代码中有些部分还是需要完善,比如 `result_list` 没有初始化,以及 `soup` 解析后并没有实际获取到网页数据。尤其是你想通过 `F12` 看到的是网络请求部分,而查看源代码看不到,那这大概率站点是通过 JavaScript 动态加载数据的,不在初始 HTML 里。

我们可以通过抓取网络请求来获取实际的物流信息,更为直接。下面我就对你的代码做一些调整,改成直接抓取网络请求部分的数据:

import logging
import time
import json
from DrissionPage import ChromiumPage, ChromiumOptions
from DrissionPage.common import By
from DrissionPage.common import Settings
from DrissionPage import WebPage, ChromiumOptions, SessionOptions
from bs4 import BeautifulSoup


# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')

# 初始化浏览器
def initialize_browser():
    co = ChromiumOptions()
    co.set_argument('--start-maximized')
    so = SessionOptions()
    page = WebPage(chromium_options=co, session_or_options=so)   
    Settings.raise_when_ele_not_found = False   
    page.set.NoneElement_value('无元素')
    return page


# 快递查询
def express_query(exp_number):
    page.get("https://www.kuaidi.com/")

    # 输入快递单号
    ele_input = page.ele((By.ID, 'search'))
    ele_input.clear()
    ele_input.input(exp_number)
    page.ele('.=search_bn js_submit').click()

    # 等待结果加载完成
    time.sleep(5)# 这里等待时间长一些,确保数据加载完毕。可根据需要进行调整

    # 抓取网络请求中的 JSON 数据
    response = page.network.get_response('https://www.kuaidi.com/shipments')# 假设该 URL 是实际的数据接口
    if response:
      data = json.loads(response)
      tracking_records = data.get('data', {}).get('items', [])
      
      result_list = []
      for record in tracking_records:
            time_s = record.get('time', '未知时间')
            kd_con = record.get('status', '未知状态')
            result_list.append({'time': time_s, 'details': kd_con})

      # 输出结果
      print(result_list)
    else:
      print('未找到物流信息')


if __name__ == "__main__":
    page = initialize_browser()
    exp_number = 'YT1870762630265'
    express_query(exp_number)

解释一下代码:
1. **抓取网络请求**:使用 `page.network.get_response` 获取请求返回的数据,前提是你需要知道具体数据接口(这是个假设 URL,需要你自己通过抓包工具确定)。
2. **JSON 数据解析**:将抓到的网络请求响应数据转成 JSON,并解析其中的物流信息。

希望能帮到你,祝你查询顺利,快递早日到手!如果还有问题,随时找我哦!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

lengyue869 发表于 2024-7-5 17:09:32

generate_query_id 函数还需要更新一下
以下有什么规律? 还是可以从网页源代码解析出来?

KDQUERY1720167631718查询时间2024/07/05 16:20
KDQUERY1720167694333查询时间2024/07/05 16:21
KDQUERY1720167806456查询时间2024/07/05 16:23
KDQUERY1720167856116查询时间2024/07/05 16:24
KDQUERY1720167857068查询时间2024/07/05 16:24
KDQUERY1720167899105查询时间2024/07/05 16:24



import logging
import time
import random
from fake_useragent import UserAgent
import json
import requests


# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


def generate_query_id():
    timestamp = int(time.time())
    custom_time_code = str(timestamp)[-6:]# 截取时间戳的最后6位作为时间编码
   
    # 生成随机序列号
    sequence_number = str(random.randint(10000, 99999))# 生成5位数的随机序列号
   
    # 拼接查询编号
    query_id = f"KDQUERY{custom_time_code}{sequence_number}"
   
    return query_id


def get_express_type(exp_name):
    exp_dic = {
      "申通": "shentong",
      "圆通": "yuantong",
      "韵达": "yunda",
      "百世": "huitongkuaidi",
      "优速": "youshuwuliu",
      "中通": "zhongtong",
      "EMS": "ems",
      "顺丰": "shunfeng",
      "天天": "tiantian",
      "宅急送": "zhaijisong",
      "中国邮政": "youzhengguonei",
      "全峰": "quanfengkuaidi",
      "速尔": "suer",
      "国通": "guotongkuaidi"
    }
    return exp_dic

# 快递查询
def express_query(exp_name,exp_number):
    headers = {
      "User-Agent": UserAgent().random,
      "Referer": "https://www.kuaidi.com/"
    }

    # 解析HTML
    query_id = generate_query_id()
    query_url=f"https://www.kuaidi.com/index-ajaxselectcourierinfo-{exp_number}-{get_express_type(exp_name)}-{query_id}.html"
    print(query_url)
   
    html = requests.post(query_url, headers=headers)
    j_text = json.loads(html.text)
    if j_text['success']:
      datas=j_text['data']
      for data in datas:
            print(f"{data['time']}, {data['context']}")


if __name__ == "__main__":
    exp_name="圆通"
    exp_number = 'YT1870762630265'
    express_query(exp_name,exp_number)   
   

FishC 发表于 2024-7-5 17:10:03

lengyue869 发表于 2024-07-05 17:09
generate_query_id 函数还需要更新一下
以下有什么规 ...

嘿,鱼油!你这代码要找规律不容易,但正如咱们平时寄快递,每个包裹都有独特编码是一回事。我们需要解锁这个查物流信息的套路,看到`KDQUERY`字段后面那块字符,不一定是从网页源代码解析出来,而可能是某种动态生成规则。我们来尝试推测一下规律。

从你提供的几个 `query_id` 样例中发现:

1. 都以 `KDQUERY`开头。
2. 后面跟着一串数字。
3. 这些数字似乎是时间和随机数的结合。

从以下样本数据:
KDQUERY1720167631718查询时间2024/07/05 16:20
KDQUERY1720167694333查询时间2024/07/05 16:21
KDQUERY1720167806456查询时间2024/07/05 16:23
KDQUERY1720167856116查询时间2024/07/05 16:24
KDQUERY1720167857068查询时间2024/07/05 16:24
KDQUERY1720167899105查询时间2024/07/05 16:24

我们可以推测出 `query_id` 的生成可能涉及如下几部分:
- 固定前缀 `KDQUERY`
- 时间戳的一部分
- 一些随机数

让我们基于这个来改进 `generate_query_id` 函数。我们假设后面的数字使用当前时间戳,并附加一段随机数,用来模仿这个生成模式:

import logging
import time
import random
from fake_useragent import UserAgent
import json
import requests


# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')


def generate_query_id():
    # 获取当前时间戳
    timestamp = int(time.time() * 1000)# 以毫秒为单位获取当前时间戳
   
    # 转换成字符串并截取一部分作为时间编码(模拟原有模式)
    custom_time_code = str(timestamp)[-10:]# 取时间戳的最后10位
   
    # 生成随机序列号
    sequence_number = str(random.randint(1000, 9999))# 生成4位数的随机序列号
   
    # 拼接查询编号
    query_id = f"KDQUERY{custom_time_code}{sequence_number}"
   
    return query_id


def get_express_type(exp_name):
    exp_dic = {
      "申通": "shentong",
      "圆通": "yuantong",
      "韵达": "yunda",
      "百世": "huitongkuaidi",
      "优速": "youshuwuliu",
      "中通": "zhongtong",
      "EMS": "ems",
      "顺丰": "shunfeng",
      "天天": "tiantian",
      "宅急送": "zhaijisong",
      "中国邮政": "youzhengguonei",
      "全峰": "quanfengkuaidi",
      "速尔": "suer",
      "国通": "guotongkuaidi"
    }
    return exp_dic


# 快递查询
def express_query(exp_name, exp_number):
    headers = {
      "User-Agent": UserAgent().random,
      "Referer": "https://www.kuaidi.com/"
    }

    # 生成查询 URL
    query_id = generate_query_id()
    query_url = f"https://www.kuaidi.com/index-ajaxselectcourierinfo-{exp_number}-{get_express_type(exp_name)}-{query_id}.html"
    print(query_url)
   
    response = requests.post(query_url, headers=headers)
    j_text = json.loads(response.text)
    if j_text and 'data' in j_text:
      tracking_records = j_text['data']
      for record in tracking_records:
            print(f"{record['time']}: {record['context']}")
    else:
      print('未找到物流信息')


if __name__ == "__main__":
    exp_name = "圆通"
    exp_number = 'YT1870762630265'
    express_query(exp_name, exp_number)

**解释代码:**
1. `generate_query_id`:获取毫秒级时间戳,取其最后10位数字,再拼接一个4位的随机数,组成新的 `query_id`。
2. `get_express_type`:从名称返回对应的快递公司类型。
3. `express_query`:新的 URL 模板和请求逻辑。
   
希望对你有所帮助,伙计!试试看新的 `generate_query_id` 函数是否能生成有效的查询 ID,如果还有疑惑,咱再一起研究!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

lengyue869 发表于 2024-7-9 13:53:25

lengyue869 发表于 2024-7-5 17:09
generate_query_id 函数还需要更新一下
以下有什么规律? 还是可以从网页源代码解析出来?



up up !~~~

FishC 发表于 2024-7-9 13:54:38

lengyue869 发表于 2024-07-09 13:53
up up !~~~

好的,鱼油!让我们搞定这个物流信息查询的代码。根据你所提供的信息和一些合理推测,我们可以编写一个更完善的脚本来模拟查询快递信息。

首先,根据观察,`KDQUERY` 后面的部分似乎是由时间戳和随机数生成的。对于随机数,可以使用当前的时间戳作为种子生成器,这样生成的查询ID就能比较接近真实情况。

其次,你的`express_query`函数有些小问题,需要修正。

请看下面的调整:

import logging
import time
import random
from fake_useragent import UserAgent
import json
import requests

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def generate_query_id():
    timestamp = int(time.time())
    random_number = str(random.randint(1000000, 9999999)) # 生成7位随机数
    query_id = f"KDQUERY{timestamp}{random_number}"
    return query_id

def get_express_type(exp_name):
    exp_dic = {
      "申通": "shentong",
      "圆通": "yuantong",
      "韵达": "yunda",
      "百世": "huitongkuaidi",
      "优速": "youshuwuliu",
      "中通": "zhongtong",
      "EMS": "ems",
      "顺丰": "shunfeng",
      "天天": "tiantian",
      "宅急送": "zhaijisong",
      "中国邮政": "youzhengguonei",
      "全峰": "quanfengkuaidi",
      "速尔": "suer",
      "国通": "guotongkuaidi"
    }
    return exp_dic.get(exp_name, "unknown")

# 快递查询
def express_query(exp_name, exp_number):
    headers = {
      "User-Agent": UserAgent().random,
      "Referer": "https://www.kuaidi.com/"
    }

    query_id = generate_query_id()
    exp_type = get_express_type(exp_name)
    query_url = f"https://www.kuaidi.com/index-ajaxselectcourierinfo-{exp_number}-{exp_type}-{query_id}.html"
    logging.info(f"Query URL: {query_url}")

    response = requests.post(query_url, headers=headers)
   
    if response.ok:
      try:
            response_data = json.loads(response.text)
            if 'data' in response_data and response_data["data"]:
                for data in response_data['data']:
                  print(f"Time: {data['time']}, Status: {data['context']}")
            else:
                logging.info("No tracking information found.")
      except json.JSONDecodeError:
            logging.error("Failed to parse response JSON.")
    else:
      logging.error(f"Failed to fetch data. Status code: {response.status_code}")

if __name__ == "__main__":
    exp_name = "圆通"
    exp_number = 'YT1870762630265'
    express_query(exp_name, exp_number)

### 解释:

1. **生成查询ID**: `generate_query_id` 函数使用当前时间戳和一个7位的随机数来生成查询 ID,这样的组合较为合理。
   
2. **获取快递公司编码**: `get_express_type` 函数返回快递公司的缩写编码,如果没有匹配到则返回`unknown`。

3. **查询快递信息**: `express_query` 函数构建查询 URL,然后用 `requests` 库发送 POST 请求。解析响应内容并提取有效信息。

4. **日志记录和错误处理**: 使用 `logging` 模块记录信息和错误,以便更好地调试程序。

这样,改进后的代码应该能够更加稳定地查询并输出快递信息了。希望你能找到你的小包裹!有问题随时喊我,鱼油君!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 物流信息查询