Merge pull request #867 from ScrapeGraphAI/pre/beta

Pre/beta
This commit is contained in:
Marco Perini 2025-01-06 04:43:28 +01:00 committed by GitHub
commit 220156e996
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 395 additions and 2621 deletions

View File

@ -1,26 +0,0 @@
name: Update requirements
on:
push:
paths:
- 'pyproject.toml'
- '.github/workflows/update-requirements.yml'
jobs:
update:
name: Update requirements
runs-on: ubuntu-latest
steps:
- name: Install the latest version of rye
uses: eifinger/setup-rye@v3
- name: Build app
run: rye run update-requirements
commit:
name: Commit changes
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add .
git commit -m "ci: update requirements.txt [skip ci]"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,32 +0,0 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: mvincig11
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
git fetch --all --tags
python setup.py sdist bdist_wheel
twine upload dist/*

View File

@ -1,8 +1,24 @@
## [1.34.0-beta.16](https://github.com/ScrapeGraphAI/Scrapegraph-ai/compare/v1.34.0-beta.15...v1.34.0-beta.16) (2025-01-06)
## [1.34.1](https://github.com/ScrapeGraphAI/Scrapegraph-ai/compare/v1.34.0...v1.34.1) (2025-01-04)
### Bug Fixes
* add back poethepoet for pylint ([a82af04](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/a82af04afed2e4ba309b5e98b5df351d9b79ca2e))
* better playwright installation handling ([f6009d1](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/f6009d1abf9e2c83999de0c9b03a41aa1bf8f2a4))
* disallow mailto: ([#861](https://github.com/ScrapeGraphAI/Scrapegraph-ai/issues/861)) ([8d9c909](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/8d9c909923dff1c247c85099db20e2a6dabb93f5))
* removed requirements files ([25861b0](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/25861b04be8a6fc60c900a46033aed91d1fef1f9))
* selenium import in ChromiumLoader ([e374e05](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/e374e055d64b7fa4c5a4c7694384dd15e6361bbd))
### chore
* chromium browser asnc handling ([5be7c49](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/5be7c497cd44fbd0c026bf3d833f572b34661b08))
* made some libs optional ([5cdf055](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/5cdf0550fe9dcd519d274bb343cf65c845e8a608))
* pandas package is now optional ([54c69a2](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/54c69a2b0b1677286b840be95ce482bcee881413))
## [1.34.0-beta.15](https://github.com/ScrapeGraphAI/Scrapegraph-ai/compare/v1.34.0-beta.14...v1.34.0-beta.15) (2025-01-03)
* add new models ([72684a9](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/72684a9476e255d5e20550f82daf3e7462fb8f5a))
## [1.34.0](https://github.com/ScrapeGraphAI/Scrapegraph-ai/compare/v1.33.11...v1.34.0) (2025-01-03)
@ -14,8 +30,11 @@
* added scrolling method to chromium docloader ([1c8b910](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/1c8b910562112947a357277bca9dc81619b72e61))
### Bug Fixes
* search graph ([d4b2679](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/d4b26796d94d314af135d2d1bbd538e1d4be7593))
* added license-files = [ ([9150e4c](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/9150e4c95fa468afe9ddda3f1278b5037a2d0f38))
* added twine ([df07da9](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/df07da9bcc59cbccf1c45d69e3a3e904eaed565b))
* build config ([b186a4f](https://github.com/ScrapeGraphAI/Scrapegraph-ai/commit/b186a4f1c73fe29fa706158cc3c61812d6b16343))

113
README.md
View File

@ -24,21 +24,6 @@ Just say which information you want to extract and the library will do it for yo
<img src="https://raw.githubusercontent.com/VinciGit00/Scrapegraph-ai/main/docs/assets/sgai-hero.png" alt="ScrapeGraphAI Hero" style="width: 100%;">
</p>
## 🔗 ScrapeGraph API & SDKs
If you are looking for a quick solution to integrate ScrapeGraph in your system, check out our powerful API [here!](https://dashboard.scrapegraphai.com/login)
<p align="center">
<img src="https://raw.githubusercontent.com/VinciGit00/Scrapegraph-ai/main/docs/assets/api-banner.png" alt="ScrapeGraph API Banner" style="width: 100%;">
</p>
We offer SDKs in both Python and Node.js, making it easy to integrate into your projects. Check them out below:
| SDK | Language | GitHub Link |
|-----------|----------|-----------------------------------------------------------------------------|
| Python SDK | Python | [scrapegraph-py](https://github.com/ScrapeGraphAI/scrapegraph-sdk/tree/main/scrapegraph-py) |
| Node.js SDK | Node.js | [scrapegraph-js](https://github.com/ScrapeGraphAI/scrapegraph-sdk/tree/main/scrapegraph-js) |
The Official API Documentation can be found [here](https://docs.scrapegraphai.com/).
## 🚀 Quick install
@ -47,35 +32,12 @@ The reference page for Scrapegraph-ai is available on the official page of PyPI:
```bash
pip install scrapegraphai
# IMPORTANT (to fetch websites content)
playwright install
```
**Note**: it is recommended to install the library in a virtual environment to avoid conflicts with other libraries 🐱
<details>
<summary><b>Optional Dependencies</b></summary>
Additional dependecies can be added while installing the library:
- <b>More Language Models</b>: additional language models are installed, such as Fireworks, Groq, Anthropic, Hugging Face, and Nvidia AI Endpoints.
This group allows you to use additional language models like Fireworks, Groq, Anthropic, Together AI, Hugging Face, and Nvidia AI Endpoints.
```bash
pip install scrapegraphai[other-language-models]
```
- <b>Semantic Options</b>: this group includes tools for advanced semantic processing, such as Graphviz.
```bash
pip install scrapegraphai[more-semantic-options]
```
- <b>Browsers Options</b>: this group includes additional browser management tools/services, such as Browserbase.
```bash
pip install scrapegraphai[more-browser-options]
```
</details>
## 💻 Usage
There are multiple standard scraping pipelines that can be used to extract information from a website (or local file).
@ -84,13 +46,12 @@ The most common one is the `SmartScraperGraph`, which extracts information from
```python
import json
from scrapegraphai.graphs import SmartScraperGraph
# Define the configuration for the scraping pipeline
graph_config = {
"llm": {
"api_key": "YOUR_OPENAI_APIKEY",
"api_key": "YOUR_OPENAI_API_KEY",
"model": "openai/gpt-4o-mini",
},
"verbose": True,
@ -99,33 +60,45 @@ graph_config = {
# Create the SmartScraperGraph instance
smart_scraper_graph = SmartScraperGraph(
prompt="Extract me all the news from the website",
source="https://www.wired.com",
prompt="Extract useful information from the webpage, including a description of what the company does, founders and social media links",
source="https://scrapegraphai.com/",
config=graph_config
)
# Run the pipeline
result = smart_scraper_graph.run()
import json
print(json.dumps(result, indent=4))
```
The output will be a dictionary like the following:
```python
"result": {
"news": [
{
"title": "The New Jersey Drone Mystery May Not Actually Be That Mysterious",
"link": "https://www.wired.com/story/new-jersey-drone-mystery-maybe-not-drones/",
"author": "Lily Hay Newman"
},
{
"title": "Former ByteDance Intern Accused of Sabotage Among Winners of Prestigious AI Award",
"link": "https://www.wired.com/story/bytedance-intern-best-paper-neurips/",
"author": "Louise Matsakis"
},
...
]
{
"description": "ScrapeGraphAI transforms websites into clean, organized data for AI agents and data analytics. It offers an AI-powered API for effortless and cost-effective data extraction.",
"founders": [
{
"name": "Marco Perini",
"role": "Founder & Technical Lead",
"linkedin": "https://www.linkedin.com/in/perinim/"
},
{
"name": "Marco Vinciguerra",
"role": "Founder & Software Engineer",
"linkedin": "https://www.linkedin.com/in/marco-vinciguerra-7ba365242/"
},
{
"name": "Lorenzo Padoan",
"role": "Founder & Product Engineer",
"linkedin": "https://www.linkedin.com/in/lorenzo-padoan-4521a2154/"
}
],
"social_media_links": {
"linkedin": "https://www.linkedin.com/company/101881123",
"twitter": "https://x.com/scrapegraphai",
"github": "https://github.com/ScrapeGraphAI/Scrapegraph-ai"
}
}
```
There are other pipelines that can be used to extract information from multiple pages, generate Python scripts, or even generate audio files.
@ -145,20 +118,30 @@ It is possible to use different LLM through APIs, such as **OpenAI**, **Groq**,
Remember to have [Ollama](https://ollama.com/) installed and download the models using the **ollama pull** command, if you want to use local models.
## 🔍 Demo
Official streamlit demo:
[![My Skills](https://skillicons.dev/icons?i=react)](https://scrapegraph-demo-demo.streamlit.app)
Try it directly on the web using Google Colab:
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1sEZBonBMGP44CtO6GQTwAlL0BGJXjtfd?usp=sharing)
## 📖 Documentation
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1sEZBonBMGP44CtO6GQTwAlL0BGJXjtfd?usp=sharing)
The documentation for ScrapeGraphAI can be found [here](https://scrapegraph-ai.readthedocs.io/en/latest/).
Check out also the Docusaurus [here](https://docs-oss.scrapegraphai.com/).
## 🔗 ScrapeGraph API & SDKs
If you are looking for a quick solution to integrate ScrapeGraph in your system, check out our powerful API [here!](https://dashboard.scrapegraphai.com/login)
<p align="center">
<img src="https://raw.githubusercontent.com/VinciGit00/Scrapegraph-ai/main/docs/assets/api-banner.png" alt="ScrapeGraph API Banner" style="width: 100%;">
</p>
We offer SDKs in both Python and Node.js, making it easy to integrate into your projects. Check them out below:
| SDK | Language | GitHub Link |
|-----------|----------|-----------------------------------------------------------------------------|
| Python SDK | Python | [scrapegraph-py](https://github.com/ScrapeGraphAI/scrapegraph-sdk/tree/main/scrapegraph-py) |
| Node.js SDK | Node.js | [scrapegraph-js](https://github.com/ScrapeGraphAI/scrapegraph-sdk/tree/main/scrapegraph-js) |
The Official API Documentation can be found [here](https://docs.scrapegraphai.com/).
## 🏆 Sponsors
<div style="text-align: center;">
<a href="https://2ly.link/1zaXG">

View File

@ -31,31 +31,6 @@ playwright install
**Not**: Diğer kütüphanelerle çakışmaları önlemek için kütüphaneyi sanal bir ortamda kurmanız önerilir 🐱
<details>
<summary><b>Opsiyonel Bağımlılıklar</b></summary>
Kütüphaneyi kurarken ek bağımlılıklar ekleyebilirsiniz:
- **Daha Fazla Dil Modeli**: Fireworks, Groq, Anthropic, Hugging Face ve Nvidia AI Endpoints gibi ek dil modelleri kurulur.
Bu grup, Fireworks, Groq, Anthropic, Together AI, Hugging Face ve Nvidia AI Endpoints gibi ek dil modellerini kullanmanızı sağlar.
```bash
pip install scrapegraphai[other-language-models]
```
- **Semantik Seçenekler**: Graphviz gibi gelişmiş semantik işleme araçlarını içerir.
```bash
pip install scrapegraphai[more-semantic-options]
```
- **Tarayıcı Seçenekleri**: Browserbase gibi ek tarayıcı yönetim araçları/hizmetlerini içerir.
```bash
pip install scrapegraphai[more-browser-options]
```
</details>
## 💻 Kullanım

View File

@ -3,9 +3,8 @@ Basic example of scraping pipeline using CSVScraperGraph from CSV documents
"""
import os
from dotenv import load_dotenv
import pandas as pd
from scrapegraphai.graphs import CSVScraperGraph
from scrapegraphai.utils import convert_to_csv, convert_to_json, prettify_exec_info
from scrapegraphai.utils import prettify_exec_info
load_dotenv()
@ -17,7 +16,8 @@ FILE_NAME = "inputs/username.csv"
curr_dir = os.path.dirname(os.path.realpath(__file__))
file_path = os.path.join(curr_dir, FILE_NAME)
text = pd.read_csv(file_path)
with open(file_path, 'r') as file:
text = file.read()
# ************************************************
# Define the configuration for the graph
@ -41,7 +41,7 @@ graph_config = {
csv_scraper_graph = CSVScraperGraph(
prompt="List me all the last names",
source=str(text), # Pass the content of the file, not the file object
source=text, # Pass the content of the file
config=graph_config
)
@ -53,8 +53,4 @@ print(result)
# ************************************************
graph_exec_info = csv_scraper_graph.get_execution_info()
print(prettify_exec_info(graph_exec_info))
# Save to json or csv
convert_to_csv(result, "result")
convert_to_json(result, "result")
print(prettify_exec_info(graph_exec_info))

View File

@ -3,9 +3,8 @@ Basic example of scraping pipeline using CSVScraperMultiGraph from CSV documents
"""
import os
from dotenv import load_dotenv
import pandas as pd
from scrapegraphai.graphs import CSVScraperMultiGraph
from scrapegraphai.utils import convert_to_csv, convert_to_json, prettify_exec_info
from scrapegraphai.utils import prettify_exec_info
load_dotenv()
# ************************************************
@ -16,7 +15,8 @@ FILE_NAME = "inputs/username.csv"
curr_dir = os.path.dirname(os.path.realpath(__file__))
file_path = os.path.join(curr_dir, FILE_NAME)
text = pd.read_csv(file_path)
with open(file_path, 'r') as file:
text = file.read()
# ************************************************
# Define the configuration for the graph
@ -48,7 +48,3 @@ print(result)
graph_exec_info = csv_scraper_graph.get_execution_info()
print(prettify_exec_info(graph_exec_info))
# Save to json or csv
convert_to_csv(result, "result")
convert_to_json(result, "result")

View File

@ -7,7 +7,7 @@ from scrapegraphai.graphs import DepthSearchGraph
load_dotenv()
openai_key = os.getenv("OPENAI_APIKEY")
openai_key = os.getenv("OPENAI_API_KEY")
graph_config = {
"llm": {

View File

@ -11,7 +11,7 @@ load_dotenv()
# Define the configuration for the graph
# ************************************************
openai_key = os.getenv("OPENAI_APIKEY")
openai_key = os.getenv("OPENAI_API_KEY")
graph_config = {
"llm": {

View File

@ -28,7 +28,7 @@ graph_config = {
# ************************************************
smart_scraper_graph = SmartScraperGraph(
prompt="Extract me all the articles",
prompt="Extract me the first article",
source="https://www.wired.com",
config=graph_config
)

View File

@ -20,7 +20,7 @@ output_path = os.path.join(curr_dir, FILE_NAME)
# Define the configuration for the graph
# ************************************************
openai_key = os.getenv("OPENAI_APIKEY")
openai_key = os.getenv("OPENAI_API_KEY")
graph_config = {
"llm": {

View File

@ -1,83 +0,0 @@
{
"id": 0,
"guid": "",
"version": "v1.0.0",
"url": "",
"meta": {},
"status": "",
"status_message": null,
"crawl_errors": 0,
"crawl_message": null,
"created_at": "2024-10-31T10:00:00Z",
"updated_at": "2024-10-31T10:00:00Z",
"entity": {
"type": "organisation",
"role": "owner",
"name": "ScrapeGraphAI, Inc.",
"email": "contact@scrapegraphai.com",
"phone": "",
"description": "An AI-powered web scraping framework that intelligently extracts structured data from websites with automatic pattern recognition, adaptive scraping strategies, and built-in rate limiting. Recognized as a top 200 open-source AI project globally.",
"webpageUrl": {
"url": "https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/main/funding.json"
}
},
"projects": [
{
"guid": "scrapegraph-core",
"name": "ScrapeGraphAI Core",
"description": "An AI-powered web scraping framework that intelligently extracts structured data from websites with automatic pattern recognition, adaptive scraping strategies, and built-in rate limiting. Recognized as a top 200 open-source AI project globally.",
"webpageUrl": {
"url": "https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/main/funding.json"
},
"repositoryUrl": {
"url": "https://github.com/ScrapeGraphAI/Scrapegraph-ai"
},
"licenses": [
"spdx:MIT"
],
"tags": [
"web-scraping",
"ai",
"data-extraction",
"python",
"machine-learning",
"open-source",
"llm"
]
}
],
"funding": {
"channels": [
{
"guid": "stripe",
"type": "bank",
"address": "https://buy.stripe.com/5kAaGW2E5gHH4vK3ce",
"description": "Will accept direct bank transfers via Stripe in the address link, for more info contact us via email contact@scrapegraphai.com"
}
],
"plans": [
{
"guid": "developer-compensation",
"status": "active",
"name": "Developer Compensation",
"description": "Provides financial support for developers working on maintenance, updates, and feature additions for the projects.",
"amount": 3000,
"currency": "USD",
"frequency": "monthly",
"channels": [
"stripe"
]
}
],
"history": [
{
"year": 2024,
"income": 15000,
"expenses": 15000,
"taxes": 0,
"currency": "USD",
"description": "From some companies that sponsor us in our Github repo page"
}
]
}
}

View File

@ -1,6 +1,6 @@
[project]
name = "scrapegraphai"
version = "1.34.1"
version = "1.35.0b1"
description = "A web scraping library based on LangChain which uses LLM and direct graph logic to create scraping pipelines."
authors = [
@ -11,15 +11,13 @@ authors = [
dependencies = [
"langchain>=0.3.0",
"langchain-google-genai>=1.0.7",
"langchain-openai>=0.1.22",
"langchain-mistralai>=0.1.12",
"langchain_community>=0.2.9",
"langchain-aws>=0.1.3",
"mistral-common>=1.4.0",
"langchain-ollama>=0.1.3",
"html2text>=2024.2.26",
"beautifulsoup4>=4.12.3",
"pandas>=2.2.2",
"python-dotenv>=1.0.1",
"tiktoken>=0.7",
"tqdm>=4.66.4",
@ -27,19 +25,11 @@ dependencies = [
"free-proxy>=1.1.1",
"playwright>=1.43.0",
"undetected-playwright>=0.3.0",
"langchain-ollama>=0.1.3",
"qdrant-client>=1.11.3",
"fastembed>=0.3.6",
"semchunk>=2.2.0",
"transformers>=4.44.2",
"transformers>=4.44.2",
"googlesearch-python>=1.2.5",
"async-timeout>=4.0.3",
"transformers>=4.44.2",
"googlesearch-python>=1.2.5",
"simpleeval>=1.0.0",
"async_timeout>=4.0.3",
"scrapegraph-py>=1.7.0"
"jsonschema>=4.23.0",
]
readme = "README.md"
@ -77,30 +67,7 @@ requires-python = ">=3.10,<4.0"
[project.optional-dependencies]
burr = ["burr[start]==0.22.1"]
docs = ["sphinx==6.0", "furo==2024.5.6"]
# Group 1: Other Language Models
other-language-models = [
"langchain-google-vertexai>=1.0.7",
"langchain-fireworks>=0.1.3",
"langchain-groq>=0.1.3",
"langchain-anthropic>=0.1.11",
"langchain-huggingface>=0.0.3",
"langchain-nvidia-ai-endpoints>=0.1.6",
"langchain_together>=0.2.0"
]
# Group 2: More Semantic Options
more-semantic-options = [
"graphviz>=0.20.3",
]
# Group 3: More Browser Options
more-browser-options = [
"browserbase>=0.3.0",
]
# Group 4: Surya Library
screenshot_scraper = [
ocr = [
"surya-ocr>=0.5.0",
"matplotlib>=3.7.2",
"ipywidgets>=8.1.0",
@ -109,22 +76,15 @@ screenshot_scraper = [
[build-system]
requires = ["hatchling==1.26.3"]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"burr[start]==0.22.1",
"sphinx==6.0",
"furo==2024.5.6",
]
[tool.uv]
dev-dependencies = [
"poethepoet>=0.31.1",
"pytest==8.0.0",
"pytest-mock==3.14.0",
"pytest>=8.0.0",
"pytest-mock>=3.14.0",
"pytest-asyncio>=0.25.0",
"pylint>=3.2.5",
"poethepoet>=0.32.0"
]
[tool.poe.tasks]

View File

@ -1,565 +0,0 @@
# generated by rye
# use `rye lock` or `rye sync` to update this lockfile
#
# last locked with the following flags:
# pre: false
# features: []
# all-features: false
# with-sources: false
-e file:.
aiofiles==24.1.0
# via burr
aiohappyeyeballs==2.3.5
# via aiohttp
aiohttp==3.10.3
# via langchain
# via langchain-community
aiosignal==1.3.1
# via aiohttp
alabaster==0.7.16
# via sphinx
altair==5.4.0
# via streamlit
annotated-types==0.7.0
# via pydantic
anyio==4.4.0
# via httpx
# via openai
# via starlette
astroid==3.2.4
# via pylint
async-timeout==4.0.3
# via aiohttp
# via langchain
# via scrapegraphai
attrs==24.2.0
# via aiohttp
# via jsonschema
# via referencing
babel==2.16.0
# via sphinx
beautifulsoup4==4.12.3
# via furo
# via googlesearch-python
# via scrapegraphai
blinker==1.8.2
# via streamlit
boto3==1.34.158
# via langchain-aws
botocore==1.34.158
# via boto3
# via s3transfer
burr==0.22.1
# via scrapegraphai
cachetools==5.4.0
# via google-auth
# via streamlit
certifi==2024.7.4
# via httpcore
# via httpx
# via requests
charset-normalizer==3.3.2
# via requests
click==8.1.7
# via burr
# via streamlit
# via uvicorn
coloredlogs==15.0.1
# via onnxruntime
contourpy==1.2.1
# via matplotlib
cycler==0.12.1
# via matplotlib
dataclasses-json==0.6.7
# via langchain-community
dill==0.3.8
# via multiprocess
# via pylint
distro==1.9.0
# via openai
docutils==0.19
# via sphinx
exceptiongroup==1.2.2
# via anyio
# via pytest
fastapi==0.112.0
# via burr
fastapi-pagination==0.12.26
# via burr
fastembed==0.3.6
# via scrapegraphai
filelock==3.15.4
# via huggingface-hub
# via transformers
flatbuffers==24.3.25
# via onnxruntime
fonttools==4.53.1
# via matplotlib
free-proxy==1.1.1
# via scrapegraphai
frozenlist==1.4.1
# via aiohttp
# via aiosignal
fsspec==2024.6.1
# via huggingface-hub
furo==2024.5.6
# via scrapegraphai
gitdb==4.0.11
# via gitpython
gitpython==3.1.43
# via streamlit
google-ai-generativelanguage==0.6.6
# via google-generativeai
google-api-core==2.19.1
# via google-ai-generativelanguage
# via google-api-python-client
# via google-generativeai
google-api-python-client==2.140.0
# via google-generativeai
google-auth==2.33.0
# via google-ai-generativelanguage
# via google-api-core
# via google-api-python-client
# via google-auth-httplib2
# via google-generativeai
google-auth-httplib2==0.2.0
# via google-api-python-client
google-generativeai==0.7.2
# via langchain-google-genai
googleapis-common-protos==1.63.2
# via google-api-core
# via grpcio-status
googlesearch-python==1.2.5
# via scrapegraphai
graphviz==0.20.3
# via burr
greenlet==3.0.3
# via playwright
grpcio==1.65.4
# via google-api-core
# via grpcio-status
# via grpcio-tools
# via qdrant-client
grpcio-status==1.62.3
# via google-api-core
grpcio-tools==1.62.3
# via qdrant-client
h11==0.14.0
# via httpcore
# via uvicorn
h2==4.1.0
# via httpx
hpack==4.0.0
# via h2
html2text==2024.2.26
# via scrapegraphai
httpcore==1.0.5
# via httpx
httplib2==0.22.0
# via google-api-python-client
# via google-auth-httplib2
httpx==0.27.0
# via langchain-mistralai
# via langsmith
# via ollama
# via openai
# via qdrant-client
httpx-sse==0.4.0
# via langchain-mistralai
huggingface-hub==0.24.5
# via fastembed
# via tokenizers
# via transformers
humanfriendly==10.0
# via coloredlogs
hyperframe==6.0.1
# via h2
idna==3.7
# via anyio
# via httpx
# via requests
# via yarl
imagesize==1.4.1
# via sphinx
iniconfig==2.0.0
# via pytest
isort==5.13.2
# via pylint
jinja2==3.1.4
# via altair
# via burr
# via pydeck
# via sphinx
jiter==0.5.0
# via openai
jmespath==1.0.1
# via boto3
# via botocore
jsonpatch==1.33
# via langchain-core
jsonpointer==3.0.0
# via jsonpatch
jsonschema==4.23.0
# via altair
# via mistral-common
jsonschema-specifications==2023.12.1
# via jsonschema
kiwisolver==1.4.5
# via matplotlib
langchain==0.3.0
# via langchain-community
# via scrapegraphai
langchain-aws==0.2.0
# via scrapegraphai
langchain-community==0.3.0
# via scrapegraphai
langchain-core==0.3.1
# via langchain
# via langchain-aws
# via langchain-community
# via langchain-google-genai
# via langchain-mistralai
# via langchain-ollama
# via langchain-openai
# via langchain-text-splitters
langchain-google-genai==2.0.0
# via scrapegraphai
langchain-mistralai==0.2.0
# via scrapegraphai
langchain-ollama==0.2.0
# via scrapegraphai
langchain-openai==0.2.0
# via scrapegraphai
langchain-text-splitters==0.3.0
# via langchain
langsmith==0.1.121
# via langchain
# via langchain-community
# via langchain-core
loguru==0.7.2
# via burr
# via fastembed
lxml==5.3.0
# via free-proxy
markdown-it-py==3.0.0
# via rich
markupsafe==2.1.5
# via jinja2
marshmallow==3.21.3
# via dataclasses-json
matplotlib==3.9.1.post1
# via burr
mccabe==0.7.0
# via pylint
mdurl==0.1.2
# via markdown-it-py
minify-html==0.15.0
# via scrapegraphai
mistral-common==1.4.1
# via scrapegraphai
mmh3==4.1.0
# via fastembed
mpire==2.10.2
# via semchunk
mpmath==1.3.0
# via sympy
multidict==6.0.5
# via aiohttp
# via yarl
multiprocess==0.70.16
# via mpire
mypy-extensions==1.0.0
# via typing-inspect
narwhals==1.3.0
# via altair
numpy==1.26.4
# via contourpy
# via fastembed
# via langchain
# via langchain-aws
# via langchain-community
# via matplotlib
# via onnx
# via onnxruntime
# via opencv-python-headless
# via pandas
# via pyarrow
# via pydeck
# via qdrant-client
# via sf-hamilton
# via streamlit
# via transformers
ollama==0.3.2
# via langchain-ollama
onnx==1.17.0
# via fastembed
onnxruntime==1.19.2
# via fastembed
openai==1.40.3
# via burr
# via langchain-openai
opencv-python-headless==4.10.0.84
# via mistral-common
orjson==3.10.7
# via langsmith
packaging==24.1
# via altair
# via huggingface-hub
# via langchain-core
# via marshmallow
# via matplotlib
# via onnxruntime
# via pytest
# via sphinx
# via streamlit
# via transformers
pandas==2.2.2
# via scrapegraphai
# via sf-hamilton
# via streamlit
pillow==10.4.0
# via fastembed
# via matplotlib
# via mistral-common
# via streamlit
platformdirs==4.2.2
# via pylint
playwright==1.45.1
# via scrapegraphai
# via undetected-playwright
pluggy==1.5.0
# via pytest
portalocker==2.10.1
# via qdrant-client
proto-plus==1.24.0
# via google-ai-generativelanguage
# via google-api-core
protobuf==4.25.4
# via google-ai-generativelanguage
# via google-api-core
# via google-generativeai
# via googleapis-common-protos
# via grpcio-status
# via grpcio-tools
# via onnx
# via onnxruntime
# via proto-plus
# via streamlit
pyarrow==17.0.0
# via streamlit
pyasn1==0.6.0
# via pyasn1-modules
# via rsa
pyasn1-modules==0.4.0
# via google-auth
pydantic==2.10.1
# via burr
# via fastapi
# via fastapi-pagination
# via google-generativeai
# via langchain
# via langchain-aws
# via langchain-core
# via langchain-google-genai
# via langchain-mistralai
# via langsmith
# via mistral-common
# via openai
# via pydantic-settings
# via qdrant-client
# via scrapegraph-py
pydantic-core==2.27.1
# via pydantic
pydantic-settings==2.5.2
# via langchain-community
pydeck==0.9.1
# via streamlit
pyee==11.1.0
# via playwright
pygments==2.18.0
# via furo
# via mpire
# via rich
# via sphinx
pylint==3.2.6
pyparsing==3.1.2
# via httplib2
# via matplotlib
pystemmer==2.2.0.1
# via fastembed
pytest==8.0.0
# via pytest-mock
pytest-mock==3.14.0
python-dateutil==2.9.0.post0
# via botocore
# via matplotlib
# via pandas
python-dotenv==1.0.1
# via pydantic-settings
# via scrapegraph-py
# via scrapegraphai
pytz==2024.1
# via pandas
pyyaml==6.0.2
# via huggingface-hub
# via langchain
# via langchain-community
# via langchain-core
# via transformers
qdrant-client==1.11.3
# via scrapegraphai
referencing==0.35.1
# via jsonschema
# via jsonschema-specifications
regex==2024.7.24
# via tiktoken
# via transformers
requests==2.32.3
# via burr
# via fastembed
# via free-proxy
# via google-api-core
# via googlesearch-python
# via huggingface-hub
# via langchain
# via langchain-community
# via langsmith
# via mistral-common
# via scrapegraph-py
# via sphinx
# via streamlit
# via tiktoken
# via transformers
rich==13.7.1
# via streamlit
rpds-py==0.20.0
# via jsonschema
# via referencing
rsa==4.9
# via google-auth
s3transfer==0.10.2
# via boto3
safetensors==0.4.5
# via transformers
scrapegraph-py==0.0.3
# via scrapegraphai
semchunk==2.2.0
# via scrapegraphai
sentencepiece==0.2.0
# via mistral-common
setuptools==75.1.0
# via grpcio-tools
sf-hamilton==1.73.1
# via burr
simpleeval==1.0.0
# via scrapegraphai
six==1.16.0
# via python-dateutil
smmap==5.0.1
# via gitdb
sniffio==1.3.1
# via anyio
# via httpx
# via openai
snowballstemmer==2.2.0
# via fastembed
# via sphinx
soupsieve==2.5
# via beautifulsoup4
sphinx==6.0.0
# via furo
# via scrapegraphai
# via sphinx-basic-ng
sphinx-basic-ng==1.0.0b2
# via furo
sphinxcontrib-applehelp==2.0.0
# via sphinx
sphinxcontrib-devhelp==2.0.0
# via sphinx
sphinxcontrib-htmlhelp==2.1.0
# via sphinx
sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-qthelp==2.0.0
# via sphinx
sphinxcontrib-serializinghtml==2.0.0
# via sphinx
sqlalchemy==2.0.32
# via langchain
# via langchain-community
starlette==0.37.2
# via fastapi
streamlit==1.37.1
# via burr
sympy==1.13.3
# via onnxruntime
tenacity==8.5.0
# via langchain
# via langchain-community
# via langchain-core
# via streamlit
tiktoken==0.7.0
# via langchain-openai
# via mistral-common
# via scrapegraphai
tokenizers==0.19.1
# via fastembed
# via langchain-mistralai
# via transformers
toml==0.10.2
# via streamlit
tomli==2.1.0
# via pylint
# via pytest
tomlkit==0.13.0
# via pylint
tornado==6.4.1
# via streamlit
tqdm==4.66.5
# via fastembed
# via google-generativeai
# via huggingface-hub
# via mpire
# via openai
# via scrapegraphai
# via semchunk
# via transformers
transformers==4.44.2
# via scrapegraphai
typing-extensions==4.12.2
# via altair
# via anyio
# via astroid
# via fastapi
# via fastapi-pagination
# via google-generativeai
# via huggingface-hub
# via langchain-core
# via mistral-common
# via openai
# via pydantic
# via pydantic-core
# via pyee
# via sf-hamilton
# via sqlalchemy
# via streamlit
# via typing-inspect
# via uvicorn
typing-inspect==0.9.0
# via dataclasses-json
# via sf-hamilton
tzdata==2024.1
# via pandas
undetected-playwright==0.3.0
# via scrapegraphai
uritemplate==4.1.1
# via google-api-python-client
urllib3==1.26.19
# via botocore
# via qdrant-client
# via requests
uvicorn==0.30.5
# via burr
yarl==1.9.4
# via aiohttp

View File

@ -1,7 +0,0 @@
pytest==8.0.0
pytest-asyncio==0.25.0
pytest-mock==3.14.0
burr[start]==0.22.1
sphinx==6.0
furo==2024.5.6
pylint>=3.2.5

View File

@ -1,403 +0,0 @@
# generated by rye
# use `rye lock` or `rye sync` to update this lockfile
#
# last locked with the following flags:
# pre: false
# features: []
# all-features: false
# with-sources: false
-e file:.
aiohttp==3.9.5
# via langchain
# via langchain-community
aiosignal==1.3.1
# via aiohttp
annotated-types==0.7.0
# via pydantic
anyio==4.4.0
# via httpx
# via openai
async-timeout==4.0.3
# via aiohttp
# via langchain
# via scrapegraphai
attrs==23.2.0
# via aiohttp
# via jsonschema
# via referencing
beautifulsoup4==4.12.3
# via googlesearch-python
# via scrapegraphai
boto3==1.34.146
# via langchain-aws
botocore==1.34.146
# via boto3
# via s3transfer
cachetools==5.4.0
# via google-auth
certifi==2024.7.4
# via httpcore
# via httpx
# via requests
charset-normalizer==3.3.2
# via requests
coloredlogs==15.0.1
# via onnxruntime
dataclasses-json==0.6.7
# via langchain-community
dill==0.3.8
# via multiprocess
distro==1.9.0
# via openai
exceptiongroup==1.2.2
# via anyio
fastembed==0.3.6
# via scrapegraphai
filelock==3.15.4
# via huggingface-hub
# via transformers
flatbuffers==24.3.25
# via onnxruntime
free-proxy==1.1.1
# via scrapegraphai
frozenlist==1.4.1
# via aiohttp
# via aiosignal
fsspec==2024.6.1
# via huggingface-hub
google-ai-generativelanguage==0.6.6
# via google-generativeai
google-api-core==2.19.1
# via google-ai-generativelanguage
# via google-api-python-client
# via google-generativeai
google-api-python-client==2.137.0
# via google-generativeai
google-auth==2.32.0
# via google-ai-generativelanguage
# via google-api-core
# via google-api-python-client
# via google-auth-httplib2
# via google-generativeai
google-auth-httplib2==0.2.0
# via google-api-python-client
google-generativeai==0.7.2
# via langchain-google-genai
googleapis-common-protos==1.63.2
# via google-api-core
# via grpcio-status
googlesearch-python==1.2.5
# via scrapegraphai
greenlet==3.0.3
# via playwright
grpcio==1.65.1
# via google-api-core
# via grpcio-status
# via grpcio-tools
# via qdrant-client
grpcio-status==1.62.2
# via google-api-core
grpcio-tools==1.62.3
# via qdrant-client
h11==0.14.0
# via httpcore
h2==4.1.0
# via httpx
hpack==4.0.0
# via h2
html2text==2024.2.26
# via scrapegraphai
httpcore==1.0.5
# via httpx
httplib2==0.22.0
# via google-api-python-client
# via google-auth-httplib2
httpx==0.27.0
# via langchain-mistralai
# via langsmith
# via ollama
# via openai
# via qdrant-client
httpx-sse==0.4.0
# via langchain-mistralai
huggingface-hub==0.24.1
# via fastembed
# via tokenizers
# via transformers
humanfriendly==10.0
# via coloredlogs
hyperframe==6.0.1
# via h2
idna==3.7
# via anyio
# via httpx
# via requests
# via yarl
jiter==0.5.0
# via openai
jmespath==1.0.1
# via boto3
# via botocore
jsonpatch==1.33
# via langchain-core
jsonpointer==3.0.0
# via jsonpatch
jsonschema==4.23.0
# via mistral-common
jsonschema-specifications==2023.12.1
# via jsonschema
langchain==0.3.0
# via langchain-community
# via scrapegraphai
langchain-aws==0.2.0
# via scrapegraphai
langchain-community==0.3.0
# via scrapegraphai
langchain-core==0.3.1
# via langchain
# via langchain-aws
# via langchain-community
# via langchain-google-genai
# via langchain-mistralai
# via langchain-ollama
# via langchain-openai
# via langchain-text-splitters
langchain-google-genai==2.0.0
# via scrapegraphai
langchain-mistralai==0.2.0
# via scrapegraphai
langchain-ollama==0.2.0
# via scrapegraphai
langchain-openai==0.2.0
# via scrapegraphai
langchain-text-splitters==0.3.0
# via langchain
langsmith==0.1.121
# via langchain
# via langchain-community
# via langchain-core
loguru==0.7.2
# via fastembed
lxml==5.2.2
# via free-proxy
marshmallow==3.21.3
# via dataclasses-json
minify-html==0.15.0
# via scrapegraphai
mistral-common==1.4.1
# via scrapegraphai
mmh3==4.1.0
# via fastembed
mpire==2.10.2
# via semchunk
mpmath==1.3.0
# via sympy
multidict==6.0.5
# via aiohttp
# via yarl
multiprocess==0.70.16
# via mpire
mypy-extensions==1.0.0
# via typing-inspect
numpy==1.26.4
# via fastembed
# via langchain
# via langchain-aws
# via langchain-community
# via onnx
# via onnxruntime
# via opencv-python-headless
# via pandas
# via qdrant-client
# via transformers
ollama==0.3.2
# via langchain-ollama
onnx==1.17.0
# via fastembed
onnxruntime==1.19.2
# via fastembed
openai==1.41.0
# via langchain-openai
opencv-python-headless==4.10.0.84
# via mistral-common
orjson==3.10.6
# via langsmith
packaging==24.1
# via huggingface-hub
# via langchain-core
# via marshmallow
# via onnxruntime
# via transformers
pandas==2.2.2
# via scrapegraphai
pillow==10.4.0
# via fastembed
# via mistral-common
playwright==1.45.1
# via scrapegraphai
# via undetected-playwright
portalocker==2.10.1
# via qdrant-client
proto-plus==1.24.0
# via google-ai-generativelanguage
# via google-api-core
protobuf==4.25.3
# via google-ai-generativelanguage
# via google-api-core
# via google-generativeai
# via googleapis-common-protos
# via grpcio-status
# via grpcio-tools
# via onnx
# via onnxruntime
# via proto-plus
pyasn1==0.6.0
# via pyasn1-modules
# via rsa
pyasn1-modules==0.4.0
# via google-auth
pydantic==2.10.1
# via google-generativeai
# via langchain
# via langchain-aws
# via langchain-core
# via langchain-google-genai
# via langchain-mistralai
# via langsmith
# via mistral-common
# via openai
# via pydantic-settings
# via qdrant-client
# via scrapegraph-py
pydantic-core==2.27.1
# via pydantic
pydantic-settings==2.5.2
# via langchain-community
pyee==11.1.0
# via playwright
pygments==2.18.0
# via mpire
pyparsing==3.1.2
# via httplib2
pystemmer==2.2.0.1
# via fastembed
python-dateutil==2.9.0.post0
# via botocore
# via pandas
python-dotenv==1.0.1
# via pydantic-settings
# via scrapegraph-py
# via scrapegraphai
pytz==2024.1
# via pandas
pyyaml==6.0.1
# via huggingface-hub
# via langchain
# via langchain-community
# via langchain-core
# via transformers
qdrant-client==1.11.3
# via scrapegraphai
referencing==0.35.1
# via jsonschema
# via jsonschema-specifications
regex==2024.5.15
# via tiktoken
# via transformers
requests==2.32.3
# via fastembed
# via free-proxy
# via google-api-core
# via googlesearch-python
# via huggingface-hub
# via langchain
# via langchain-community
# via langsmith
# via mistral-common
# via scrapegraph-py
# via tiktoken
# via transformers
rpds-py==0.20.0
# via jsonschema
# via referencing
rsa==4.9
# via google-auth
s3transfer==0.10.2
# via boto3
safetensors==0.4.5
# via transformers
scrapegraph-py==0.0.3
# via scrapegraphai
semchunk==2.2.0
# via scrapegraphai
sentencepiece==0.2.0
# via mistral-common
setuptools==75.1.0
# via grpcio-tools
simpleeval==1.0.0
# via scrapegraphai
six==1.16.0
# via python-dateutil
sniffio==1.3.1
# via anyio
# via httpx
# via openai
snowballstemmer==2.2.0
# via fastembed
soupsieve==2.5
# via beautifulsoup4
sqlalchemy==2.0.31
# via langchain
# via langchain-community
sympy==1.13.3
# via onnxruntime
tenacity==8.5.0
# via langchain
# via langchain-community
# via langchain-core
tiktoken==0.7.0
# via langchain-openai
# via mistral-common
# via scrapegraphai
tokenizers==0.19.1
# via fastembed
# via langchain-mistralai
# via transformers
tqdm==4.66.4
# via fastembed
# via google-generativeai
# via huggingface-hub
# via mpire
# via openai
# via scrapegraphai
# via semchunk
# via transformers
transformers==4.44.2
# via scrapegraphai
typing-extensions==4.12.2
# via anyio
# via google-generativeai
# via huggingface-hub
# via langchain-core
# via mistral-common
# via openai
# via pydantic
# via pydantic-core
# via pyee
# via sqlalchemy
# via typing-inspect
typing-inspect==0.9.0
# via dataclasses-json
tzdata==2024.1
# via pandas
undetected-playwright==0.3.0
# via scrapegraphai
uritemplate==4.1.1
# via google-api-python-client
urllib3==1.26.19
# via botocore
# via qdrant-client
# via requests
yarl==1.9.4
# via aiohttp

View File

@ -1,22 +0,0 @@
langchain>=0.2.14
langchain-google-genai>=1.0.7
langchain-openai>=0.1.22
langchain-mistralai>=0.1.12
langchain_community>=0.2.9
langchain-aws>=0.1.3
html2text>=2024.2.26
faiss-cpu>=1.8.0
beautifulsoup4>=4.12.3
pandas>=2.2.2
python-dotenv>=1.0.1
tiktoken>=0.7
tqdm>=4.66.4
minify-html>=0.15.0
free-proxy>=1.1.1
playwright>=1.43.0
undetected-playwright>=0.3.0
semchunk>=1.0.1
langchain-ollama>=0.1.3
simpleeval>=0.9.13
googlesearch-python>=1.2.5
async_timeout>=4.0.3

View File

@ -4,7 +4,6 @@ GraphBuilder Module
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_extraction_chain
from langchain_community.chat_models import ErnieBotChat
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_openai import ChatOpenAI
from ..helpers import nodes_metadata, graph_schema
@ -70,6 +69,10 @@ class GraphBuilder:
if "gpt-" in llm_params["model"]:
return ChatOpenAI(llm_params)
elif "gemini" in llm_params["model"]:
try:
from langchain_google_genai import ChatGoogleGenerativeAI
except ImportError:
raise ImportError("langchain_google_genai is not installed. Please install it using 'pip install langchain-google-genai'.")
return ChatGoogleGenerativeAI(llm_params)
elif "ernie" in llm_params["model"]:
return ErnieBotChat(llm_params)

View File

@ -4,8 +4,6 @@ from langchain_community.document_loaders.base import BaseLoader
from langchain_core.documents import Document
import aiohttp
import async_timeout
from selenium import webdriver
from selenium.webdriver.chrome.options import Options as ChromeOptions
from typing import Union
from ..utils import Proxy, dynamic_import, get_logger, parse_or_search_proxy
@ -25,9 +23,6 @@ class ChromiumLoader(BaseLoader):
requires_js_support: Flag to determine if JS rendering is required.
"""
RETRY_LIMIT = 3
TIMEOUT = 10
def __init__(
self,
urls: List[str],
@ -39,6 +34,8 @@ class ChromiumLoader(BaseLoader):
requires_js_support: bool = False,
storage_state: Optional[str] = None,
browser_name: str = "chromium", #default chromium
retry_limit: int = 1,
timeout: int = 10,
**kwargs: Any,
):
"""Initialize the loader with a list of URL paths.
@ -49,6 +46,8 @@ class ChromiumLoader(BaseLoader):
proxy: A dictionary containing proxy information; None disables protection.
urls: A list of URLs to scrape content from.
requires_js_support: Whether to use JS rendering for scraping.
retry_limit: Maximum number of retry attempts for scraping. Defaults to 3.
timeout: Maximum time in seconds to wait for scraping. Defaults to 10.
kwargs: A dictionary containing additional browser kwargs.
Raises:
@ -70,12 +69,17 @@ class ChromiumLoader(BaseLoader):
self.requires_js_support = requires_js_support
self.storage_state = storage_state
self.browser_name = browser_name
self.retry_limit = retry_limit
self.timeout = timeout
async def scrape(self, url:str) -> str:
if self.backend == "playwright":
return await self.ascrape_playwright(url)
elif self.backend == "selenium":
return await self.ascrape_undetected_chromedriver(url)
try:
return await self.ascrape_undetected_chromedriver(url)
except Exception as e:
raise ValueError(f"Failed to scrape with undetected chromedriver: {e}")
else:
raise ValueError(f"Unsupported backend: {self.backend}")
@ -90,18 +94,22 @@ class ChromiumLoader(BaseLoader):
Returns:
str: The scraped HTML content or an error message if an exception occurs.
"""
import undetected_chromedriver as uc
try:
import undetected_chromedriver as uc
except ImportError:
raise ImportError("undetected_chromedriver is required for ChromiumLoader. Please install it with `pip install undetected-chromedriver`.")
logger.info(f"Starting scraping with {self.backend}...")
results = ""
attempt = 0
while attempt < self.RETRY_LIMIT:
while attempt < self.retry_limit:
try:
async with async_timeout.timeout(self.TIMEOUT):
async with async_timeout.timeout(self.timeout):
# Handling browser selection
if self.backend == "selenium":
if self.browser_name == "chromium":
from selenium.webdriver.chrome.options import Options as ChromeOptions
options = ChromeOptions()
options.headless = self.headless
# Initialize undetected chromedriver for Selenium
@ -112,6 +120,7 @@ class ChromiumLoader(BaseLoader):
break
elif self.browser_name == "firefox":
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium import webdriver
options = FirefoxOptions()
options.headless = self.headless
# Initialize undetected Firefox driver (if required)
@ -131,9 +140,9 @@ class ChromiumLoader(BaseLoader):
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
attempt += 1
logger.error(f"Attempt {attempt} failed: {e}")
if attempt == self.RETRY_LIMIT:
if attempt == self.retry_limit:
results = (
f"Error: Network error after {self.RETRY_LIMIT} attempts - {e}"
f"Error: Network error after {self.retry_limit} attempts - {e}"
)
finally:
driver.quit()
@ -201,7 +210,7 @@ class ChromiumLoader(BaseLoader):
results = ""
attempt = 0
while attempt < self.RETRY_LIMIT:
while attempt < self.retry_limit:
try:
async with async_playwright() as p:
browser = None
@ -265,8 +274,8 @@ class ChromiumLoader(BaseLoader):
except (aiohttp.ClientError, asyncio.TimeoutError, Exception) as e:
attempt += 1
logger.error(f"Attempt {attempt} failed: {e}")
if attempt == self.RETRY_LIMIT:
results = f"Error: Network error after {self.RETRY_LIMIT} attempts - {e}"
if attempt == self.retry_limit:
results = f"Error: Network error after {self.retry_limit} attempts - {e}"
finally:
await browser.close()
@ -280,7 +289,11 @@ class ChromiumLoader(BaseLoader):
url (str): The URL to scrape.
Returns:
str: The scraped HTML content or an error message if an exception occurs.
str: The scraped HTML content
Raises:
RuntimeError: When retry limit is reached without successful scraping
ValueError: When an invalid browser name is provided
"""
from playwright.async_api import async_playwright
from undetected_playwright import Malenia
@ -289,9 +302,9 @@ class ChromiumLoader(BaseLoader):
results = ""
attempt = 0
while attempt < self.RETRY_LIMIT:
while attempt < self.retry_limit:
try:
async with async_playwright() as p, async_timeout.timeout(self.TIMEOUT):
async with async_playwright() as p, async_timeout.timeout(self.timeout):
browser = None
if browser_name == "chromium":
browser = await p.chromium.launch(
@ -312,22 +325,15 @@ class ChromiumLoader(BaseLoader):
await page.wait_for_load_state(self.load_state)
results = await page.content()
logger.info("Content scraped")
break
await browser.close()
return results
except (aiohttp.ClientError, asyncio.TimeoutError, Exception) as e:
attempt += 1
logger.error(f"Attempt {attempt} failed: {e}")
if attempt == self.RETRY_LIMIT:
results = f"Error: Network error after {self.RETRY_LIMIT} attempts - {e}"
finally:
if "browser" in locals():
await browser.close()
if attempt == self.retry_limit:
raise RuntimeError(f"Failed to scrape after {self.retry_limit} attempts: {str(e)}")
return results
async def ascrape_with_js_support(self, url: str , browser_name:str = "chromium") -> str:
async def ascrape_with_js_support(self, url: str, browser_name: str = "chromium") -> str:
"""
Asynchronously scrape the content of a given URL by rendering JavaScript using Playwright.
@ -335,18 +341,20 @@ class ChromiumLoader(BaseLoader):
url (str): The URL to scrape.
Returns:
str: The fully rendered HTML content after JavaScript execution,
or an error message if an exception occurs.
str: The fully rendered HTML content after JavaScript execution
Raises:
RuntimeError: When retry limit is reached without successful scraping
ValueError: When an invalid browser name is provided
"""
from playwright.async_api import async_playwright
logger.info(f"Starting scraping with JavaScript support for {url}...")
results = ""
attempt = 0
while attempt < self.RETRY_LIMIT:
while attempt < self.retry_limit:
try:
async with async_playwright() as p, async_timeout.timeout(self.TIMEOUT):
async with async_playwright() as p, async_timeout.timeout(self.timeout):
browser = None
if browser_name == "chromium":
browser = await p.chromium.launch(
@ -365,19 +373,15 @@ class ChromiumLoader(BaseLoader):
await page.goto(url, wait_until="networkidle")
results = await page.content()
logger.info("Content scraped after JavaScript rendering")
break
return results
except (aiohttp.ClientError, asyncio.TimeoutError, Exception) as e:
attempt += 1
logger.error(f"Attempt {attempt} failed: {e}")
if attempt == self.RETRY_LIMIT:
results = (
f"Error: Network error after {self.RETRY_LIMIT} attempts - {e}"
)
if attempt == self.retry_limit:
raise RuntimeError(f"Failed to scrape after {self.retry_limit} attempts: {str(e)}")
finally:
await browser.close()
return results
def lazy_load(self) -> Iterator[Document]:
"""
Lazily load text content from the provided URLs.

View File

@ -234,7 +234,7 @@ class AbstractGraph(ABC):
from langchain_together import ChatTogether
except ImportError:
raise ImportError("""The langchain_together module is not installed.
Please install it using `pip install scrapegraphai[other-language-models]`.""")
Please install it using `pip install langchain-together`.""")
return ChatTogether(**llm_params)
elif model_provider == "nvidia":
@ -242,7 +242,7 @@ class AbstractGraph(ABC):
from langchain_nvidia_ai_endpoints import ChatNVIDIA
except ImportError:
raise ImportError("""The langchain_nvidia_ai_endpoints module is not installed.
Please install it using `pip install scrapegraphai[other-language-models]`.""")
Please install it using `pip install langchain-nvidia-ai-endpoints`.""")
return ChatNVIDIA(**llm_params)
except Exception as e:

View File

@ -3,8 +3,6 @@ SmartScraperGraph Module
"""
from typing import Optional
from pydantic import BaseModel
from scrapegraph_py import Client
from scrapegraph_py.logger import sgai_logger
from .base_graph import BaseGraph
from .abstract_graph import AbstractGraph
from ..nodes import (
@ -55,6 +53,9 @@ class SmartScraperGraph(AbstractGraph):
super().__init__(prompt, config, source, schema)
self.input_key = "url" if source.startswith("http") else "local_dir"
# for detailed logging of the SmartScraper API set it to True
self.verbose = config.get("verbose", False)
def _create_graph(self) -> BaseGraph:
"""
@ -64,7 +65,12 @@ class SmartScraperGraph(AbstractGraph):
BaseGraph: A graph instance representing the web scraping workflow.
"""
if self.llm_model == "scrapegraphai/smart-scraper":
try:
from scrapegraph_py import Client
from scrapegraph_py.logger import sgai_logger
except ImportError:
raise ImportError("scrapegraph_py is not installed. Please install it using 'pip install scrapegraph-py'.")
sgai_logger.set_logging(level="INFO")
# Initialize the client with explicit API key

View File

@ -4,14 +4,12 @@ FetchNode Module
import json
from typing import List, Optional
from langchain_openai import ChatOpenAI, AzureChatOpenAI
import pandas as pd
import requests
from langchain_community.document_loaders import PyPDFLoader
from langchain_core.documents import Document
from ..utils.cleanup_html import cleanup_html
from ..docloaders import ChromiumLoader
from ..utils.convert_to_md import convert_to_md
from ..utils.logging import get_logger
from .base_node import BaseNode
class FetchNode(BaseNode):
@ -80,24 +78,6 @@ class FetchNode(BaseNode):
None if node_config is None else node_config.get("storage_state", None)
)
def is_valid_url(self, source: str) -> bool:
"""
Validates if the source string is a valid URL using regex.
Parameters:
source (str): The URL string to validate
Raises:
ValueError: If the URL is invalid
"""
import re
url_pattern = r"^https?://[^\s/$.?#].[^\s]*$"
if not bool(re.match(url_pattern, source)):
raise ValueError(
f"Invalid URL format: {source}. URL must start with http(s):// and contain a valid domain."
)
return True
def execute(self, state):
"""
@ -130,12 +110,9 @@ class FetchNode(BaseNode):
elif self.input == "pdf_dir":
return state
# For web sources, validate URL before proceeding
try:
if self.is_valid_url(source):
return self.handle_web_source(state, source)
return self.handle_web_source(state, source)
except ValueError as e:
# Re-raise the exception from is_valid_url
raise
return self.handle_local_source(state, source)
@ -199,6 +176,10 @@ class FetchNode(BaseNode):
loader = PyPDFLoader(source)
return loader.load()
elif input_type == "csv":
try:
import pandas as pd
except ImportError:
raise ImportError("pandas is not installed. Please install it using `pip install pandas`.")
return [
Document(
page_content=str(pd.read_csv(source)), metadata={"source": "csv"}

View File

@ -160,20 +160,42 @@ class FetchNodeLevelK(BaseNode):
def get_full_links(self, base_url: str, links: list) -> list:
"""
Converts relative URLs to full URLs based on the base URL.
Filters out non-web links (mailto:, tel:, javascript:, etc.).
Args:
base_url (str): The base URL for resolving relative links.
links (list): A list of links to convert.
Returns:
list: A list of full URLs.
list: A list of valid full URLs.
"""
# List of invalid URL schemes to filter out
invalid_schemes = {
'mailto:', 'tel:', 'fax:', 'sms:', 'callto:', 'wtai:', 'javascript:',
'data:', 'file:', 'ftp:', 'irc:', 'news:', 'nntp:', 'feed:', 'webcal:',
'skype:', 'im:', 'mtps:', 'spotify:', 'steam:', 'teamspeak:', 'udp:',
'unreal:', 'ut2004:', 'ventrilo:', 'view-source:', 'ws:', 'wss:'
}
full_links = []
for link in links:
if self.only_inside_links and link.startswith("http"):
# Skip if link starts with any invalid scheme
if any(link.lower().startswith(scheme) for scheme in invalid_schemes):
continue
full_link = link if link.startswith("http") else urljoin(base_url, link)
full_links.append(full_link)
# Skip if it's an external link and only_inside_links is True
if self.only_inside_links and link.startswith(('http://', 'https://')):
continue
# Convert relative URLs to absolute URLs
try:
full_link = link if link.startswith(('http://', 'https://')) else urljoin(base_url, link)
# Ensure the final URL starts with http:// or https://
if full_link.startswith(('http://', 'https://')):
full_links.append(full_link)
except Exception as e:
self.logger.warning(f"Failed to process link {link}: {str(e)}")
return full_links
def obtain_content(self, documents: List, loader_kwargs) -> List:
@ -191,7 +213,11 @@ class FetchNodeLevelK(BaseNode):
for doc in documents:
source = doc["source"]
if "document" not in doc:
document = self.fetch_content(source, loader_kwargs)
try:
document = self.fetch_content(source, loader_kwargs)
except Exception as e:
self.logger.warning(f"Failed to fetch content for {source}: {str(e)}")
continue
if not document or not document[0].page_content.strip():
self.logger.warning(f"Failed to fetch content for {source}")

View File

@ -12,10 +12,8 @@ from langchain_aws import ChatBedrock
from langchain_community.chat_models import ChatOllama
from tqdm import tqdm
from .base_node import BaseNode
from ..utils.output_parser import get_structured_output_parser, get_pydantic_output_parser
from ..utils.output_parser import get_pydantic_output_parser
from requests.exceptions import Timeout
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks import get_openai_callback
from ..prompts import (
TEMPLATE_CHUNKS, TEMPLATE_NO_CHUNKS, TEMPLATE_MERGE,
TEMPLATE_CHUNKS_MD, TEMPLATE_NO_CHUNKS_MD, TEMPLATE_MERGE_MD

View File

@ -96,7 +96,17 @@ class MergeAnswersNode(BaseNode):
merge_chain = prompt_template | self.llm_model | output_parser
answer = merge_chain.invoke({"user_prompt": user_prompt})
answer["sources"] = state.get("urls", [])
# Get the URLs from the state, ensuring we get the actual URLs used for scraping
urls = []
if "urls" in state:
urls = state["urls"]
elif "considered_urls" in state:
urls = state["considered_urls"]
# Only add sources if we actually have URLs
if urls:
answer["sources"] = urls
state.update({self.output[0]: answer})
return state

View File

@ -3,8 +3,6 @@ RAGNode Module
"""
from typing import List, Optional
from .base_node import BaseNode
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, VectorParams, Distance
class RAGNode(BaseNode):
"""
@ -42,6 +40,14 @@ class RAGNode(BaseNode):
def execute(self, state: dict) -> dict:
self.logger.info(f"--- Executing {self.node_name} Node ---")
try:
import qdrant_client
except ImportError:
raise ImportError("qdrant_client is not installed. Please install it using 'pip install qdrant-client'.")
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct, VectorParams, Distance
if self.node_config.get("client_type") in ["memory", None]:
client = QdrantClient(":memory:")
elif self.node_config.get("client_type") == "local_db":

View File

@ -99,5 +99,8 @@ class SearchInternetNode(BaseNode):
if len(answer) == 0:
raise ValueError("Zero results found for the search query.")
# Store both the URLs and considered_urls in the state
state.update({self.output[0]: answer})
state["considered_urls"] = answer # Add this as a backup
return state

View File

@ -7,8 +7,7 @@ Classes:
import threading
from contextlib import contextmanager
from langchain_community.callbacks import get_openai_callback
from langchain_community.callbacks.manager import get_bedrock_anthropic_callback
from langchain_community.callbacks.manager import get_openai_callback, get_bedrock_anthropic_callback
from langchain_openai import ChatOpenAI, AzureChatOpenAI
from langchain_aws import ChatBedrock
from .custom_callback import get_custom_callback

View File

@ -1,25 +1,45 @@
"""
Prettify the execution information of the graph.
"""
import pandas as pd
from typing import Union
def prettify_exec_info(complete_result: list[dict]) -> pd.DataFrame:
def prettify_exec_info(complete_result: list[dict], as_string: bool = True) -> Union[str, list[dict]]:
"""
Transforms the execution information of a graph into a DataFrame for enhanced visualization.
Formats the execution information of a graph showing node statistics.
Args:
complete_result (list[dict]): The complete execution information of the graph.
complete_result (list[dict]): The execution information containing node statistics.
as_string (bool, optional): If True, returns a formatted string table.
If False, returns the original list. Defaults to True.
Returns:
pd.DataFrame: A DataFrame that organizes the execution information
for better readability and analysis.
Example:
>>> prettify_exec_info([{'node': 'A', 'status': 'success'},
{'node': 'B', 'status': 'failure'}])
DataFrame with columns 'node' and 'status' showing execution results for each node.
Union[str, list[dict]]: A formatted string table if as_string=True,
otherwise the original list of dictionaries.
"""
if not as_string:
return complete_result
df_nodes = pd.DataFrame(complete_result)
if not complete_result:
return "Empty result"
return df_nodes
# Format the table
lines = []
lines.append("Node Statistics:")
lines.append("-" * 100)
lines.append(f"{'Node':<20} {'Tokens':<10} {'Prompt':<10} {'Compl.':<10} {'Requests':<10} {'Cost ($)':<10} {'Time (s)':<10}")
lines.append("-" * 100)
for item in complete_result:
node = item['node_name']
tokens = item['total_tokens']
prompt = item['prompt_tokens']
completion = item['completion_tokens']
requests = item['successful_requests']
cost = f"{item['total_cost_USD']:.4f}"
time = f"{item['exec_time']:.2f}"
lines.append(
f"{node:<20} {tokens:<10} {prompt:<10} {completion:<10} {requests:<10} {cost:<10} {time:<10}"
)
return "\n".join(lines)

View File

@ -41,7 +41,7 @@ def search_on_web(query: str, search_engine: str = "Google",
research = DuckDuckGoSearchResults(max_results=max_results)
res = research.run(query)
links = re.findall(r'https?://[^\s,\]]+', res)
return links
return links[:max_results]
elif search_engine.lower() == "bing":
headers = {
@ -66,7 +66,7 @@ def search_on_web(query: str, search_engine: str = "Google",
response = requests.get(url, params=params)
data = response.json()
limited_results = data["results"][:max_results]
limited_results = [result['url'] for result in data["results"][:max_results]]
return limited_results
else:

View File

@ -22,7 +22,7 @@ def detect_text(image, languages: list = ["en"]):
from surya.model.recognition.model import load_model as load_rec_model
from surya.model.recognition.processor import load_processor as load_rec_processor
except:
raise ImportError("The dependencies for screenshot scraping are not installed. Please install them using `pip install scrapegraphai[screenshot_scraper]`.")
raise ImportError("The dependencies for OCR are not installed. Please install them using `pip install scrapegraphai[ocr]`.")
langs = languages

View File

@ -1,10 +1,6 @@
"""
Tokenization utilities for Mistral models
"""
from mistral_common.protocol.instruct.messages import UserMessage
from mistral_common.protocol.instruct.request import ChatCompletionRequest
from mistral_common.protocol.instruct.tool_calls import Function, Tool
from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
from langchain_core.language_models.chat_models import BaseChatModel
from ..logging import get_logger
@ -31,6 +27,13 @@ def num_tokens_mistral(text: str, llm_model:BaseChatModel) -> int:
raise NotImplementedError(f"The model provider you are using ('{llm_model}') "
"does not give us a model name so we cannot identify which encoding to use")
try:
from mistral_common.tokens.tokenizers.mistral import MistralTokenizer
from mistral_common.protocol.instruct.messages import UserMessage
from mistral_common.protocol.instruct.request import ChatCompletionRequest
except ImportError:
raise ImportError("mistral_common is not installed. Please install it using 'pip install mistral-common'.")
tokenizer = MistralTokenizer.from_model(model)
tokenized = tokenizer.encode_chat_completion(

View File

@ -11,7 +11,6 @@ from scrapegraphai.nodes import (
from scrapegraphai.models import OneApi, DeepSeek
from langchain_openai import ChatOpenAI, AzureChatOpenAI
from langchain_ollama import ChatOllama
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_aws import ChatBedrock
@ -68,7 +67,6 @@ class TestAbstractGraph:
"api_version": "no version",
"azure_endpoint": "https://www.example.com/"},
AzureChatOpenAI),
({"model": "google_genai/gemini-pro", "google_api_key": "google-key-test"}, ChatGoogleGenerativeAI),
({"model": "ollama/llama2"}, ChatOllama),
({"model": "oneapi/qwen-turbo", "api_key": "oneapi-api-key"}, OneApi),
({"model": "deepseek/deepseek-coder", "api_key": "deepseek-api-key"}, DeepSeek),
@ -86,7 +84,6 @@ class TestAbstractGraph:
@pytest.mark.parametrize("llm_config, expected_model", [
({"model": "openai/gpt-3.5-turbo", "openai_api_key": "sk-randomtest001", "rate_limit": {"requests_per_second": 1}}, ChatOpenAI),
({"model": "azure_openai/gpt-3.5-turbo", "api_key": "random-api-key", "api_version": "no version", "azure_endpoint": "https://www.example.com/", "rate_limit": {"requests_per_second": 1}}, AzureChatOpenAI),
({"model": "google_genai/gemini-pro", "google_api_key": "google-key-test", "rate_limit": {"requests_per_second": 1}}, ChatGoogleGenerativeAI),
({"model": "ollama/llama2", "rate_limit": {"requests_per_second": 1}}, ChatOllama),
({"model": "oneapi/qwen-turbo", "api_key": "oneapi-api-key", "rate_limit": {"requests_per_second": 1}}, OneApi),
({"model": "deepseek/deepseek-coder", "api_key": "deepseek-api-key", "rate_limit": {"requests_per_second": 1}}, DeepSeek),

View File

@ -4,10 +4,8 @@ Module for testing the scrape graph class
import os
import pytest
import pandas as pd
from dotenv import load_dotenv
from scrapegraphai.graphs import ScrapeGraph
from scrapegraphai.utils import prettify_exec_info
load_dotenv()

View File

@ -4,10 +4,8 @@ Module for testing the smart scraper class
import os
import pytest
import pandas as pd
from dotenv import load_dotenv
from scrapegraphai.graphs import SmartScraperGraph
from scrapegraphai.utils import prettify_exec_info
load_dotenv()

View File

@ -4,10 +4,8 @@ Module for testing the smart scraper class
import os
import pytest
import pandas as pd
from dotenv import load_dotenv
from scrapegraphai.graphs import SmartScraperMultiLiteGraph
from scrapegraphai.utils import prettify_exec_info
load_dotenv()

View File

@ -3,7 +3,6 @@ Module for testing th smart scraper class
"""
import pytest
from scrapegraphai.graphs import SmartScraperGraph
from transformers import GPT2TokenizerFast
@pytest.fixture
@ -52,10 +51,3 @@ def test_get_execution_info(graph_config: dict):
assert graph_exec_info is not None
def test_gpt2_tokenizer_loading():
"""
Test loading of GPT2TokenizerFast
"""
tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
assert tokenizer is not None

View File

@ -4,10 +4,8 @@ Module for testing the smart scraper class
import os
import pytest
import pandas as pd
from dotenv import load_dotenv
from scrapegraphai.graphs import SmartScraperGraph
from scrapegraphai.utils import prettify_exec_info
load_dotenv()

1347
uv.lock

File diff suppressed because it is too large Load Diff