{"id":15890348,"url":"https://github.com/kodaho/manen","last_synced_at":"2026-01-25T02:02:43.673Z","repository":{"id":43756120,"uuid":"254894120","full_name":"kodaho/manen","owner":"kodaho","description":"An implementation of the Page Object Model design pattern, and other utilities for web scraping and automation","archived":false,"fork":false,"pushed_at":"2024-10-12T14:22:05.000Z","size":11498,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-30T03:04:28.144Z","etag":null,"topics":["automation","page-object","page-object-model","page-object-model-design-pattern","python3","scraping","selenium-python"],"latest_commit_sha":null,"homepage":"https://kodaho.github.io/manen/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kodaho.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}},"created_at":"2020-04-11T15:12:11.000Z","updated_at":"2024-10-12T14:17:20.000Z","dependencies_parsed_at":"2024-10-28T10:20:24.799Z","dependency_job_id":null,"html_url":"https://github.com/kodaho/manen","commit_stats":{"total_commits":259,"total_committers":1,"mean_commits":259.0,"dds":0.0,"last_synced_commit":"daf7dd996b7e9524f1dfcf5279cc90cf1a3bdbbe"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/kodaho/manen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodaho%2Fmanen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodaho%2Fmanen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodaho%2Fmanen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodaho%2Fmanen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kodaho","download_url":"https://codeload.github.com/kodaho/manen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodaho%2Fmanen/sbom","scorecard":{"id":565503,"data":{"date":"2025-08-11","repo":{"name":"github.com/kodaho/manen","commit":"daf7dd996b7e9524f1dfcf5279cc90cf1a3bdbbe"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.8,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build-documentation.yml:1","Warn: no topLevel permission defined: .github/workflows/deploy-package.yml:1","Warn: no topLevel permission defined: .github/workflows/test-package.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build-documentation.yml:53: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/build-documentation.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy-package.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/deploy-package.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy-package.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/deploy-package.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy-package.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/deploy-package.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy-package.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/deploy-package.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test-package.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/test-package.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test-package.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/kodaho/manen/test-package.yml/main?enable=pin","Warn: downloadThenRun not pinned by hash: .github/workflows/build-documentation.yml:15","Warn: downloadThenRun not pinned by hash: .github/workflows/deploy-package.yml:15","Warn: downloadThenRun not pinned by hash: .github/workflows/deploy-package.yml:38","Warn: downloadThenRun not pinned by hash: .github/workflows/test-package.yml:22","Warn: downloadThenRun not pinned by hash: .github/workflows/test-package.yml:44","Info:   0 out of  12 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   5 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T14:52:39.177Z","repository_id":43756120,"created_at":"2025-08-20T14:52:39.178Z","updated_at":"2025-08-20T14:52:39.178Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28741634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T01:40:51.112Z","status":"online","status_checked_at":"2026-01-25T02:00:06.841Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["automation","page-object","page-object-model","page-object-model-design-pattern","python3","scraping","selenium-python"],"created_at":"2024-10-06T07:05:18.749Z","updated_at":"2026-01-25T02:02:43.657Z","avatar_url":"https://github.com/kodaho.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003e 🌔  Manen\u003c/h1\u003e\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/python-%3E=3.10-informational?style=for-the-badge\u0026logo=python\"\u003e\n  \u003cimg alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/manen?logo=pypi\u0026style=for-the-badge\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/status-beta-yellow?style=for-the-badge\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ci\u003e\n    An implementation of the Page Object Model design pattern, and other utilities for web\n    scraping and automation.\n  \u003c/i\u003e\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pypi.org/project/manen\"\u003ePyPI\u003c/a\u003e\n  ・\n  \u003ca href=\"https://kodaho.github.io/manen/\"\u003eDocumentation\u003c/a\u003e\n  ・\n  \u003ca href=\"https://kodaho.github.io/manen/changelog.html\"\u003eChangelog\u003c/a\u003e\n  ・\n  \u003ca href=\"https://github.com/kodaho/manen/issues\"\u003eIssue tracker\u003c/a\u003e\n\u003c/p\u003e\n\nManen is a package built to enhance developer experience when using Selenium. Among the core\nfeatures, you can find:\n\n- an implementation of the [Page Object Model](https://www.selenium.dev/documentation/en/guidelines_and_recommendations/page_object_models/)\n  design pattern\n- a class which improves the operability of a Selenium WebDriver\n- a function to easily find and isolate DOM elements inside a Selenium page\n\nThis package aims to provide you the tools to write more concise, flexible and powerful code\ncompared to what you would do by using only Selenium.\n\n\u003e [!NOTE]\n\u003e For now, only Selenium Chrome WebDriver is supported. Other browsers will be supported in the\n\u003e future, as well as other automation tools such as Playwright or Scrapy.\n\n## 📥 Installation\n\nThe package can be installed using the official Python package manager `pip`.\n\n```bash\npip install manen\n```\n\n## ✨ Features\n\n- `manen.finder.find` allows to easily get element(s) in a HTML page. This function support\n  several very different use cases, to help reduce your code complexity when fetching for\n  elements (example: using default values, trying different selectors, iterating over several\n  elements).\n- `manen.browser` defines an enhanced Selenium `WebDriver` called `Browser`\n- `manen.page_object_model` is an implementation of the Page Object Model design pattern. It will\n  wrap a HTML page, component and DOM values inside Python classes and objects, providing a better\n  way to interact with a web page.\n\n## 🚀 Getting started\n\nManen features will be explored by a simple example: going to the PyPI page, searching for a\nspecific package and extracting some information from the search results.\n\nFirst thing to do is to initialize a WebDriver instance. It can be done using the usual way\nprovided by Selenium, but an alternative is to use the `Browser` class provided by Manen. Note\nthat both ways are equivalent, but `Browser` provides some additional features, that won't be\nexplored here.\n\n```python\nfrom manen.browser import ChromeBrowser\n\nbrowser = ChromeBrowser.initialize()\nbrowser.get(\"https://pypi.org\")\n```\n\n![PyPI home page](https://raw.githubusercontent.com/kodaho/manen/main/docs/assets/screenshot_pypi_home.png)\n\nWe are now on the home page of PyPI. What we are going to do now is building a class that will\ninherit from `Page` from the `manen.page_object_model.component` module. This Python class will be\na reflect of the HTML page, allowing us to access DOM elements in the same way we access\nattributes. Note the whole page object model design pattern is implemented with type hints (a bit\nlike in `Pydantic` model).\n\n```python\nfrom manen.page_object_model.types import href, input_value\nfrom manen.page_object_model.config import CSS, Attribute, DatetimeFormat, XPath\nfrom manen.page_object_model.component import Page, Component\n\n\nclass HomePage(Page):\n    query: Annotated[input_value, CSS(\"input[name='q']\")]\n\n\nclass SearchResultPage(Page):\n    class Result(Component):\n        name: Annotated[str, CSS(\"h3 span.package-snippet__name\")]\n        version: Annotated[str, CSS(\"h3 span.package-snippet__version\")]\n        link: Annotated[href, CSS(\"a.package-snippet\")]\n        description: Annotated[str, CSS(\"p.package-snippet__description\")]\n        release_datetime: A[\n            datetime,\n            DatetimeFormat(\"%Y-%m-%dT%H:%M:%S%z\"),\n            Attribute(\"datetime\"),\n            CSS(\"span.package-snippet__created time\"),\n        ]\n\n    nb_results: Annotated[\n        int,\n        XPath(\"//*[@id='content']//form/div[1]/div[1]/p/strong\"),\n    ]\n    results: Annotated[\n        list[Result],\n        CSS(\"ul[aria-label='Search results'] li\"),\n    ]\n```\n\nThe `Page` class encapsulates the whole current HTML page available through the driver. Each DOM\nvalue we want to extract is then represented by a class attribute, with a type (what to extract)\nand a selector (where to extract it). Depending on the type of the value, Manen will automatically\nexecute the appropriate DOM content extraction on the HTML element (for example, it will extract\nthe inner text for a `str` type, the HTML attribute `href` for a `HRef` , or the property\n`innerHTML` for `InnerHTML`).\n\nA `Component` captures a sub-part of an HTML page. All the elements defined under this will be\nfetched inside the HTML element represented by the `Component` class.\n\nHere the class `HomePage` defines an `Input` element, that will be linked to the search bar.\nFilling the search bar is done by assigning a value to the attribute `query`.\n\n```python\nfrom selenium.webdriver.common.keys import Keys\n\npage = HomePage(browser) # A Page object is initialized only with a WebDriver instance\n\npage.query = \"manen\"\npage.query += Keys.ENTER\n```\n\nAfter submitting the form, we are redirected to the search results page.\n\n![PyPI home page](https://raw.githubusercontent.com/kodaho/manen/main/docs/assets/screenshot_pypi_search_results.png)\n\nThe `SearchResultPage` will then be used to extract the results.\n\n```python\npage = SearchResultPage(browser)\n\nprint(page.nb_results)\n# 3\n\nprint(page.results[0])\n# \u003c__main__.SearchResultPage.Result at 0x1058e97c0\u003e\n```\n\nManen provides a `model_dump` method, quite similar to the one in Pydantic to easily get all the\nattributes of a component or a page.\n\n```python\nprint(page.results[0].model_dump())\n# {'name': 'manen',\n#  'version': '0.2.0',\n#  'link': 'https://pypi.org/project/manen/',\n#  'description': 'A package around Selenium with an implementation of the page object model, an enhanced WebDriver and a CLI.',\n#  'release_datetime': datetime.datetime(2022, 2, 19, 12, 10, 31, tzinfo=datetime.timezone.utc)}\n```\n\n\u003e [!TIP]\n\u003e Other DOM elements are also implemented, such as `ImageSrc`, `Input`, `Checkbox`... Each one of\n\u003e them is used to target a specific attribute from a DOM value and can enable interaction with it,\n\u003e in a flawless Pythonic way. Check the [documentation](https://kodaho.github.io/manen/user_guide/page_object_model.html#List-of-available-elements)\n\u003e for the list of available elements.\n\nLet's finally close the Selenium WebDriver to avoid any remaining running applications once we\nexit the Python program.\n\n```python\nbrowser.quit()\n```\n\n## 🦾 Going further\n\n[The documentation](https://kodaho.github.io/manen/) provides an extensive overview of the\npossibilities offered by the package. It also contains several user guides to help you get\nstarted with the package.\n\nA [set of examples](https://github.com/kodaho/manen/tree/main/examples) is also available in the\n`examples/` directory of the source code repository. They are designed to show you how to use the\npackage in a real-world context.\n\nDon't hesitate to open an issue if you have any question or concern about this project!\n\nLooking to contribute to fix or add new features? Just read\n[this page](https://kodaho.github.io/manen/contributing.html),\nfork the repository and start doing the modifications you want.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodaho%2Fmanen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkodaho%2Fmanen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodaho%2Fmanen/lists"}