From 756f54466d37f00850343cc8ed57979a0d587c50 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 20 May 2024 17:24:21 -0700 Subject: [PATCH 01/21] Nick: allowed keywords for now --- apps/api/src/scraper/WebScraper/utils/blocklist.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/api/src/scraper/WebScraper/utils/blocklist.ts b/apps/api/src/scraper/WebScraper/utils/blocklist.ts index a50e42e..ededfc7 100644 --- a/apps/api/src/scraper/WebScraper/utils/blocklist.ts +++ b/apps/api/src/scraper/WebScraper/utils/blocklist.ts @@ -1,6 +1,7 @@ const socialMediaBlocklist = [ 'facebook.com', 'twitter.com', + 'x.com', 'instagram.com', 'linkedin.com', 'pinterest.com', @@ -14,12 +15,18 @@ const socialMediaBlocklist = [ 'telegram.org', ]; -const allowedUrls = [ - 'linkedin.com/pulse' +const allowedKeywords = [ + 'pulse', + 'privacy', + 'terms', + 'policy', + 'user-agreement', + 'legal', + 'help' ]; export function isUrlBlocked(url: string): boolean { - if (allowedUrls.some(allowedUrl => url.includes(allowedUrl))) { + if (allowedKeywords.some(keyword => url.includes(keyword))) { return false; } From 7f64fe884a57441ab1103fab8d8ca44a1e92bfd7 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 20 May 2024 17:26:01 -0700 Subject: [PATCH 02/21] Update blocklist.ts --- apps/api/src/scraper/WebScraper/utils/blocklist.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/api/src/scraper/WebScraper/utils/blocklist.ts b/apps/api/src/scraper/WebScraper/utils/blocklist.ts index ededfc7..c3d37c4 100644 --- a/apps/api/src/scraper/WebScraper/utils/blocklist.ts +++ b/apps/api/src/scraper/WebScraper/utils/blocklist.ts @@ -22,7 +22,14 @@ const allowedKeywords = [ 'policy', 'user-agreement', 'legal', - 'help' + 'help', + 'support', + 'contact', + 'about', + 'careers', + 'blog', + 'press', + 'conditions', ]; export function isUrlBlocked(url: string): boolean { From c47dae13a93d1b54680cb948230173f0de26c68a Mon Sep 17 00:00:00 2001 From: youqiang Date: Tue, 21 May 2024 14:53:57 +0800 Subject: [PATCH 03/21] update: wait until body attached in playwright-service --- apps/playwright-service/main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py index c28bc63..a044597 100644 --- a/apps/playwright-service/main.py +++ b/apps/playwright-service/main.py @@ -5,6 +5,7 @@ from pydantic import BaseModel app = FastAPI() + class UrlModel(BaseModel): url: str wait: int = None @@ -29,9 +30,12 @@ async def shutdown_event(): async def root(body: UrlModel): context = await browser.new_context() page = await context.new_page() - await page.goto(body.url, timeout=15000) # Set max timeout to 15s - if body.wait: # Check if wait parameter is provided in the request body - await page.wait_for_timeout(body.wait) # Convert seconds to milliseconds for playwright + await page.goto( + body.url, + wait_until="load", + timeout=body.wait if body.wait else 15, + ) + await page.wait_for_selector("body", state="attached") page_content = await page.content() await context.close() json_compatible_item_data = {"content": page_content} From f9ae1729b6beeb32bde3d3106baee6cc5bbf3bd6 Mon Sep 17 00:00:00 2001 From: rafaelsideguide <150964962+rafaelsideguide@users.noreply.github.com> Date: Wed, 22 May 2024 09:40:38 -0300 Subject: [PATCH 04/21] Update firecrawl.py --- apps/python-sdk/firecrawl/firecrawl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 98cb8ed..b946686 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -45,7 +45,7 @@ class FirecrawlApp: ) if response.status_code == 200: response = response.json() - if response['success']: + if response['success'] and 'data' in response: return response['data'] else: raise Exception(f'Failed to scrape URL. Error: {response["error"]}') @@ -70,7 +70,7 @@ class FirecrawlApp: ) if response.status_code == 200: response = response.json() - if response['success'] == True: + if response['success'] and 'data' in response: return response['data'] else: raise Exception(f'Failed to search. Error: {response["error"]}') From 3e63985e53bc795c4633b218b71d5851691fc585 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 22 May 2024 10:40:47 -0700 Subject: [PATCH 05/21] Update main.py --- apps/playwright-service/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py index a044597..544f113 100644 --- a/apps/playwright-service/main.py +++ b/apps/playwright-service/main.py @@ -35,7 +35,6 @@ async def root(body: UrlModel): wait_until="load", timeout=body.wait if body.wait else 15, ) - await page.wait_for_selector("body", state="attached") page_content = await page.content() await context.close() json_compatible_item_data = {"content": page_content} From 3aa5f266272039cda4fb6407d57a218623af6e93 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 22 May 2024 10:45:43 -0700 Subject: [PATCH 06/21] Update main.py --- apps/playwright-service/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py index 544f113..a2f5e52 100644 --- a/apps/playwright-service/main.py +++ b/apps/playwright-service/main.py @@ -33,8 +33,12 @@ async def root(body: UrlModel): await page.goto( body.url, wait_until="load", - timeout=body.wait if body.wait else 15, + timeout=body.timeout if body.timeout else 15000, ) + # Wait != timeout. Wait is the time to wait after the page is loaded - useful in some cases were "load" / "networkidle" is not enough + if body.wait: + await page.wait_for_timeout(body.wait) + # await page.wait_for_selector("body", state="attached") page_content = await page.content() await context.close() json_compatible_item_data = {"content": page_content} From 4e39701644e724dd01bceebf4488aae1ed7b7900 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 22 May 2024 12:59:56 -0700 Subject: [PATCH 07/21] Update main.py --- apps/playwright-service/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py index a2f5e52..8344adc 100644 --- a/apps/playwright-service/main.py +++ b/apps/playwright-service/main.py @@ -38,7 +38,7 @@ async def root(body: UrlModel): # Wait != timeout. Wait is the time to wait after the page is loaded - useful in some cases were "load" / "networkidle" is not enough if body.wait: await page.wait_for_timeout(body.wait) - # await page.wait_for_selector("body", state="attached") + page_content = await page.content() await context.close() json_compatible_item_data = {"content": page_content} From 8d041c05b461a8ecd3a90d17f47ce32611b429be Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:00:56 +1000 Subject: [PATCH 08/21] rearranged logic for FIRECRAWL_API_URL It would not use the ENV unless the param was set to None which was counter-intuitive. --- apps/python-sdk/firecrawl/firecrawl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 98cb8ed..2b7121a 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -4,11 +4,11 @@ import requests import time class FirecrawlApp: - def __init__(self, api_key=None, api_url='https://api.firecrawl.dev'): + def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None) -> None: self.api_key = api_key or os.getenv('FIRECRAWL_API_KEY') if self.api_key is None: raise ValueError('No API key provided') - self.api_url = api_url or os.getenv('FIRECRAWL_API_URL') + self.api_url = api_url or os.getenv('FIRECRAWL_API_URL', 'https://api.firecrawl.dev') From 971e1f85c45341645214d0f8ea3fa5de202e78fb Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:03:58 +1000 Subject: [PATCH 09/21] Added module docstring PyLint C0114 - missing-module-docstring --- apps/python-sdk/firecrawl/firecrawl.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 2b7121a..23934cf 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -1,3 +1,15 @@ +""" +FirecrawlApp Module + +This module provides a class `FirecrawlApp` for interacting with the Firecrawl API. +It includes methods to scrape URLs, perform searches, initiate and monitor crawl jobs, +and check the status of these jobs. The module uses requests for HTTP communication +and handles retries for certain HTTP status codes. + +Classes: + - FirecrawlApp: Main class for interacting with the Firecrawl API. +""" + import os from typing import Any, Dict, Optional import requests From 8adf2b71322238823fc5c770723bcaca548311a6 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:20:32 +1000 Subject: [PATCH 10/21] Added Docstrings for functions PyLint C0116: Missing function or method docstring (missing-function-docstring) --- apps/python-sdk/firecrawl/firecrawl.py | 128 ++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 23934cf..9aa9359 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -11,11 +11,20 @@ Classes: """ import os -from typing import Any, Dict, Optional -import requests import time +from typing import Any, Dict, Optional + +import requests + class FirecrawlApp: + """ + Initialize the FirecrawlApp instance. + + Args: + api_key (Optional[str]): API key for authenticating with the Firecrawl API. + api_url (Optional[str]): Base URL for the Firecrawl API. + """ def __init__(self, api_key: Optional[str] = None, api_url: Optional[str] = None) -> None: self.api_key = api_key or os.getenv('FIRECRAWL_API_KEY') if self.api_key is None: @@ -25,6 +34,20 @@ class FirecrawlApp: def scrape_url(self, url: str, params: Optional[Dict[str, Any]] = None) -> Any: + """ + Scrape the specified URL using the Firecrawl API. + + Args: + url (str): The URL to scrape. + params (Optional[Dict[str, Any]]): Additional parameters for the scrape request. + + Returns: + Any: The scraped data if the request is successful. + + Raises: + Exception: If the scrape request fails. + """ + headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}' @@ -68,6 +91,19 @@ class FirecrawlApp: raise Exception(f'Failed to scrape URL. Status code: {response.status_code}') def search(self, query, params=None): + """ + Perform a search using the Firecrawl API. + + Args: + query (str): The search query. + params (Optional[Dict[str, Any]]): Additional parameters for the search request. + + Returns: + Any: The search results if the request is successful. + + Raises: + Exception: If the search request fails. + """ headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}' @@ -94,6 +130,21 @@ class FirecrawlApp: raise Exception(f'Failed to search. Status code: {response.status_code}') def crawl_url(self, url, params=None, wait_until_done=True, timeout=2): + """ + Initiate a crawl job for the specified URL using the Firecrawl API. + + Args: + url (str): The URL to crawl. + params (Optional[Dict[str, Any]]): Additional parameters for the crawl request. + wait_until_done (bool): Whether to wait until the crawl job is completed. + timeout (int): Timeout between status checks when waiting for job completion. + + Returns: + Any: The crawl job ID or the crawl results if waiting until completion. + + Raises: + Exception: If the crawl job initiation or monitoring fails. + """ headers = self._prepare_headers() json_data = {'url': url} if params: @@ -109,6 +160,18 @@ class FirecrawlApp: self._handle_error(response, 'start crawl job') def check_crawl_status(self, job_id): + """ + Check the status of a crawl job using the Firecrawl API. + + Args: + job_id (str): The ID of the crawl job. + + Returns: + Any: The status of the crawl job. + + Raises: + Exception: If the status check request fails. + """ headers = self._prepare_headers() response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers) if response.status_code == 200: @@ -117,12 +180,34 @@ class FirecrawlApp: self._handle_error(response, 'check crawl status') def _prepare_headers(self): + """ + Prepare the headers for API requests. + + Returns: + Dict[str, str]: The headers including content type and authorization. + """ return { 'Content-Type': 'application/json', 'Authorization': f'Bearer {self.api_key}' } def _post_request(self, url, data, headers, retries=3, backoff_factor=0.5): + """ + Make a POST request with retries. + + Args: + url (str): The URL to send the POST request to. + data (Dict[str, Any]): The JSON data to include in the POST request. + headers (Dict[str, str]): The headers to include in the POST request. + retries (int): Number of retries for the request. + backoff_factor (float): Backoff factor for retries. + + Returns: + requests.Response: The response from the POST request. + + Raises: + requests.RequestException: If the request fails after the specified retries. + """ for attempt in range(retries): response = requests.post(url, headers=headers, json=data) if response.status_code == 502: @@ -132,6 +217,21 @@ class FirecrawlApp: return response def _get_request(self, url, headers, retries=3, backoff_factor=0.5): + """ + Make a GET request with retries. + + Args: + url (str): The URL to send the GET request to. + headers (Dict[str, str]): The headers to include in the GET request. + retries (int): Number of retries for the request. + backoff_factor (float): Backoff factor for retries. + + Returns: + requests.Response: The response from the GET request. + + Raises: + requests.RequestException: If the request fails after the specified retries. + """ for attempt in range(retries): response = requests.get(url, headers=headers) if response.status_code == 502: @@ -141,6 +241,20 @@ class FirecrawlApp: return response def _monitor_job_status(self, job_id, headers, timeout): + """ + Monitor the status of a crawl job until completion. + + Args: + job_id (str): The ID of the crawl job. + headers (Dict[str, str]): The headers to include in the status check requests. + timeout (int): Timeout between status checks. + + Returns: + Any: The crawl results if the job is completed successfully. + + Raises: + Exception: If the job fails or an error occurs during status checks. + """ import time while True: status_response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers) @@ -161,6 +275,16 @@ class FirecrawlApp: self._handle_error(status_response, 'check crawl status') def _handle_error(self, response, action): + """ + Handle errors from API responses. + + Args: + response (requests.Response): The response object from the API request. + action (str): Description of the action that was being performed. + + Raises: + Exception: An exception with a message containing the status code and error details from the response. + """ if response.status_code in [402, 408, 409, 500]: error_message = response.json().get('error', 'Unknown error occurred') raise Exception(f'Failed to {action}. Status code: {response.status_code}. Error: {error_message}') From 6216c8532295ba1d8abdd9d944c4708cf66f3036 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:21:32 +1000 Subject: [PATCH 11/21] Time module already imported Pylint W0404: Reimport 'time' (imported line 16) (reimported) C0415: Import outside toplevel (time) (import-outside-toplevel) --- apps/python-sdk/firecrawl/firecrawl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 9aa9359..d96db4e 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -255,7 +255,6 @@ class FirecrawlApp: Raises: Exception: If the job fails or an error occurs during status checks. """ - import time while True: status_response = self._get_request(f'{self.api_url}/v0/crawl/status/{job_id}', headers) if status_response.status_code == 200: From 96b19172a167467a9fba9dfc018fc27e595c9056 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:30:23 +1000 Subject: [PATCH 12/21] Removed trailing whitespace PyLint C0303: Trailing whitespace (trailing-whitespace) --- apps/python-sdk/firecrawl/firecrawl.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index d96db4e..7612e5d 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -30,9 +30,6 @@ class FirecrawlApp: if self.api_key is None: raise ValueError('No API key provided') self.api_url = api_url or os.getenv('FIRECRAWL_API_URL', 'https://api.firecrawl.dev') - - - def scrape_url(self, url: str, params: Optional[Dict[str, Any]] = None) -> Any: """ Scrape the specified URL using the Firecrawl API. @@ -54,7 +51,7 @@ class FirecrawlApp: } # Prepare the base scrape parameters with the URL scrape_params = {'url': url} - + # If there are additional params, process them if params: # Initialize extractorOptions if present @@ -67,7 +64,7 @@ class FirecrawlApp: extractor_options['mode'] = extractor_options.get('mode', 'llm-extraction') # Update the scrape_params with the processed extractorOptions scrape_params['extractorOptions'] = extractor_options - + # Include any other params directly at the top level of scrape_params for key, value in params.items(): if key != 'extractorOptions': @@ -89,7 +86,7 @@ class FirecrawlApp: raise Exception(f'Failed to scrape URL. Status code: {response.status_code}. Error: {error_message}') else: raise Exception(f'Failed to scrape URL. Status code: {response.status_code}') - + def search(self, query, params=None): """ Perform a search using the Firecrawl API. @@ -122,7 +119,7 @@ class FirecrawlApp: return response['data'] else: raise Exception(f'Failed to search. Error: {response["error"]}') - + elif response.status_code in [402, 409, 500]: error_message = response.json().get('error', 'Unknown error occurred') raise Exception(f'Failed to search. Status code: {response.status_code}. Error: {error_message}') @@ -283,7 +280,7 @@ class FirecrawlApp: Raises: Exception: An exception with a message containing the status code and error details from the response. - """ + """ if response.status_code in [402, 408, 409, 500]: error_message = response.json().get('error', 'Unknown error occurred') raise Exception(f'Failed to {action}. Status code: {response.status_code}. Error: {error_message}') From 7d2efe5acb5595c53323565046564927143370de Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:39:19 +1000 Subject: [PATCH 13/21] Added request timeouts connection timeout to 5 seconds and the response timeout to 10 PyLint W3101 --- apps/python-sdk/firecrawl/firecrawl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 7612e5d..d986407 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -73,7 +73,8 @@ class FirecrawlApp: response = requests.post( f'{self.api_url}/v0/scrape', headers=headers, - json=scrape_params + json=scrape_params, + timeout=(5,10) ) if response.status_code == 200: response = response.json() @@ -111,7 +112,8 @@ class FirecrawlApp: response = requests.post( f'{self.api_url}/v0/search', headers=headers, - json=json_data + json=json_data, + timeout=(5,10) ) if response.status_code == 200: response = response.json() From 48e91c89e7bbd97af388c8edcc3cdc173572d3f5 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:42:07 +1000 Subject: [PATCH 14/21] Removed unnecessary If block PyLint R1731 --- apps/python-sdk/firecrawl/firecrawl.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index d986407..62db7a2 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -264,8 +264,7 @@ class FirecrawlApp: else: raise Exception('Crawl job completed but no data was returned') elif status_data['status'] in ['active', 'paused', 'pending', 'queued']: - if timeout < 2: - timeout = 2 + timeout=max(timeout,2) time.sleep(timeout) # Wait for the specified timeout before checking again else: raise Exception(f'Crawl job failed or was stopped. Status: {status_data["status"]}') From 5c21aed9c783beb6775c551a8a1f631778ca1af7 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:45:56 +1000 Subject: [PATCH 15/21] adding pylintrc to allow longer lines --- apps/python-sdk/.pylintrc | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 apps/python-sdk/.pylintrc diff --git a/apps/python-sdk/.pylintrc b/apps/python-sdk/.pylintrc new file mode 100644 index 0000000..a580885 --- /dev/null +++ b/apps/python-sdk/.pylintrc @@ -0,0 +1,2 @@ +[FORMAT] +max-line-length = 120 \ No newline at end of file From 106c18d11f851595413256210af3d0f7158ba8f9 Mon Sep 17 00:00:00 2001 From: Matt Joyce Date: Thu, 23 May 2024 08:57:53 +1000 Subject: [PATCH 16/21] Use truthiness check for 'success' key in API response PyLint C0121 --- apps/python-sdk/firecrawl/firecrawl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 62db7a2..9e3011c 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -117,7 +117,7 @@ class FirecrawlApp: ) if response.status_code == 200: response = response.json() - if response['success'] == True: + if response['success']: return response['data'] else: raise Exception(f'Failed to search. Error: {response["error"]}') From 9562c837eb757f49174d05c8ae3869645b7f5859 Mon Sep 17 00:00:00 2001 From: Rafael Miller <150964962+rafaelsideguide@users.noreply.github.com> Date: Fri, 24 May 2024 09:34:43 -0300 Subject: [PATCH 17/21] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 35 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 26 +++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..bb47b47 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the Bug** +Provide a clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the issue: +1. Configure the environment or settings with '...' +2. Run the command '...' +3. Observe the error or unexpected output at '...' +4. Log output/error message + +**Expected Behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots or copies of the command line output to help explain the issue. + +**Environment (please complete the following information):** +- OS: [e.g. macOS, Linux, Windows] +- Firecrawl Version: [e.g. 1.2.3] +- Node.js Version: [e.g. 14.x] + +**Logs** +If applicable, include detailed logs to help understand the problem. + +**Additional Context** +Add any other context about the problem here, such as configuration specifics, network conditions, data volumes, etc. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..b01699b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,26 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feat]" +labels: '' +assignees: '' + +--- + +**Problem Description** +Describe the issue you're experiencing that has prompted this feature request. For example, "I find it difficult when..." + +**Proposed Feature** +Provide a clear and concise description of the feature you would like implemented. + +**Alternatives Considered** +Discuss any alternative solutions or features you've considered. Why were these alternatives not suitable? + +**Implementation Suggestions** +If you have ideas on how the feature could be implemented, share them here. This could include technical details, API changes, or interaction mechanisms. + +**Use Case** +Explain how this feature would be used and what benefits it would bring. Include specific examples to illustrate how this would improve functionality or user experience. + +**Additional Context** +Add any other context such as comparisons with similar features in other products, or links to prototypes or mockups. From b001aded46a620021f0265db683c17ab3fc46793 Mon Sep 17 00:00:00 2001 From: Jakob Stadlhuber Date: Fri, 24 May 2024 17:41:34 +0200 Subject: [PATCH 18/21] Add proxy and media blocking configurations Updated environment variables and application settings to include proxy configurations and media blocking option. The proxy settings allow users to use a proxy service, while the media blocking is an optional feature that can help save bandwidth. Changes have been made in the .env.example, docker-compose.yaml, and main.py files. --- apps/api/.env.example | 9 ++++++++- apps/playwright-service/main.py | 20 +++++++++++++++++++- docker-compose.yaml | 4 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/api/.env.example b/apps/api/.env.example index 659d68f..0ba20e8 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -35,4 +35,11 @@ STRIPE_PRICE_ID_SCALE= HYPERDX_API_KEY= HDX_NODE_BETA_MODE=1 -FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta \ No newline at end of file +FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta + +# Proxy Settings (Alternative you can can use a proxy service like oxylabs, which rotates IPs for you on every request) +PROXY_SERVER= +PROXY_USERNAME= +PROXY_PASSWORD= +# set if you'd like to block media requests to save proxy bandwidth +BLOCK_MEDIA= \ No newline at end of file diff --git a/apps/playwright-service/main.py b/apps/playwright-service/main.py index c28bc63..337d283 100644 --- a/apps/playwright-service/main.py +++ b/apps/playwright-service/main.py @@ -2,9 +2,16 @@ from fastapi import FastAPI from playwright.async_api import async_playwright, Browser from fastapi.responses import JSONResponse from pydantic import BaseModel +from os import environ + +PROXY_SERVER = environ.get('PROXY_SERVER', None) +PROXY_USERNAME = environ.get('PROXY_USERNAME', None) +PROXY_PASSWORD = environ.get('PROXY_PASSWORD', None) +BLOCK_MEDIA = environ.get('BLOCK_MEDIA', 'False').upper() == 'TRUE' app = FastAPI() + class UrlModel(BaseModel): url: str wait: int = None @@ -27,7 +34,18 @@ async def shutdown_event(): @app.post("/html") async def root(body: UrlModel): - context = await browser.new_context() + context = None + if PROXY_SERVER and PROXY_USERNAME and PROXY_PASSWORD: + context = await browser.new_context(proxy={"server": PROXY_SERVER, + "username": PROXY_USERNAME, + "password": PROXY_PASSWORD}) + else: + context = await browser.new_context() + + if BLOCK_MEDIA: + await context.route("**/*.{png,jpg,jpeg,gif,svg,mp3,mp4,avi,flac,ogg,wav,webm}", + handler=lambda route, request: route.abort()) + page = await context.new_page() await page.goto(body.url, timeout=15000) # Set max timeout to 15s if body.wait: # Check if wait parameter is provided in the request body diff --git a/docker-compose.yaml b/docker-compose.yaml index 049672d..c95ccc9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,10 @@ services: build: apps/playwright-service environment: - PORT=3000 + - PROXY_SERVER=${PROXY_SERVER} + - PROXY_USERNAME=${PROXY_USERNAME} + - PROXY_PASSWORD=${PROXY_PASSWORD} + - BLOCK_MEDIA=${BLOCK_MEDIA} networks: - backend From 9fc5a0ff98492c9c6bfba517f995e2c36cb2569a Mon Sep 17 00:00:00 2001 From: Jakob Stadlhuber Date: Fri, 24 May 2024 17:45:59 +0200 Subject: [PATCH 19/21] Update comment in .env.example for proxy settings This commit modifies the comment in .env.example to specify that proxy settings are for Playwright. This clarification aims to provide users a more clear context about when and why these proxy settings are used. --- apps/api/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/.env.example b/apps/api/.env.example index 0ba20e8..735444b 100644 --- a/apps/api/.env.example +++ b/apps/api/.env.example @@ -37,7 +37,7 @@ HDX_NODE_BETA_MODE=1 FIRE_ENGINE_BETA_URL= # set if you'd like to use the fire engine closed beta -# Proxy Settings (Alternative you can can use a proxy service like oxylabs, which rotates IPs for you on every request) +# Proxy Settings for Playwright (Alternative you can can use a proxy service like oxylabs, which rotates IPs for you on every request) PROXY_SERVER= PROXY_USERNAME= PROXY_PASSWORD= From 53a7ec0f6eee6a1cb22d7ea174b489e042e5bbb3 Mon Sep 17 00:00:00 2001 From: Rafael Miller <150964962+rafaelsideguide@users.noreply.github.com> Date: Fri, 24 May 2024 13:46:16 -0300 Subject: [PATCH 20/21] Removed hard coded timeout --- apps/python-sdk/firecrawl/firecrawl.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 9e3011c..1826605 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -74,7 +74,6 @@ class FirecrawlApp: f'{self.api_url}/v0/scrape', headers=headers, json=scrape_params, - timeout=(5,10) ) if response.status_code == 200: response = response.json() @@ -112,8 +111,7 @@ class FirecrawlApp: response = requests.post( f'{self.api_url}/v0/search', headers=headers, - json=json_data, - timeout=(5,10) + json=json_data ) if response.status_code == 200: response = response.json() From 8c380d70a5f37cf8ac54a090c1159909ba99fa97 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 24 May 2024 09:48:48 -0700 Subject: [PATCH 21/21] Update firecrawl.py --- apps/python-sdk/firecrawl/firecrawl.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/python-sdk/firecrawl/firecrawl.py b/apps/python-sdk/firecrawl/firecrawl.py index 0e4fc3e..f28a057 100644 --- a/apps/python-sdk/firecrawl/firecrawl.py +++ b/apps/python-sdk/firecrawl/firecrawl.py @@ -115,11 +115,8 @@ class FirecrawlApp: ) if response.status_code == 200: response = response.json() -<<<<<<< main - if response['success']: -======= + if response['success'] and 'data' in response: ->>>>>>> main return response['data'] else: raise Exception(f'Failed to search. Error: {response["error"]}')