scrapeMM 0.3.1__tar.gz → 0.3.2__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.1/scrapeMM.egg-info → scrapemm-0.3.2}/PKG-INFO +1 -1
  2. {scrapemm-0.3.1 → scrapemm-0.3.2}/pyproject.toml +1 -1
  3. {scrapemm-0.3.1 → scrapemm-0.3.2/scrapeMM.egg-info}/PKG-INFO +1 -1
  4. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/retrieval.py +5 -1
  5. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/decodo.py +13 -17
  6. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/util.py +5 -3
  7. scrapemm-0.3.2/scripts/example.py +7 -0
  8. scrapemm-0.3.1/scripts/example.py +0 -8
  9. {scrapemm-0.3.1 → scrapemm-0.3.2}/.gitignore +0 -0
  10. {scrapemm-0.3.1 → scrapemm-0.3.2}/LICENSE +0 -0
  11. {scrapemm-0.3.1 → scrapemm-0.3.2}/README.md +0 -0
  12. {scrapemm-0.3.1 → scrapemm-0.3.2}/requirements.txt +0 -0
  13. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapeMM.egg-info/SOURCES.txt +0 -0
  14. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapeMM.egg-info/dependency_links.txt +0 -0
  15. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapeMM.egg-info/requires.txt +0 -0
  16. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapeMM.egg-info/top_level.txt +0 -0
  17. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/__init__.py +0 -0
  18. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/common.py +0 -0
  19. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/__init__.py +0 -0
  20. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/base.py +0 -0
  21. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/bluesky.py +0 -0
  22. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/fb.py +0 -0
  23. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/instagram.py +0 -0
  24. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/telegram.py +0 -0
  25. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/tiktok.py +0 -0
  26. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/x.py +0 -0
  27. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/integrations/youtube.py +0 -0
  28. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/__init__.py +0 -0
  29. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/firecrawl.py +0 -0
  30. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/no_bot_domains.txt +0 -0
  31. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/scraping/ytdlp.py +0 -0
  32. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/secrets.py +0 -0
  33. {scrapemm-0.3.1 → scrapemm-0.3.2}/scrapemm/util.py +0 -0
  34. {scrapemm-0.3.1 → scrapemm-0.3.2}/setup.cfg +0 -0
  35. {scrapemm-0.3.1 → scrapemm-0.3.2}/testing/test_retrieval.py +0 -0
  36. {scrapemm-0.3.1 → scrapemm-0.3.2}/testing/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrapeMM
3
- Version: 0.3.1
3
+ Version: 0.3.2
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.1"
3
+ version = "0.3.2"
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.1
3
+ Version: 0.3.2
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
@@ -114,7 +114,11 @@ async def _retrieve_single(
114
114
  continue
115
115
 
116
116
  logger.debug(f"Trying method: {method_name}")
117
- result = await method_map[method_name]()
117
+ try:
118
+ result = await method_map[method_name]()
119
+ except Exception as e:
120
+ logger.warning(f"Error while retrieving with method '{method_name}': {e}")
121
+ result = None
118
122
 
119
123
  if result is not None:
120
124
  logger.debug(f"Successfully retrieved with method: {method_name}")
@@ -111,8 +111,6 @@ class Decodo:
111
111
  # Create basic auth
112
112
  auth = aiohttp.BasicAuth(self.username, self.password)
113
113
 
114
- # logger.debug(f"Decodo request payload: {payload}")
115
-
116
114
  try:
117
115
  async with session.post(
118
116
  self.DECODO_API_URL,
@@ -121,26 +119,17 @@ class Decodo:
121
119
  auth=auth,
122
120
  timeout=aiohttp.ClientTimeout(total=timeout)
123
121
  ) as response:
124
-
122
+ # Validate response health
125
123
  if response.status != 200:
126
- # Try to get the error message from response
127
- try:
128
- error_body = await response.json()
129
- error_msg = error_body.get('message', error_body)
130
- except:
131
- error_msg = await response.text()
132
-
133
- logger.info(
134
- f"Failed to scrape {url} with Decodo\n"
135
- f"Status code: {response.status} - Reason: {response.reason}\n"
136
- f"Error details: {error_msg}"
137
- )
138
-
124
+ logger.debug("Communication with Decodo API failed.")
139
125
  match response.status:
126
+ case 400:
127
+ logger.debug("Error 400: Bad request. If you use JavaScript, make sure you have the "
128
+ "Advanced plan subscription.")
140
129
  case 401:
141
130
  logger.error("Error 401: Unauthorized. Check your Decodo credentials.")
142
131
  case 402:
143
- logger.debug("Error 402: Payment required. Check your Decodo subscription.")
132
+ logger.error("Error 402: Payment required. Check your Decodo subscription.")
144
133
  case 403:
145
134
  logger.debug("Error 403: Forbidden.")
146
135
  case 408:
@@ -157,6 +146,13 @@ class Decodo:
157
146
  # Parse response
158
147
  json_response = await response.json()
159
148
 
149
+ # Validate if scrape was successful
150
+ if json_response.get("status") == "failed":
151
+ status_code = json_response.get("status_code")
152
+ message = json_response.get("message")
153
+ logger.info(f"Decodo failed to scrape. Error {status_code}: {message}")
154
+ return None
155
+
160
156
  # Extract HTML content from results
161
157
  if "results" in json_response and len(json_response["results"]) > 0:
162
158
  result = json_response["results"][0]
@@ -6,6 +6,7 @@ from typing import Optional
6
6
 
7
7
  import aiohttp
8
8
  import requests
9
+ from PIL import UnidentifiedImageError
9
10
  from ezmm import MultimodalSequence, download_item, Item, Image, Video
10
11
  from markdownify import markdownify as md
11
12
  from requests.exceptions import ReadTimeout, ConnectionError, RetryError
@@ -159,7 +160,8 @@ def from_base64(b64_data: str, mime_type: str = "image/jpeg") -> Optional[Item]:
159
160
  else:
160
161
  raise ValueError(f"Unsupported media type: {mime_type}")
161
162
  except binascii.Error: # base64 validation failed
162
- return
163
+ return None
164
+ except UnidentifiedImageError: # Pillow could not identify image format
165
+ return None
163
166
  except Exception as e:
164
- logger.warning(f"Error decoding base64 data!\n"
165
- f"{type(e).__name__}: {e}")
167
+ logger.debug(f"Error decoding {mime_type} base64 data. \n {type(e).__name__}: {e}")
@@ -0,0 +1,7 @@
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)
@@ -1,8 +0,0 @@
1
- from scrapemm import retrieve
2
- import asyncio
3
-
4
- if __name__ == "__main__":
5
- url = "https://www.tiktok.com/@xxxx.xxxx5743/video/7521704371109793046"
6
- loop = asyncio.get_event_loop()
7
- result = loop.run_until_complete(retrieve(url))
8
- 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