scrapeMM 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scrapemm-0.3.2/scrapeMM.egg-info → scrapemm-0.3.3}/PKG-INFO +1 -1
- {scrapemm-0.3.2 → scrapemm-0.3.3}/pyproject.toml +1 -1
- {scrapemm-0.3.2 → scrapemm-0.3.3/scrapeMM.egg-info}/PKG-INFO +1 -1
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/retrieval.py +2 -2
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/decodo.py +3 -2
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/firecrawl.py +5 -2
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/util.py +22 -5
- scrapemm-0.3.3/scripts/example.py +7 -0
- scrapemm-0.3.2/scripts/example.py +0 -7
- {scrapemm-0.3.2 → scrapemm-0.3.3}/.gitignore +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/LICENSE +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/README.md +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/requirements.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/SOURCES.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/dependency_links.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/requires.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/top_level.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/__init__.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/common.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/__init__.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/base.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/bluesky.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/fb.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/instagram.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/telegram.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/tiktok.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/x.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/youtube.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/__init__.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/no_bot_domains.txt +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/ytdlp.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/secrets.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/util.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/setup.cfg +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/testing/test_retrieval.py +0 -0
- {scrapemm-0.3.2 → scrapemm-0.3.3}/testing/test_utils.py +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, Collection
|
|
3
3
|
from traceback import format_exc
|
|
4
4
|
|
|
5
5
|
import aiohttp
|
|
@@ -14,7 +14,7 @@ METHODS = ["integrations", "firecrawl", "decodo"]
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
async def retrieve(
|
|
17
|
-
urls: str |
|
|
17
|
+
urls: str | Collection[str],
|
|
18
18
|
remove_urls: bool = False,
|
|
19
19
|
show_progress: bool = True,
|
|
20
20
|
actions: list[dict] = None,
|
|
@@ -6,7 +6,7 @@ import aiohttp
|
|
|
6
6
|
from ezmm import MultimodalSequence
|
|
7
7
|
|
|
8
8
|
from scrapemm.secrets import get_secret
|
|
9
|
-
from scrapemm.scraping.util import to_multimodal_sequence
|
|
9
|
+
from scrapemm.scraping.util import to_multimodal_sequence, get_domain_root
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger("scrapeMM")
|
|
12
12
|
|
|
@@ -74,7 +74,8 @@ class Decodo:
|
|
|
74
74
|
if format == "html":
|
|
75
75
|
return html
|
|
76
76
|
else:
|
|
77
|
-
|
|
77
|
+
domain_root = get_domain_root(url)
|
|
78
|
+
return await to_multimodal_sequence(html, remove_urls=remove_urls, session=session, domain_root=domain_root)
|
|
78
79
|
return None
|
|
79
80
|
|
|
80
81
|
async def _call_decodo(self, url: str,
|
|
@@ -7,7 +7,7 @@ from ezmm import MultimodalSequence
|
|
|
7
7
|
|
|
8
8
|
from scrapemm.common import get_config_var, update_config
|
|
9
9
|
from scrapemm.util import read_urls_from_file, get_domain
|
|
10
|
-
from scrapemm.scraping.util import find_firecrawl, to_multimodal_sequence, firecrawl_is_running
|
|
10
|
+
from scrapemm.scraping.util import find_firecrawl, to_multimodal_sequence, firecrawl_is_running, get_domain_root
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger("scrapeMM")
|
|
13
13
|
|
|
@@ -58,6 +58,7 @@ class Firecrawl:
|
|
|
58
58
|
|
|
59
59
|
def connect(self):
|
|
60
60
|
from firecrawl import AsyncFirecrawl
|
|
61
|
+
logging.getLogger("firecrawl").setLevel(logging.WARNING)
|
|
61
62
|
self.firecrawl_url = locate_firecrawl()
|
|
62
63
|
self._firecrawl = AsyncFirecrawl(api_url=self.firecrawl_url)
|
|
63
64
|
|
|
@@ -88,7 +89,9 @@ class Firecrawl:
|
|
|
88
89
|
if format == "html":
|
|
89
90
|
return html
|
|
90
91
|
else:
|
|
91
|
-
|
|
92
|
+
domain_root = get_domain_root(url)
|
|
93
|
+
return await to_multimodal_sequence(html, remove_urls=remove_urls, session=session, domain_root=domain_root)
|
|
94
|
+
return None
|
|
92
95
|
|
|
93
96
|
|
|
94
97
|
fire = Firecrawl()
|
|
@@ -54,6 +54,7 @@ def postprocess_scraped(text: str) -> str:
|
|
|
54
54
|
|
|
55
55
|
async def resolve_media_hyperlinks(
|
|
56
56
|
text: str, session: aiohttp.ClientSession,
|
|
57
|
+
domain_root: str = None,
|
|
57
58
|
remove_urls: bool = False,
|
|
58
59
|
) -> Optional[MultimodalSequence]:
|
|
59
60
|
"""Downloads all media that are hyperlinked in the provided Markdown text.
|
|
@@ -65,19 +66,21 @@ async def resolve_media_hyperlinks(
|
|
|
65
66
|
|
|
66
67
|
# Extract URLs and base64-encoded data from the text
|
|
67
68
|
hyperlinks = get_markdown_hyperlinks(text)
|
|
68
|
-
|
|
69
|
+
hrefs_urls = dict()
|
|
69
70
|
data_uris = set()
|
|
70
71
|
for _, _, href in hyperlinks:
|
|
71
72
|
if is_url(href):
|
|
72
|
-
|
|
73
|
+
hrefs_urls[href] = href
|
|
74
|
+
elif domain_root and is_root_relative_url(href):
|
|
75
|
+
hrefs_urls[href] = f"{domain_root}{href}"
|
|
73
76
|
elif is_data_uri(href):
|
|
74
77
|
data_uris.add(href)
|
|
75
78
|
|
|
76
79
|
# Try to download media for each URL
|
|
77
|
-
tasks = [download_item(url, session=session) for url in
|
|
80
|
+
tasks = [download_item(url, session=session) for url in hrefs_urls.values()]
|
|
78
81
|
media: list[Item | None] = await run_with_semaphore(tasks, limit=100, show_progress=False)
|
|
79
82
|
|
|
80
|
-
href_media = dict(zip(
|
|
83
|
+
href_media = dict(zip(hrefs_urls.keys(), media))
|
|
81
84
|
|
|
82
85
|
# Convert each base64-encoded data to the respective medium
|
|
83
86
|
for data_uri in data_uris:
|
|
@@ -102,15 +105,29 @@ async def resolve_media_hyperlinks(
|
|
|
102
105
|
|
|
103
106
|
|
|
104
107
|
def is_url(href: str) -> bool:
|
|
105
|
-
"""Returns True iff the given string is
|
|
108
|
+
"""Returns True iff the given string is an absolute HTTP URL."""
|
|
106
109
|
return re.match(URL_REGEX, href) is not None
|
|
107
110
|
|
|
108
111
|
|
|
112
|
+
def is_root_relative_url(href: str) -> bool:
|
|
113
|
+
"""Returns True iff the given string is a root-relative URL."""
|
|
114
|
+
return href.startswith("/")
|
|
115
|
+
|
|
116
|
+
|
|
109
117
|
def is_data_uri(href: str) -> bool:
|
|
110
118
|
"""Returns True iff the given string is a valid data URI."""
|
|
111
119
|
return re.match(DATA_URI_REGEX, href) is not None
|
|
112
120
|
|
|
113
121
|
|
|
122
|
+
def get_domain_root(url: str) -> Optional[str]:
|
|
123
|
+
"""Extracts the domain root from the given URL. Allows for missing http(s) prefix."""
|
|
124
|
+
match = re.match(r"(:?https?://)?([^/]+)", url)
|
|
125
|
+
if match:
|
|
126
|
+
return match.group(0)
|
|
127
|
+
else:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
114
131
|
def get_markdown_hyperlinks(text: str) -> list[tuple[str, str, str]]:
|
|
115
132
|
"""Extracts all web hyperlinks from the given markdown-formatted string. Returns
|
|
116
133
|
a list of fullmatch-hypertext-URL-triples."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|