{"id":28718740,"url":"https://github.com/open-inflation/playwright_interceptor","last_synced_at":"2025-06-15T05:03:35.487Z","repository":{"id":295429452,"uuid":"982457154","full_name":"Open-Inflation/playwright_interceptor","owner":"Open-Inflation","description":"Addon for Playwright for intercepting and modifying HTTP requests and responses.","archived":false,"fork":false,"pushed_at":"2025-06-15T00:48:40.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-15T01:31:28.906Z","etag":null,"topics":["interceptor","playwright","playwright-python"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/playwright-interceptor/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Open-Inflation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-12T23:13:12.000Z","updated_at":"2025-06-15T00:48:43.000Z","dependencies_parsed_at":"2025-06-15T01:31:31.040Z","dependency_job_id":null,"html_url":"https://github.com/Open-Inflation/playwright_interceptor","commit_stats":null,"previous_names":["open-inflation/standard_open_inflation_package","open-inflation/playwright_interceptor"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Open-Inflation/playwright_interceptor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-Inflation%2Fplaywright_interceptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-Inflation%2Fplaywright_interceptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-Inflation%2Fplaywright_interceptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-Inflation%2Fplaywright_interceptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Open-Inflation","download_url":"https://codeload.github.com/Open-Inflation/playwright_interceptor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Open-Inflation%2Fplaywright_interceptor/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259924678,"owners_count":22932782,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["interceptor","playwright","playwright-python"],"created_at":"2025-06-15T05:03:34.775Z","updated_at":"2025-06-15T05:03:35.469Z","avatar_url":"https://github.com/Open-Inflation.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Playwright Interceptor\n\n[![GitHub Actions](https://github.com/Open-Inflation/playwright_interceptor/workflows/Tests/badge.svg)](https://github.com/Open-Inflation/playwright_interceptor/actions/workflows/check_tests.yml?query=branch%3Amain)\n![Python Version](https://img.shields.io/badge/python-3.10%2B-blue)\n![PyPI - Package Version](https://img.shields.io/pypi/v/playwright_interceptor?color=blue)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/playwright_interceptor?label=PyPi%20downloads)](https://pypi.org/project/playwright_interceptor/)\n![License](https://img.shields.io/badge/license-MIT-green)\n[![Discord](https://img.shields.io/discord/792572437292253224?label=Discord\u0026labelColor=%232c2f33\u0026color=%237289da)](https://discord.gg/UnJnGHNbBp)\n[![Telegram](https://img.shields.io/badge/Telegram-24A1DE)](https://t.me/miskler_dev)\n\n**Addon for Playwright for intercepting and modifying HTTP requests and responses.**\n\n## Features\n\n- Request modification before sending to the server\n- Server response modification before passing to the browser\n- Request filtering by URL, method, and content type\n- Support for synchronous and asynchronous modification functions\n- Processing requests with multiple handlers\n- Obtaining information about intercepted requests\n- Type safety with beartype\n- Direct access to request and response properties\n\n## Installation\n\n```bash\npip install playwright_interceptor\n```\n\n## Usage\n\n```python\nfrom playwright.async_api import async_playwright\nfrom playwright_interceptor import NetworkInterceptor, Handler, Execute, Request, Response\nimport asyncio\n\nasync def main():\n    async with async_playwright() as pw:\n        browser = await pw.firefox.launch()\n        page = await browser.new_page()\n\n        interceptor = NetworkInterceptor(page)\n        \n        # Intercepting and modifying requests and responses\n        handler = Handler.ALL(execute=Execute.ALL(\n            request_modify=modify_request,\n            response_modify=modify_response,\n            max_modifications=5,\n            max_responses=2\n        ))\n        \n        # Starting interception\n        results, _ = await asyncio.gather(\n            interceptor.execute([handler]),\n            page.goto(\"https://httpbin.org/get\")\n        )\n        \n        print(f\"Results: {results}\")\n        await browser.close()\n\ndef modify_request(request: Request) -\u003e Request:\n    \"\"\"Modifies request before sending\"\"\"\n    request.headers[\"X-Custom-Header\"] = \"ModifiedByInterceptor\"\n    request.params[\"intercepted\"] = \"true\"\n    return request\n\ndef modify_response(response: Response) -\u003e Response:\n    \"\"\"Modifies response after receiving\"\"\"\n    response.response_headers[\"X-Response-Modified\"] = \"true\"\n    \n    # Parsing and modifying JSON\n    parsed_content = response.content_parse()\n    if isinstance(parsed_content, dict):\n        parsed_content[\"_intercepted\"] = True\n        # Updating content\n        import json\n        response.content = json.dumps(parsed_content).encode('utf-8')\n    \n    return response\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## Components\n\n### NetworkInterceptor\n\nClass for intercepting HTTP traffic:\n\n```python\nfrom playwright_interceptor import NetworkInterceptor\n\ninterceptor = NetworkInterceptor(page, logger=custom_logger)\nresults = await interceptor.execute(handlers, timeout=10.0)\n```\n\n**Parameters:**\n- `page` - Playwright page\n- `logger` - Optional logger\n\n**Methods:**\n- `execute(handlers, timeout=10.0)` - Start interception with specified handlers\n\n### Handler\n\nRules for capturing and processing requests:\n\n```python\nfrom playwright_interceptor import Handler, Execute, ExpectedContentType, HttpMethod\n\nhandler_all = Handler.ALL(\n    expected_content=ExpectedContentType.JSON,\n    startswith_url=\"https://api.example.com\",\n    method=HttpMethod.GET,\n    execute=Execute.RETURN(max_responses=3)\n)\n\nhandler_modify = Handler.ALL(\n    expected_content=ExpectedContentType.ANY,\n    execute=Execute.MODIFY(\n        request_modify=my_request_modifier,\n        response_modify=my_response_modifier,\n        max_modifications=5\n    )\n)\n\nhandler_combined = Handler.ALL(\n    slug=\"my_handler\",\n    expected_content=ExpectedContentType.JSON,\n    startswith_url=\"https://api.example.com\",\n    method=HttpMethod.POST,\n    execute=Execute.ALL(\n        request_modify=my_request_modifier,\n        response_modify=my_response_modifier,\n        max_modifications=3,\n        max_responses=2\n    )\n)\n```\n\n**Parameters:**\n- `expected_content` - Expected content type\n- `startswith_url` - Filter by URL beginning\n- `method` - HTTP method for filtering\n- `execute` - Execution configuration\n- `slug` - Handler identifier\n\n**Factory methods:**\n- `Handler.ALL()` - Universal handler for all types of requests\n- `Handler.MAIN()` - Handler for main page requests\n- `Handler.SIDE()` - Handler for side resource requests  \n- `Handler.NONE()` - Empty handler\n\n### Execute\n\nHandler behavior configuration:\n\n```python\nfrom playwright_interceptor import Execute\n\nexecute_return = Execute.RETURN(max_responses=5)\n\nexecute_modify = Execute.MODIFY(\n    request_modify=modify_request,\n    max_modifications=3\n)\n\nexecute_all = Execute.ALL(\n    request_modify=modify_request,\n    response_modify=modify_response,\n    max_modifications=5,\n    max_responses=3\n)\n```\n\n**Modes:**\n- `RETURN` - Request interception\n- `MODIFY` - Request/response modification\n- `ALL` - Combination of interception and modification\n\n**Parameters:**\n- `request_modify` - Request modification function\n- `response_modify` - Response modification function\n- `max_modifications` - Maximum number of modifications\n- `max_responses` - Maximum number of intercepted responses\n\n### Request\n\nHTTP request representation:\n\n```python\nfrom playwright_interceptor import Request, HttpMethod\n\nrequest = Request(\n    url=\"https://api.example.com/users\",\n    headers={\"Authorization\": \"Bearer token\"},\n    params={\"page\": \"1\", \"limit\": \"10\"},\n    body={\"name\": \"John\"},\n    method=HttpMethod.POST\n)\n\n# Modification (direct field access)\nrequest.headers[\"X-Custom\"] = \"value\"\nrequest.params[\"filter\"] = \"active\"\nrequest.method = HttpMethod.PUT\nrequest.body = {\"updated\": \"data\"}\n\n# URL with parameters\nfinal_url = request.real_url\n```\n\n**Properties:**\n- `url` - Base URL\n- `real_url` - URL with parameters (read-only property)\n- `base_url` - URL without parameters (read-only property)\n- `headers` - Headers dictionary\n- `params` - Request parameters dictionary\n- `body` - Request body\n- `method` - HTTP method\n\n### Response\n\nHTTP response representation:\n\n```python\nfrom playwright_interceptor import Response\n\ndef modify_response(response: Response) -\u003e Response:\n    response.response_headers[\"X-Modified\"] = \"true\"\n    \n    parsed_content = response.content_parse()\n    if isinstance(parsed_content, dict):\n        parsed_content[\"_intercepted\"] = True\n        import json\n        response.content = json.dumps(parsed_content).encode('utf-8')\n    \n    return response\n```\n\n**Properties:**\n- `status` - HTTP status code\n- `url` - Request URL\n- `request_headers` - Request headers\n- `response_headers` - Response headers\n- `content` - Response content (bytes)\n- `duration` - Request execution time\n\n**Methods:**\n- `content_parse()` - Parse content into objects\n\n### Enum Classes\n\n```python\nfrom playwright_interceptor import ExpectedContentType, HttpMethod\n\n# Content types\nExpectedContentType.JSON        # application/json\nExpectedContentType.JS          # application/javascript\nExpectedContentType.CSS         # text/css\nExpectedContentType.IMAGE       # image/*\nExpectedContentType.VIDEO       # video/*\nExpectedContentType.AUDIO       # audio/*\nExpectedContentType.FONT        # font/*\nExpectedContentType.APPLICATION # application/*\nExpectedContentType.ARCHIVE     # archive formats\nExpectedContentType.TEXT        # text/*\nExpectedContentType.ANY         # any type\n\n# HTTP methods\nHttpMethod.GET\nHttpMethod.POST  \nHttpMethod.PUT\nHttpMethod.DELETE\nHttpMethod.PATCH\nHttpMethod.HEAD\nHttpMethod.OPTIONS\nHttpMethod.ANY\n```\n\n## Examples\n\n### Adding Authentication\n\n```python\ndef add_auth(request: Request) -\u003e Request:\n    if \"/api/\" in request.url:\n        request.headers[\"Authorization\"] = \"Bearer your-token\"\n    return request\n\nhandler = Handler.ALL(\n    startswith_url=\"https://api.example.com\",\n    execute=Execute.MODIFY(request_modify=add_auth, max_modifications=10)\n)\n```\n\n### Adding Analytics\n\n```python\nfrom datetime import datetime\n\nasync def add_analytics(response: Response) -\u003e Response:\n    parsed_content = response.content_parse()\n    if isinstance(parsed_content, dict):\n        parsed_content[\"_analytics\"] = {\n            \"intercepted_at\": datetime.now().isoformat(),\n            \"response_time_ms\": response.duration * 1000,\n            \"status_code\": response.status\n        }\n        import json\n        response.content = json.dumps(parsed_content).encode('utf-8')\n    return response\n\nhandler = Handler.ALL(\n    expected_content=ExpectedContentType.JSON,\n    execute=Execute.ALL(\n        response_modify=add_analytics,\n        max_modifications=5,\n        max_responses=3\n    )\n)\n```\n\n### Multiple Handlers\n\n```python\nasync def run_multiple_handlers():\n    request_handler = Handler.ALL(\n        slug=\"request_modifier\",\n        execute=Execute.MODIFY(\n            request_modify=add_tracking,\n            max_modifications=10\n        )\n    )\n    \n    response_handler = Handler.ALL(\n        slug=\"response_modifier\", \n        expected_content=ExpectedContentType.JSON,\n        execute=Execute.MODIFY(\n            response_modify=add_metadata,\n            max_modifications=10\n        )\n    )\n    \n    collector_handler = Handler.ALL(\n        slug=\"data_collector\",\n        startswith_url=\"https://api.example.com\",\n        execute=Execute.ALL(\n            response_modify=collect_data,\n            max_modifications=5,\n            max_responses=5\n        )\n    )\n    \n    results = await interceptor.execute([\n        request_handler,\n        response_handler, \n        collector_handler\n    ])\n```\n\n### Asynchronous Modifiers\n\n```python\nasync def async_request_modifier(request: Request) -\u003e Request:\n    await asyncio.sleep(0.01)\n    request.headers[\"X-Async-Modified\"] = \"true\"\n    return request\n\nasync def async_response_modifier(response: Response) -\u003e Response:\n    parsed_content = response.content_parse()\n    if isinstance(parsed_content, dict):\n        parsed_content[\"_processed_async\"] = True\n        import json\n        response.content = json.dumps(parsed_content).encode('utf-8')\n    return response\n```\n\n### Error Handling\n\n```python\ndef safe_modifier(response: Response) -\u003e Response:\n    try:\n        parsed_content = response.content_parse()\n        if isinstance(parsed_content, dict):\n            parsed_content[\"_modified\"] = True\n            import json\n            response.content = json.dumps(parsed_content).encode('utf-8')\n        return response\n    except Exception as e:\n        print(f\"Modification error: {e}\")\n        return response\n```\n\n## Important Notes\n\n1. When using multiple handlers, modifications are applied sequentially\n2. For `MODIFY` and `ALL` modes, at least one of the modifiers is required\n3. With multiple handlers, unique `slug` values are required\n4. Avoid heavy operations in modifiers\n\n## License\n\nMIT License\n\n## Support\n\n- [Discord](https://discord.gg/UnJnGHNbBp)\n- [Telegram](https://t.me/miskler_dev)\n- [GitHub Issues](https://github.com/Open-Inflation/playwright_interceptor/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-inflation%2Fplaywright_interceptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopen-inflation%2Fplaywright_interceptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopen-inflation%2Fplaywright_interceptor/lists"}