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.
Files changed (36) hide show
  1. {scrapemm-0.3.2/scrapeMM.egg-info → scrapemm-0.3.3}/PKG-INFO +1 -1
  2. {scrapemm-0.3.2 → scrapemm-0.3.3}/pyproject.toml +1 -1
  3. {scrapemm-0.3.2 → scrapemm-0.3.3/scrapeMM.egg-info}/PKG-INFO +1 -1
  4. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/retrieval.py +2 -2
  5. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/decodo.py +3 -2
  6. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/firecrawl.py +5 -2
  7. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/util.py +22 -5
  8. scrapemm-0.3.3/scripts/example.py +7 -0
  9. scrapemm-0.3.2/scripts/example.py +0 -7
  10. {scrapemm-0.3.2 → scrapemm-0.3.3}/.gitignore +0 -0
  11. {scrapemm-0.3.2 → scrapemm-0.3.3}/LICENSE +0 -0
  12. {scrapemm-0.3.2 → scrapemm-0.3.3}/README.md +0 -0
  13. {scrapemm-0.3.2 → scrapemm-0.3.3}/requirements.txt +0 -0
  14. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/SOURCES.txt +0 -0
  15. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/dependency_links.txt +0 -0
  16. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/requires.txt +0 -0
  17. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapeMM.egg-info/top_level.txt +0 -0
  18. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/__init__.py +0 -0
  19. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/common.py +0 -0
  20. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/__init__.py +0 -0
  21. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/base.py +0 -0
  22. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/bluesky.py +0 -0
  23. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/fb.py +0 -0
  24. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/instagram.py +0 -0
  25. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/telegram.py +0 -0
  26. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/tiktok.py +0 -0
  27. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/x.py +0 -0
  28. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/integrations/youtube.py +0 -0
  29. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/__init__.py +0 -0
  30. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/no_bot_domains.txt +0 -0
  31. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/scraping/ytdlp.py +0 -0
  32. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/secrets.py +0 -0
  33. {scrapemm-0.3.2 → scrapemm-0.3.3}/scrapemm/util.py +0 -0
  34. {scrapemm-0.3.2 → scrapemm-0.3.3}/setup.cfg +0 -0
  35. {scrapemm-0.3.2 → scrapemm-0.3.3}/testing/test_retrieval.py +0 -0
  36. {scrapemm-0.3.2 → scrapemm-0.3.3}/testing/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrapeMM
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: LLM-friendly scraper for media and text from social media and the open web.
5
5
  Author-email: Mark Rothermel <mark.rothermel@tu-darmstadt.de>
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "scrapeMM"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "LLM-friendly scraper for media and text from social media and the open web."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.8"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrapeMM
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: LLM-friendly scraper for media and text from social media and the open web.
5
5
  Author-email: Mark Rothermel <mark.rothermel@tu-darmstadt.de>
6
6
  License-Expression: Apache-2.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 | list[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
- return await to_multimodal_sequence(html, remove_urls=remove_urls, session=session)
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
- return await to_multimodal_sequence(html, remove_urls=remove_urls, session=session)
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
- urls = set()
69
+ hrefs_urls = dict()
69
70
  data_uris = set()
70
71
  for _, _, href in hyperlinks:
71
72
  if is_url(href):
72
- urls.add(href)
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 urls]
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(urls, media))
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 a valid URL."""
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."""
@@ -0,0 +1,7 @@
1
+ from scrapemm import retrieve
2
+ import asyncio
3
+
4
+ if __name__ == "__main__":
5
+ url = "https://factuel.afp.com/doc.afp.com.43ZN7NP"
6
+ result = asyncio.run(retrieve(url, methods=["decodo"]))
7
+ print(result)
@@ -1,7 +0,0 @@
1
- from scrapemm import retrieve
2
- import asyncio
3
-
4
- if __name__ == "__main__":
5
- url = "https://verafiles.org"
6
- result = asyncio.run(retrieve(url))
7
- print(result)
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