{"id":13413985,"url":"https://github.com/kkyr/go-recipe","last_synced_at":"2026-02-15T02:12:39.260Z","repository":{"id":57749046,"uuid":"524250036","full_name":"kkyr/go-recipe","owner":"kkyr","description":"Go package for scraping website recipes","archived":false,"fork":false,"pushed_at":"2023-03-16T12:21:12.000Z","size":576,"stargazers_count":25,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-07-31T20:53:13.222Z","etag":null,"topics":["go","golang","recipe","recipes","scraper"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kkyr.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":"2022-08-12T23:34:17.000Z","updated_at":"2024-05-26T11:20:32.000Z","dependencies_parsed_at":"2024-06-19T05:18:08.179Z","dependency_job_id":"405e9f34-84ff-48a4-b6ec-00d6048b5e09","html_url":"https://github.com/kkyr/go-recipe","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/kkyr/go-recipe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkyr%2Fgo-recipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkyr%2Fgo-recipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkyr%2Fgo-recipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkyr%2Fgo-recipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kkyr","download_url":"https://codeload.github.com/kkyr/go-recipe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kkyr%2Fgo-recipe/sbom","scorecard":{"id":562955,"data":{"date":"2025-08-11","repo":{"name":"github.com/kkyr/go-recipe","commit":"f89e0c28b8eb15eac5218c59037da71aa02efbe3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"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":"Code-Review","score":1,"reason":"Found 2/14 approved changesets -- score normalized to 1","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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/golangci-lint.yml:10","Info: topLevel 'pull-requests' permission set to 'read': .github/workflows/golangci-lint.yml:11","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/go.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/go.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/golangci-lint.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/golangci-lint.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/kkyr/go-recipe/golangci-lint.yml/main?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction 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":"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.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":"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":"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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"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 21 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"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2023-1988 / GHSA-2wrh-6pvc-2jm9","Warn: Project is vulnerable to: GO-2023-2102 / GHSA-4374-p667-p6c8","Warn: Project is vulnerable to: GHSA-qppj-fm5r-hxr3","Warn: Project is vulnerable to: GO-2024-2687 / GHSA-4v7x-pqxf-cx7m","Warn: Project is vulnerable to: GO-2024-3333","Warn: Project is vulnerable to: GO-2025-3503 / GHSA-qxp5-gwg8-xv66","Warn: Project is vulnerable to: GO-2025-3595 / GHSA-vvgc-356p-c3xw"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T14:11:37.307Z","repository_id":57749046,"created_at":"2025-08-20T14:11:37.307Z","updated_at":"2025-08-20T14:11:37.307Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29465432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T01:01:38.065Z","status":"online","status_checked_at":"2026-02-15T02:00:07.449Z","response_time":118,"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":["go","golang","recipe","recipes","scraper"],"created_at":"2024-07-30T20:01:54.452Z","updated_at":"2026-02-15T02:12:39.239Z","avatar_url":"https://github.com/kkyr.png","language":"Go","funding_links":[],"categories":["Text Processing","文本处理","Template Engines"],"sub_categories":["Scrapers","刮刀"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"logo.png\" alt=\"go-recipe-logo\" title=\"go-recipe\" class=\"img-responsive\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/kkyr/go-recipe?tab=doc\"\u003e\u003cimg src=\"https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white\" alt=\"godoc\" title=\"godoc\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/kkyr/go-recipe/tags\"\u003e\u003cimg src=\"https://img.shields.io/github/v/tag/kkyr/go-recipe\" alt=\"semver tag\" title=\"semver tag\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/kkyr/go-recipe\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/kkyr/go-recipe\" alt=\"go report card\" title=\"go report card\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://coveralls.io/github/kkyr/go-recipe?branch=main\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/kkyr/go-recipe/badge.svg?branch=main\" alt=\"coverage status\" title=\"coverage status\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/kkyr/go-recipe/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/kkyr/go-recipe\" alt=\"license\" title=\"license\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# go-recipe\n\ngo-recipe is a Go library that scrapes recipes from websites.\n\n## Installation\n\n`$ go get github.com/kkyr/go-recipe@latest`\n\n## Usage\n\n```go\npackage main\n\nimport \"github.com/kkyr/go-recipe/pkg/recipe\"\n\nfunc main() {\n  url := \"https://minimalistbaker.com/quick-pickled-jalapenos/\"\n\n  recipe, err := recipe.ScrapeURL(url)\n  if err != nil {\n      // handle err\n  }\n\n  ingredients, ok := recipe.Ingredients()\n  instructions, ok := recipe.Instructions()\n  // ... \u0026 more fields available\n}\n```\n\n## Scraping\n\nThe go-recipe default scraper looks for a `ld+json` encoded [Schema Recipe](https://schema.org/Recipe) on the target website and is able to retrieve most fields defined in the schema. However, some websites have incomplete Schema data or simply do not encode their recipe in such a format.\n\nTherefore, custom scrapers exist that are used to scrape specific websites. These scrapers can make use of the default scraper so that custom scraping logic is only defined for fields that the default scraper could not find any data for.\n\nThe custom scrapers are registered in [pkg/recipe/scrapers.go](/pkg/recipe/scrapers.go) and are identified by host name, which represents the website that they are used for. When a client provides go-recipe with a link to scrape, the host name is extracted from the link and is used to find the corresponding custom scraper. The default scraper is used if no custom scraper is defined.\n\n## Contributing\n\nContributions are welcome! You can contribute in a few ways: by adding a custom scraper, patching a bug, implementing a feature based on the roadmap (see further below), or by incorporating any other feature that you'd like to see. For the latter case, please first open a discussion so that we can align on the change before you start coding.\n\n### Custom scrapers\n\nCreating a custom scraper is easy as pie thanks to the code generator that's included in this package.\n\nThe generator requires two arguments: a link to a recipe on a website and the domain of the website that the recipe is hosted on. The domain is used to generate the source code (particularly the file and struct names), and the link is used to scrape recipe data, which is then used to generate a fully functioning unit test. If the generator is unable to scrape recipe data (which can happen if the website does not contain a Schema Recipe), a test will still be generated but test assertions will be made against empty fields.\n\nTo use the code generator, run the following command while inside the go-recipe package:\n\n```shell\n$ go run cmd/scrapergen/*.go \\\n  -d CopyKat \\\n  -u https://copykat.com/dunkin-donuts-caramel-iced-coffee\n```\n\n_(replace the domain and link with your own)_\n\n**Important:** the domain should be provided in **PascalCase** so that the generated structs are correctly cased. Otherwise, your PR will not get approved.\n\nThe sample command above would generate the following files:\n\n```shell\ngo-recipe/internal/html/scrape/custom/copykat.go\ngo-recipe/internal/html/scrape/custom/copykat_test.go\ngo-recipe/internal/html/scrape/custom/testdata/copykat.com.html\n```\n\nThe generator can't do everything (at least not yet), so there's some final touches that you must put in:\n1. Register your custom scraper in the `hostToScraper` map located in [pkg/recipe/scrapers.go](/pkg/recipe/scrapers.go). Please maintain alphabetical ordering.\n2. (Optional) Modify the custom scraper to add your own scraping logic.\n3. Verify that the generated test is correct.\n4. Verify that `make lint` and `make test` are passing.\n\nYou should now be ready to send a PR!\n\n### Roadmap\n\n- [ ] Refactor `scrapergen` and add unit tests.\n- [ ] Modify `scrapergen` so that it also adds the new scraper to the `hostToScraper` map.\n- [ ] Add option for user to specify http client timeout.\n- [ ] Add option for user to specify \"strict\" mode: in this mode only custom scrapers will be used if defined, otherwise fail.\n- [ ] Add more Schema Recipe fields.\n- [ ] Add CLI wrapper over the scraper, providing output in JSON.\n\n## Acknowledgements\n\ngo-recipe is heavily inspired by [recipe-scrapers](https://github.com/hhursev/recipe-scrapers).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkyr%2Fgo-recipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkkyr%2Fgo-recipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkkyr%2Fgo-recipe/lists"}