基于 AI 的日常新闻 收集/处理/判断 有几种玩法?

3,473 阅读8分钟

一. 前言

近期一直在尝试实现一个功能 ,做了很多尝试后 ,有一些零零散散的记录 ,这里拿出来给大家分享一下。

二. 数据收集的几种维度

2.1 Python 脚本爬取网页新闻数据 (常规方式)

在 AI 还没发展出来 ,一般可以通过新闻聚合提供的接口 或者 爬虫 来拉取消息数据。

其中新闻聚合在 AI 出现之后 ,逐渐转变成支持组件 ,为 AI 智能体提供 MCP 服务。 比如博查/聚合数据这类。

而爬虫这东西, 具有一定的风险 ,可能涉及到违法,这里 Demo 就很简单的说说 :

  • 常用的组件 :
    • requests:发送HTTP请求抓取网页内容
    • beautifulsoup4:解析网页HTML,提取新闻信息
    • fake_useragent(可选):动态伪装请求头,避免被服务器拒绝

import requests  
from bs4 import BeautifulSoup  
from fake_useragent import UserAgent  
import time  
from urllib.parse import urljoin  

def log(msg):  
    print(f"[LOG] {msg}")  

def fetch_sina_finance_news():  
    # 某财经网站作为案例
    base_url = "https://xxx.xxx.xxx.cn/stock/"  
    log(f"开始访问 {base_url}")  

    headers = {  
        "User-Agent": UserAgent().random  
    }  

    try:  
        response = requests.get(base_url, headers=headers, timeout=10)  
        response.raise_for_status()  
        log("网页请求成功")  
    except Exception as e:  
        log(f"请求失败: {e}")  
        return  

    soup = BeautifulSoup(response.content, "html.parser")  

    # 找到第一个<div class="tabs-cont sto_cont0">对应要闻板块  
    news_container = soup.find("div", class_="tabs-cont sto_cont0")  
    if not news_container:  
        log("未找到要闻内容区,网页结构可能已变更")  
        return  

    # 找到里面第一个<ul class="list01" data-client="scroll">就是要闻新闻列表  
    news_list = news_container.find("ul", class_="list01", attrs={"data-client": "scroll"})  
    if not news_list:  
        log("未找到要闻新闻列表")  
        return  

    news_items = news_list.find_all("li", recursive=False)  # 只查一级li,避免抓取嵌套的  

    if not news_items:  
        log("新闻列表为空")  
        return  

    log(f"共找到 {len(news_items)} 条新闻,开始提取标题和链接")  

    for index, li in enumerate(news_items, 1):  
        a_tag = li.find("a")  
        if not a_tag:  
            log(f"第{index}条新闻无链接,跳过")  
            continue  
        title = a_tag.get_text(strip=True)  
        href = a_tag.get("href")  
        full_url = urljoin(base_url, href)  # 完整url拼接  
        print(f"{index}. {title}\n   链接: {full_url}")  

    log("新闻提取完成")  

if __name__ == "__main__":  
    fetch_sina_finance_news()  


image.png

阶段总结 :

  • 使用爬虫可以达到目的 ,但是 开发成本比较高 ,高度 依赖目标网站 的特性
  • 爬虫获取的数据比较原始 ,需要 进行比较复杂的二次分析 ,才能获取最终效果
  • 爬虫具有一定的 法律风险 ,可能会带来不必要的麻烦,使用要慎重

2.2 聚合平台获取数据 / 供 AI 使用的基础数据 (组件依赖)

如果规模比较大 ,一般就会考虑通过聚合平台来获取数据了 ,聚合平台会把各平台的数据进行聚合 ,只需要调用接口就能获取对应的新闻信息 : ( 案例平台 : 博查AI开放平台 | Search API, Reranker API)

from typing import List, Optional, Dict, Any
from dataclasses import asdict, dataclass, field
import requests  



@dataclass
class WebPageItem:
    id: Optional[str] = None
    name: Optional[str] = None
    url: Optional[str] = None
    displayUrl: Optional[str] = None  # 修改为匹配 JSON 中的键名
    snippet: Optional[str] = None
    siteName: Optional[str] = None  # 修改为匹配 JSON 中的键名
    siteIcon: Optional[str] = None  # 修改为匹配 JSON 中的键名
    datePublished: Optional[str] = None  # 修改为匹配 JSON 中的键名
    dateLastCrawled: Optional[str] = None  # 修改为匹配 JSON 中的键名
    cachedPageUrl: Optional[str] = None  # 修改为匹配 JSON 中的键名
    language: Optional[str] = None
    isFamilyFriendly: Optional[bool] = None
    isNavigational: Optional[bool] = None


