> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Building a News Aggretor Using WebSearchTool

> Guide to building a stock news aggregator that searches the web for company updates, summarizes one sentence per stock, classifies sentiment, and exports results to Excel

Welcome back.

In this lesson we build a lightweight stock news tracker that:

* searches the web for the latest updates on a list of companies,
* summarizes one interesting story for each company,
* analyzes the sentiment of that summary (positive / neutral / negative),
* exports the aggregated results into a clean Excel file for further review.

Create a new notebook (or script) and name it `Stock News Pro`. Save it and make sure your environment contains your [OpenAI API key](https://platform.openai.com/account/api-keys) (for example, in a `.env` file).

<Callout icon="lightbulb" color="#1CB2FE">
  Make sure your `.env` contains a valid [API key](https://platform.openai.com/account/api-keys) (for example `OPENAI_API_KEY=...`). This lesson uses an agents package that provides an Agents SDK and a `WebSearchTool` to perform live web queries.
</Callout>

## Overview

This guide is organized into four clear steps:

1. Setup and imports
2. Main logic (single async function that performs search, summarize, classify, and collect)
3. Running the script (script vs. notebook)
4. Output format and where files are saved

Follow the sections below to implement the tracker end-to-end.

## 1 — Setup and imports

Load environment variables and import the core libraries:

```python theme={null}
# python
from dotenv import load_dotenv
import os

load_dotenv()  # loads environment variables from .env into os.environ
```

Then import the remaining dependencies:

```python theme={null}
# python
import asyncio
import pandas as pd
from pathlib import Path

from agents import Agent, Runner, WebSearchTool, trace
```

Define the list of favorite stocks we want to track:

```python theme={null}
# python
fav_stocks = ["Google", "Apple", "Nvidia"]
```

## 2 — Main logic

Below is a single consolidated async function that:

1. Creates an `Agent` with the `WebSearchTool`.
2. For each stock:
   * searches the web and requests one recent update in a single sentence,
   * extracts the summary,
   * asks the agent to classify sentiment (positive / neutral / negative),
   * maps the sentiment to an emoji,
   * extracts a source link if available,
   * appends the result to the `results` list.
3. Converts the list to a pandas `DataFrame` and exports it to Excel (both the current working directory and the user's Downloads folder when possible).

```python theme={null}
# python
async def main():
    agent = Agent(
        name="Stock News Expert",
        instructions="You are a stock news expert. Review the most recent news on these stocks/companies and provide concise answers.",
        tools=[WebSearchTool(user_location={"type": "approximate", "city": "New York City"})],
    )

    results = []

    with trace("Stock news summary"):
        for stock in fav_stocks:
            query = f"Search the web for news about '{stock}' and give me 1 recent update in a sentence."
            result = await Runner.run(agent, query)
            summary = (result.final_output or "").strip()

            # Ask for sentiment and expect only: positive, neutral, or negative
            sentiment_query = (
                f"What is the sentiment of this sentence? '{summary}' "
                "Answer only with: positive, neutral, or negative."
            )
            sentiment_result = await Runner.run(agent, sentiment_query)
            sentiment = (sentiment_result.final_output or "").strip().lower()

            # Map sentiment to an emoji for quick visual scanning
            sentiment_emoji = {
                "positive": "✅",
                "neutral": "😐",
                "negative": "❌",
            }
            sentiment_display = f"{sentiment.capitalize()} {sentiment_emoji.get(sentiment, '')}"

            # Extract a link from sources if present
            link = None
            if hasattr(result, "sources") and result.sources:
                try:
                    # result.sources is often a list of source dicts; attempt to get a URL
                    if isinstance(result.sources, list) and result.sources:
                        link = result.sources[0].get("url")
                    elif isinstance(result.sources, dict):
                        link = result.sources.get("url")
                except Exception:
                    link = None

            results.append({
                "Stock": stock,
                "News": summary,
                "Sentiment": sentiment_display,
                "Link": link
            })

    # Convert results to DataFrame and save to Excel
    df = pd.DataFrame(results)
    filename = "Stock_News_Summary_Pro.xlsx"
    df.to_excel(filename, index=False)
    print(f"News saved to {filename}")

    # Also save a copy to the user's Downloads folder (convenience)
    downloads_path = Path.home() / "Downloads" / filename
    try:
        df.to_excel(downloads_path, index=False)
        print(f"Saved to {downloads_path}")
    except Exception as e:
        print(f"Could not save to Downloads: {e}")
```

<Callout icon="warning" color="#FF6B6B">
  The script performs live web searches using the `WebSearchTool`. Expect variability in outputs and occasional missing source links. Monitor API usage and rate limits for your API key to avoid unexpected charges.
</Callout>

## 3 — Running the script

If you are running this as a standalone script, start the async function like this:

```python theme={null}
# python
if __name__ == "__main__":
    asyncio.run(main())
```

If you are in a Jupyter notebook, run the coroutine directly with:

```python theme={null}
# python
await main()
```

## 4 — What the output looks like

When the script finishes you will see the Excel files (if both saves succeeded):

| Location                  | Filename                      |
| ------------------------- | ----------------------------- |
| Current working directory | `Stock_News_Summary_Pro.xlsx` |
| User's Downloads folder   | `Stock_News_Summary_Pro.xlsx` |

Each Excel file contains the following columns:

| Column    | Description                                            | Example                                   |
| --------- | ------------------------------------------------------ | ----------------------------------------- |
| Stock     | The company name being tracked                         | `Apple`                                   |
| News      | The one-sentence recent update returned by the agent   | `Apple announces new AI features in iOS.` |
| Sentiment | Sentiment label with emoji (e.g., "Positive ✅")        | `Positive ✅`                              |
| Link      | Source URL if available (`None` or blank if not found) | `https://example.com/news/article`        |

This output is ready for sorting, filtering, or importing into other analytics tools.

## Links and references

* [OpenAI API keys](https://platform.openai.com/account/api-keys)
* Pandas documentation: [https://pandas.pydata.org/docs/](https://pandas.pydata.org/docs/)
* Python asyncio: [https://docs.python.org/3/library/asyncio.html](https://docs.python.org/3/library/asyncio.html)

That’s it — you now have a working stock news tracker that searches headlines, summarizes one recent update per company, classifies sentiment, and exports the results to Excel for later analysis. Hope you enjoyed this lesson.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/ai-agents/module/a433ab93-c13a-4a03-adf7-f89a6f61ced3/lesson/a2ef1e56-45a5-4172-8f3e-f8bc485dbc36" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/ai-agents/module/a433ab93-c13a-4a03-adf7-f89a6f61ced3/lesson/b0b26272-8019-4ea2-b8ba-5aeebd0ac0f8" />
</CardGroup>
