Fetch content from a URL. Args: url: URL to fetch Returns: Tuple of (content, file_extension)
(url: str)
| 155 | return False |
| 156 | |
| 157 | async def fetch_url(url: str) -> tuple[str, str | None]: |
| 158 | """ |
| 159 | Fetch content from a URL. |
| 160 | |
| 161 | Args: |
| 162 | url: URL to fetch |
| 163 | |
| 164 | Returns: |
| 165 | Tuple of (content, file_extension) |
| 166 | """ |
| 167 | print(f"Fetching content from URL: {url}") |
| 168 | |
| 169 | try: |
| 170 | async with aiohttp.ClientSession() as session, session.get(url) as response: |
| 171 | if response.status != 200: |
| 172 | raise ValueError(f"Failed to fetch URL {url}: HTTP {response.status}") |
| 173 | |
| 174 | # Get content type and extension |
| 175 | content_type = response.headers.get('Content-Type', '').lower() |
| 176 | |
| 177 | # Determine file extension based on content type |
| 178 | ext = None |
| 179 | if 'application/json' in content_type: |
| 180 | ext = '.json' |
| 181 | elif 'text/csv' in content_type or 'application/csv' in content_type: |
| 182 | ext = '.csv' |
| 183 | elif 'application/rss+xml' in content_type or 'application/atom+xml' in content_type or 'text/xml' in content_type: |
| 184 | ext = '.xml' |
| 185 | |
| 186 | # If extension not determined from content-type, try from URL |
| 187 | if ext is None: |
| 188 | path = urlparse(url).path |
| 189 | if '.' in path: |
| 190 | ext = os.path.splitext(path)[1].lower() |
| 191 | # For URLs ending with 'feed' or similar |
| 192 | elif any(kw in path.lower() for kw in ['/feed', '/rss', '/podcast']): |
| 193 | ext = '.xml' |
| 194 | |
| 195 | # Get content as text |
| 196 | content = await response.text() |
| 197 | return content, ext |
| 198 | except Exception as e: |
| 199 | print(f"Error fetching URL {url}: {e!s}") |
| 200 | raise |
| 201 | |
| 202 | async def save_url_content(url: str) -> tuple[str, str]: |
| 203 | """ |
no test coverage detected