@dataclass
class ImageItem:
    webSearchUrl: Optional[str] = None  # 修改为匹配 JSON 中的键名
    name: Optional[str] = None
    thumbnailUrl: Optional[str] = None  # 修改为匹配 JSON 中的键名
    datePublished: Optional[str] = None
    contentUrl: Optional[str] = None
    hostPageUrl: Optional[str] = None
    contentSize: Optional[str] = None
    encodingFormat: Optional[str] = None
    hostPageDisplayUrl: Optional[str] = None
    width: Optional[int] = None
    height: Optional[int] = None
    thumbnail: Optional[str] = None    


@dataclass
class SearchResponse:
    code: int
    log_id: str
    msg: Optional[str] = None
    data: Dict[str, Any] = field(default_factory=dict)
    web_pages: List[WebPageItem] = field(default_factory=list)
    images: List[ImageItem] = field(default_factory=list)

    def __post_init__(self):
        if self.data and 'webPages' in self.data:
            self.web_pages = [WebPageItem(
                **page) for page in self.data['webPages'].get('value', [])]
        if self.data and 'images' in self.data:
            self.images = [ImageItem(**image)
                           for image in self.data['images'].get('value', [])]

class WebSearchClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.bochaai.com/v1/web-search"
        print(f"初始化 WebSearchClient,API端点: {self.base_url}")

    def search(self, query):
        """通过 Web Search API 查询网络内容"""
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        payload = {
            "query": query,
            "freshness": "oneDay",
            "summary": "false",
            "count": 10
        }

        print(f"发起搜索查询:{query}")

        try:
            response = requests.post(
                self.base_url,
                headers=headers,
                json=payload
            )
            response.raise_for_status()
            results = response.json()

            # 使用 DTO 对象处理结果
            search_response = SearchResponse(**results)
            print(f"搜索成功,返回结果数量:{len(search_response.web_pages)}")
            return search_response

        except requests.exceptions.RequestException as e:
            print(f"请求失败: {e}")
            return None
        
def main():
    # API 密钥
    api_key = "sk-11111"

    # 预定义查询关键词
    query = "当天和经济金融股票相关的新闻"

    web_search = WebSearchClient(api_key)

    print(f"开始查询:{query}")
    results = web_search.search(query)
    print(f"查询结果{results}")

if __name__ == "__main__":
    main()


image.png

