Merge branch 'main' into docs
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
include LICENSE
|
include LICENSE
|
||||||
include *.db
|
include *.db
|
||||||
include *.js
|
include *.js
|
||||||
|
include scrapling/engines/toolbelt/bypasses/*.js
|
||||||
include scrapling/*.db
|
include scrapling/*.db
|
||||||
include scrapling/*.db*
|
include scrapling/*.db*
|
||||||
include scrapling/py.typed
|
include scrapling/py.typed
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from scrapling.parser import Adaptor, Adaptors
|
|||||||
from scrapling.core.custom_types import TextHandler, AttributesHandler
|
from scrapling.core.custom_types import TextHandler, AttributesHandler
|
||||||
|
|
||||||
__author__ = "Karim Shoair (karim.shoair@pm.me)"
|
__author__ = "Karim Shoair (karim.shoair@pm.me)"
|
||||||
__version__ = "0.2.2"
|
__version__ = "0.2.4"
|
||||||
__copyright__ = "Copyright (c) 2024 Karim Shoair"
|
__copyright__ = "Copyright (c) 2024 Karim Shoair"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from scrapling.core._types import Union, Callable, Optional, Dict, List, Literal
|
|||||||
from scrapling.engines.toolbelt import (
|
from scrapling.engines.toolbelt import (
|
||||||
Response,
|
Response,
|
||||||
do_nothing,
|
do_nothing,
|
||||||
|
StatusText,
|
||||||
get_os_name,
|
get_os_name,
|
||||||
intercept_route,
|
intercept_route,
|
||||||
check_type_validity,
|
check_type_validity,
|
||||||
@@ -111,12 +112,17 @@ class CamoufoxEngine:
|
|||||||
if 'charset=' in content_type.lower():
|
if 'charset=' in content_type.lower():
|
||||||
encoding = content_type.lower().split('charset=')[-1].split(';')[0].strip()
|
encoding = content_type.lower().split('charset=')[-1].split(';')[0].strip()
|
||||||
|
|
||||||
|
status_text = res.status_text
|
||||||
|
# PlayWright API sometimes give empty status text for some reason!
|
||||||
|
if not status_text:
|
||||||
|
status_text = StatusText.get(res.status)
|
||||||
|
|
||||||
response = Response(
|
response = Response(
|
||||||
url=res.url,
|
url=res.url,
|
||||||
text=page.content(),
|
text=page.content(),
|
||||||
body=res.body(),
|
body=page.content().encode('utf-8'),
|
||||||
status=res.status,
|
status=res.status,
|
||||||
reason=res.status_text,
|
reason=status_text,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
cookies={cookie['name']: cookie['value'] for cookie in page.context.cookies()},
|
cookies={cookie['name']: cookie['value'] for cookie in page.context.cookies()},
|
||||||
headers=res.all_headers(),
|
headers=res.all_headers(),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from scrapling.engines.constants import DEFAULT_STEALTH_FLAGS, NSTBROWSER_DEFAUL
|
|||||||
from scrapling.engines.toolbelt import (
|
from scrapling.engines.toolbelt import (
|
||||||
Response,
|
Response,
|
||||||
do_nothing,
|
do_nothing,
|
||||||
|
StatusText,
|
||||||
js_bypass_path,
|
js_bypass_path,
|
||||||
intercept_route,
|
intercept_route,
|
||||||
generate_headers,
|
generate_headers,
|
||||||
@@ -221,12 +222,17 @@ class PlaywrightEngine:
|
|||||||
if 'charset=' in content_type.lower():
|
if 'charset=' in content_type.lower():
|
||||||
encoding = content_type.lower().split('charset=')[-1].split(';')[0].strip()
|
encoding = content_type.lower().split('charset=')[-1].split(';')[0].strip()
|
||||||
|
|
||||||
|
status_text = res.status_text
|
||||||
|
# PlayWright API sometimes give empty status text for some reason!
|
||||||
|
if not status_text:
|
||||||
|
status_text = StatusText.get(res.status)
|
||||||
|
|
||||||
response = Response(
|
response = Response(
|
||||||
url=res.url,
|
url=res.url,
|
||||||
text=page.content(),
|
text=page.content(),
|
||||||
body=res.body(),
|
body=page.content().encode('utf-8'),
|
||||||
status=res.status,
|
status=res.status,
|
||||||
reason=res.status_text,
|
reason=status_text,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
cookies={cookie['name']: cookie['value'] for cookie in page.context.cookies()},
|
cookies={cookie['name']: cookie['value'] for cookie in page.context.cookies()},
|
||||||
headers=res.all_headers(),
|
headers=res.all_headers(),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from .fingerprints import (
|
|||||||
from .custom import (
|
from .custom import (
|
||||||
Response,
|
Response,
|
||||||
do_nothing,
|
do_nothing,
|
||||||
|
StatusText,
|
||||||
BaseFetcher,
|
BaseFetcher,
|
||||||
get_variable_name,
|
get_variable_name,
|
||||||
check_type_validity,
|
check_type_validity,
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ Functions related to custom types or type checking
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from scrapling.core.utils import setup_basic_logging
|
from scrapling.core.custom_types import MappingProxyType
|
||||||
from scrapling.parser import Adaptor, SQLiteStorageSystem
|
from scrapling.parser import Adaptor, SQLiteStorageSystem
|
||||||
|
from scrapling.core.utils import setup_basic_logging, cache
|
||||||
from scrapling.core._types import Any, List, Type, Union, Optional, Dict, Callable
|
from scrapling.core._types import Any, List, Type, Union, Optional, Dict, Callable
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +68,83 @@ class BaseFetcher:
|
|||||||
self.adaptor_arguments.update({'automatch_domain': automatch_domain})
|
self.adaptor_arguments.update({'automatch_domain': automatch_domain})
|
||||||
|
|
||||||
|
|
||||||
|
class StatusText:
|
||||||
|
"""A class that gets the status text of response status code.
|
||||||
|
|
||||||
|
Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
||||||
|
"""
|
||||||
|
_phrases = MappingProxyType({
|
||||||
|
100: "Continue",
|
||||||
|
101: "Switching Protocols",
|
||||||
|
102: "Processing",
|
||||||
|
103: "Early Hints",
|
||||||
|
200: "OK",
|
||||||
|
201: "Created",
|
||||||
|
202: "Accepted",
|
||||||
|
203: "Non-Authoritative Information",
|
||||||
|
204: "No Content",
|
||||||
|
205: "Reset Content",
|
||||||
|
206: "Partial Content",
|
||||||
|
207: "Multi-Status",
|
||||||
|
208: "Already Reported",
|
||||||
|
226: "IM Used",
|
||||||
|
300: "Multiple Choices",
|
||||||
|
301: "Moved Permanently",
|
||||||
|
302: "Found",
|
||||||
|
303: "See Other",
|
||||||
|
304: "Not Modified",
|
||||||
|
305: "Use Proxy",
|
||||||
|
307: "Temporary Redirect",
|
||||||
|
308: "Permanent Redirect",
|
||||||
|
400: "Bad Request",
|
||||||
|
401: "Unauthorized",
|
||||||
|
402: "Payment Required",
|
||||||
|
403: "Forbidden",
|
||||||
|
404: "Not Found",
|
||||||
|
405: "Method Not Allowed",
|
||||||
|
406: "Not Acceptable",
|
||||||
|
407: "Proxy Authentication Required",
|
||||||
|
408: "Request Timeout",
|
||||||
|
409: "Conflict",
|
||||||
|
410: "Gone",
|
||||||
|
411: "Length Required",
|
||||||
|
412: "Precondition Failed",
|
||||||
|
413: "Payload Too Large",
|
||||||
|
414: "URI Too Long",
|
||||||
|
415: "Unsupported Media Type",
|
||||||
|
416: "Range Not Satisfiable",
|
||||||
|
417: "Expectation Failed",
|
||||||
|
418: "I'm a teapot",
|
||||||
|
421: "Misdirected Request",
|
||||||
|
422: "Unprocessable Entity",
|
||||||
|
423: "Locked",
|
||||||
|
424: "Failed Dependency",
|
||||||
|
425: "Too Early",
|
||||||
|
426: "Upgrade Required",
|
||||||
|
428: "Precondition Required",
|
||||||
|
429: "Too Many Requests",
|
||||||
|
431: "Request Header Fields Too Large",
|
||||||
|
451: "Unavailable For Legal Reasons",
|
||||||
|
500: "Internal Server Error",
|
||||||
|
501: "Not Implemented",
|
||||||
|
502: "Bad Gateway",
|
||||||
|
503: "Service Unavailable",
|
||||||
|
504: "Gateway Timeout",
|
||||||
|
505: "HTTP Version Not Supported",
|
||||||
|
506: "Variant Also Negotiates",
|
||||||
|
507: "Insufficient Storage",
|
||||||
|
508: "Loop Detected",
|
||||||
|
510: "Not Extended",
|
||||||
|
511: "Network Authentication Required"
|
||||||
|
})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cache(maxsize=128)
|
||||||
|
def get(cls, status_code: int) -> str:
|
||||||
|
"""Get the phrase for a given HTTP status code."""
|
||||||
|
return cls._phrases.get(status_code, "Unknown Status Code")
|
||||||
|
|
||||||
|
|
||||||
def check_if_engine_usable(engine: Callable) -> Union[Callable, None]:
|
def check_if_engine_usable(engine: Callable) -> Union[Callable, None]:
|
||||||
"""This function check if the passed engine can be used by a Fetcher-type class or not.
|
"""This function check if the passed engine can be used by a Fetcher-type class or not.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = scrapling
|
name = scrapling
|
||||||
version = 0.2.2
|
version = 0.2.4
|
||||||
author = Karim Shoair
|
author = Karim Shoair
|
||||||
author_email = karim.shoair@pm.me
|
author_email = karim.shoair@pm.me
|
||||||
description = Scrapling is an undetectable, powerful, flexible, adaptive, and high-performance web scraping library for Python.
|
description = Scrapling is an undetectable, powerful, flexible, adaptive, and high-performance web scraping library for Python.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="scrapling",
|
name="scrapling",
|
||||||
version="0.2.2",
|
version="0.2.4",
|
||||||
description="""Scrapling is a powerful, flexible, and high-performance web scraping library for Python. It
|
description="""Scrapling is a powerful, flexible, and high-performance web scraping library for Python. It
|
||||||
simplifies the process of extracting data from websites, even when they undergo structural changes, and offers
|
simplifies the process of extracting data from websites, even when they undergo structural changes, and offers
|
||||||
impressive speed improvements over many popular scraping tools.""",
|
impressive speed improvements over many popular scraping tools.""",
|
||||||
@@ -57,7 +57,7 @@ setup(
|
|||||||
'httpx[brotli,zstd]',
|
'httpx[brotli,zstd]',
|
||||||
'playwright',
|
'playwright',
|
||||||
'rebrowser-playwright',
|
'rebrowser-playwright',
|
||||||
'camoufox>=0.3.9',
|
'camoufox>=0.3.10',
|
||||||
'browserforge',
|
'browserforge',
|
||||||
],
|
],
|
||||||
python_requires=">=3.8",
|
python_requires=">=3.8",
|
||||||
|
|||||||
Reference in New Issue
Block a user