阶段总结 :

  • 这类聚合平台使用简单 ,数据来源渠道充足 ,开发成本很低
  • 需要一定的支出 , 总体不贵 ,这个是 0.04 一次(平台很多,大家可以自行斟酌
  • 同样是新闻主体 ,还是需要进行二次分析 ,多用于 MCP 外部支援部分 (后面来看)
  • 这里只是演示了最基础的 WebSearch ,通常这种还自带 AI Search 及 Agent Search

2.3 AI 厂商获取新闻数据 (AI玩法)

部分 AI 厂商本身就集成了 WebSearch 的功能 ,只需要直接调用接口 ,就可以快速搜索, 这里就算入门了 ,不再需要额外的分析操作,交给大模型进行处理 :

import os
import json
from typing import Dict, Any, List, Optional

from openai import OpenAI
from openai.types.chat.chat_completion import Choice


class SinaFinanceNewsSearcher:
    def __init__(self, api_key: Optional[str] = None):
        """  
        初始化 SinaFinanceNewsSearcher  

        :param api_key: 可选的 API Key,如果为 None 则尝试从本地或 DataMap 获取  
        """
        self.api_key = self._get_api_key(api_key)

        self.client = OpenAI(
            base_url="https://api.moonshot.cn/v1",
            api_key=self.api_key
        )

        self.initial_messages = [
            {
                "role": "system",
                "content": "你是一个金融工作者,现在要收集每天相关的金融信息,你从新浪财经(https://finance.sina.com.cn/)获取数据。数据的返回通过 JSON 返回给我"
            }
        ]

    def _get_api_key(self, provided_key: Optional[str] = None) -> str:
        """  
        获取 API Key 的优先级方法  

        优先级:  
        1. 直接提供的 API Key  
        2. 本地文件 (moonshot_api_key.txt)  
        3. DataMap 中获取  
        4. 抛出异常  

        :param provided_key: 直接提供的 API Key  
        :return: API Key  
        """
        # 1. 如果直接提供了 API Key,直接返回
        if provided_key:
            return provided_key

        # 2. 尝试从本地文件获取
        local_file_path = os.path.join(
            os.path.dirname(__file__), "moonshot_api_key.txt")
        if os.path.exists(local_file_path):
            with open(local_file_path, "r") as file:
                local_key = file.read().strip()
                if local_key:
                    return local_key

    def _search_impl(self, arguments: Dict[str, Any]) -> Any:
        """  
        搜索实现,目前直接返回参数  

        :param arguments: 搜索参数  
        :return: 搜索结果  
        """
        return arguments

    def search_finance_news(self, query: Optional[str] = None) -> str:
        """  
        搜索金融新闻  

        :param query: 可选的搜索查询,默认为今天的热点新闻  
        :return: 搜索结果内容  
        """
        messages = self.initial_messages.copy()

        # 如果没有提供查询,使用默认查询
        if query is None:
            query = "搜索今天有什么热点新闻"

        messages.append({
            "role": "user",
            "content": query
        })

        finish_reason = None
        while finish_reason is None or finish_reason == "tool_calls":
            choice = self._chat(messages)
            finish_reason = choice.finish_reason

            if finish_reason == "tool_calls":
                messages.append(choice.message)

                for tool_call in choice.message.tool_calls:
                    tool_call_name = tool_call.function.name
                    tool_call_arguments = json.loads(
                        tool_call.function.arguments)

                    if tool_call_name == "$web_search":
                        tool_result = self._search_impl(tool_call_arguments)
                    else:
                        tool_result = f"Error: unable to find tool by name '{tool_call_name}'"

                    messages.append({
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "name": tool_call_name,
                        "content": json.dumps(tool_result)
                    })

        return choice.message.content

    def _chat(self, messages: List[Dict[str, str]]) -> Choice:
        """  
        调用 Moonshot AI 聊天接口  

        :param messages: 消息列表  
        :return: 聊天返回结果  
        """
        completion = self.client.chat.completions.create(
            model="moonshot-v1-128k",
            messages=messages,
            temperature=0.3,
            tools=[
                {
                    "type": "builtin_function",
                    "function": {
                        "name": "$web_search",
                    }
                }
            ]
        )
        return completion.choices[0]


def get_sina_finance_news(api_key: Optional[str] = None, query: Optional[str] = None) -> str:
    """  
    外部调用函数,获取新浪财经新闻  

    :param api_key: 可选的 Moonshot AI 的 API Key  
    :param query: 可选的搜索查询  
    :return: 新闻内容  
    """
    searcher = SinaFinanceNewsSearcher(api_key)
    return searcher.search_finance_news(query)


# 使用示例
if __name__ == "__main__":
    # 三种使用方式:
    # 1. 不提供 API Key,自动从本地或 DataMap 获取
    news1 = get_sina_finance_news(None, "搜索最近的科技股新闻")

    # # 2. 直接提供 API Key
    # news2 = get_sina_finance_news("your-specific-api-key")

    # # 3. 提供 API Key 和具体查询
    # news3 = get_sina_finance_news("your-specific-api-key", "搜索最近的科技股新闻")

    print(news1)
    # print(news2)
    # print(news3)

image.png

阶段总结 :

  • 除了 Kimi ,包括 DeepSeek 等都有完善的 API
  • 费用和查询的结果数量挂钩 ,价格会比单纯的查消息略贵 ,涉及到大模型的计费
  • 整体来说开发难度低 ,适用面广
  • 效果依赖于AI厂商,灵活度不高

2.4 AI 综合平台获取新闻数据 (高度定制 AI)

虽然上面的方式已经足够我们了解到日常的新闻需求 ,但是其更依赖于单个厂商 ,如果需要更灵活的整合 ,就需要综合性的 AI 平台处理了 , 这里我使用的是阿里云的 :

image.png

image.png

Client 端进行查询

import os
from http import HTTPStatus
from dashscope import Application

response = Application.call(
    # 若没有配置环境变量,可用百炼API Key将下行替换为:api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。
    api_key="sk-xxxx",
    app_id='xxxx',# 替换为实际的应用 ID
    prompt='给我当天的热点新闻')

if response.status_code != HTTPStatus.OK:
    print(f'request_id={response.request_id}')
    print(f'code={response.status_code}')
    print(f'message={response.message}')
    print(f'请参考文档:https://help.aliyun.com/zh/model-studio/developer-reference/error-code')
else:
    print(response.output.text)

image.png

阶段总结

  • 可以更灵活 ,自行集成 MCP / Agent 都很方便
  • 价格也比较便宜 ,每个不一样 (想尝试的可以现在去白嫖 Token)

总结

  • 这么一看 ,其实使用云厂商的工具是很不错的选择 ,DeepSeek 开源后 ,能力一下就上来了
  • 后续得看AI模型商得费用 ,费用低也是很能打的
  • 整体的效果还是都不错到的 ,后面就准备用上了

image.png

最后的最后 ❤️❤️❤️👇👇👇