{"id":18402195,"url":"https://github.com/gerapy/gerapypyppeteer","last_synced_at":"2025-04-09T07:08:20.601Z","repository":{"id":40770561,"uuid":"278991355","full_name":"Gerapy/GerapyPyppeteer","owner":"Gerapy","description":"Downloader Middleware to support Pyppeteer in Scrapy \u0026 Gerapy","archived":false,"fork":false,"pushed_at":"2021-12-27T08:41:19.000Z","size":181,"stargazers_count":136,"open_issues_count":7,"forks_count":37,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T04:59:13.768Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gerapy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-12T04:32:42.000Z","updated_at":"2025-03-31T13:53:56.000Z","dependencies_parsed_at":"2022-08-27T22:31:00.719Z","dependency_job_id":null,"html_url":"https://github.com/Gerapy/GerapyPyppeteer","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gerapy%2FGerapyPyppeteer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gerapy%2FGerapyPyppeteer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gerapy%2FGerapyPyppeteer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gerapy%2FGerapyPyppeteer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gerapy","download_url":"https://codeload.github.com/Gerapy/GerapyPyppeteer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247994121,"owners_count":21030050,"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":[],"created_at":"2024-11-06T02:41:34.259Z","updated_at":"2025-04-09T07:08:20.579Z","avatar_url":"https://github.com/Gerapy.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gerapy Pyppeteer\n\nThis is a package for supporting pyppeteer in Scrapy, also this\npackage is a module in [Gerapy](https://github.com/Gerapy/Gerapy).\n\n## Installation\n\n```shell script\npip3 install gerapy-pyppeteer\n```\n\n## Usage\n\nYou can use `PyppeteerRequest` to specify a request which uses pyppeteer to render.\n\nFor example:\n\n```python\nyield PyppeteerRequest(detail_url, callback=self.parse_detail)\n```\n\nAnd you also need to enable `PyppeteerMiddleware` in `DOWNLOADER_MIDDLEWARES`:\n\n```python\nDOWNLOADER_MIDDLEWARES = {\n    'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware': 543,\n}\n```\n\nCongratulate, you've finished the all of the required configuration.\n\nIf you run the Spider again, Pyppeteer will be started to render every\nweb page which you configured the request as PyppeteerRequest.\n\n## Settings\n\nGerapyPyppeteer provides some optional settings.\n\n### Concurrency\n\nYou can directly use Scrapy's setting to set Concurrency of Pyppeteer,\nfor example:\n\n```python\nCONCURRENT_REQUESTS = 3\n```\n\n### Pretend as Real Browser\n\nSome website will detect WebDriver or Headless, GerapyPyppeteer can\npretend Chromium by inject scripts. This is enabled by default.\n\nYou can close it if website does not detect WebDriver to speed up:\n\n```python\nGERAPY_PYPPETEER_PRETEND = False\n```\n\nAlso you can use `pretend` attribute in `PyppeteerRequest` to overwrite this\nconfiguration.\n\n### Logging Level\n\nBy default, Pyppeteer will log all the debug messages, so GerapyPyppeteer\nconfigured the logging level of Pyppeteer to WARNING.\n\nIf you want to see more logs from Pyppeteer, you can change the this setting:\n\n```python\nimport logging\nGERAPY_PYPPETEER_LOGGING_LEVEL = logging.DEBUG\n```\n\n### Download Timeout\n\nPyppeteer may take some time to render the required web page, you can also change this setting, default is `30s`:\n\n```python\n# pyppeteer timeout\nGERAPY_PYPPETEER_DOWNLOAD_TIMEOUT = 30\n```\n\n### Headless\n\nBy default, Pyppeteer is running in `Headless` mode, you can also\nchange it to `False` as you need, default is `True`:\n\n```python\nGERAPY_PYPPETEER_HEADLESS = False\n```\n\n### Window Size\n\nYou can also set the width and height of Pyppeteer window:\n\n```python\nGERAPY_PYPPETEER_WINDOW_WIDTH = 1400\nGERAPY_PYPPETEER_WINDOW_HEIGHT = 700\n```\n\nDefault is 1400, 700.\n\n### Proxy\n\nYou can set a proxy channel via below this config:\n\n```python\nGERAPY_PYPPETEER_PROXY = 'http://tps254.kdlapi.com:15818'\nGERAPY_PYPPETEER_PROXY_CREDENTIAL = {\n  'username': 'xxx',\n  'password': 'xxxx'\n}\n```\n\n### Pyppeteer Args\n\nYou can also change the args of Pyppeteer, such as `dumpio`, `devtools`, etc.\n\nOptional settings and their default values:\n\n```python\nGERAPY_PYPPETEER_DUMPIO = False\nGERAPY_PYPPETEER_DEVTOOLS = False\nGERAPY_PYPPETEER_EXECUTABLE_PATH = None\nGERAPY_PYPPETEER_DISABLE_EXTENSIONS = True\nGERAPY_PYPPETEER_HIDE_SCROLLBARS = True\nGERAPY_PYPPETEER_MUTE_AUDIO = True\nGERAPY_PYPPETEER_NO_SANDBOX = True\nGERAPY_PYPPETEER_DISABLE_SETUID_SANDBOX = True\nGERAPY_PYPPETEER_DISABLE_GPU = True\n```\n\n### Disable loading of specific resource type\n\nYou can disable the loading of specific resource type to\ndecrease the loading time of web page. You can configure\nthe disabled resource types using `GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES`:\n\n```python\nGERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES = []\n```\n\nFor example, if you want to disable the loading of css and javascript,\nyou can set as below:\n\n```python\nGERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES = ['stylesheet', 'script']\n```\n\nAll of the optional resource type list:\n\n- document: the Original HTML document\n- stylesheet: CSS files\n- script: JavaScript files\n- image: Images\n- media: Media files such as audios or videos\n- font: Fonts files\n- texttrack: Text Track files\n- xhr: Ajax Requests\n- fetch: Fetch Requests\n- eventsource: Event Source\n- websocket: Websocket\n- manifest: Manifest files\n- other: Other files\n\n### Screenshot\n\nYou can get screenshot of loaded page, you can pass `screenshot` args to `PyppeteerRequest` as dict:\n\n- `type` (str): Specify screenshot type, can be either `jpeg` or `png`. Defaults to `png`.\n- `quality` (int): The quality of the image, between 0-100. Not applicable to `png` image.\n- `fullPage` (bool): When true, take a screenshot of the full scrollable page. Defaults to `False`.\n- `clip` (dict): An object which specifies clipping region of the page. This option should have the following fields:\n  - `x` (int): x-coordinate of top-left corner of clip area.\n  - `y` (int): y-coordinate of top-left corner of clip area.\n  - `width` (int): width of clipping area.\n  - `height` (int): height of clipping area.\n- `omitBackground` (bool): Hide default white background and allow capturing screenshot with transparency.\n- `encoding` (str): The encoding of the image, can be either `base64` or `binary`. Defaults to `binary`. If binary it will return `BytesIO` object.\n\nFor example:\n\n```python\nyield PyppeteerRequest(start_url, callback=self.parse_index, wait_for='.item .name', screenshot={\n            'type': 'png',\n            'fullPage': True\n        })\n```\n\nthen you can get screenshot result in `response.meta['screenshot']`:\n\nSimplest save it to file:\n\n```python\ndef parse_index(self, response):\n    with open('screenshot.png', 'wb') as f:\n        f.write(response.meta['screenshot'].getbuffer())\n```\n\nIf you want to enable screenshot for all requests, you can configure it by `GERAPY_PYPPETEER_SCREENSHOT`.\n\nFor example:\n\n```python\nGERAPY_PYPPETEER_SCREENSHOT = {\n    'type': 'png',\n    'fullPage': True\n}\n```\n\n## PyppeteerRequest\n\n`PyppeteerRequest` provide args which can override global settings above.\n\n- url: request url\n- callback: callback\n- one of \"load\", \"domcontentloaded\", \"networkidle0\", \"networkidle2\".\n  see https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.page.Page.goto, default is `domcontentloaded`\n- wait_for: wait for some element to load, also supports dict\n- script: script to execute\n- actions: actions defined for execution of Page object\n- proxy: use proxy for this time, like `http://x.x.x.x:x`\n- proxy_credential: the proxy credential, like `{'username': 'xxxx', 'password': 'xxxx'}`\n- sleep: time to sleep after loaded, override `GERAPY_PYPPETEER_SLEEP`\n- timeout: load timeout, override `GERAPY_PYPPETEER_DOWNLOAD_TIMEOUT`\n- ignore_resource_types: ignored resource types, override `GERAPY_PYPPETEER_IGNORE_RESOURCE_TYPES`\n- pretend: pretend as normal browser, override `GERAPY_PYPPETEER_PRETEND`\n- screenshot: ignored resource types, see\n  https://miyakogi.github.io/pyppeteer/_modules/pyppeteer/page.html#Page.screenshot,\n  override `GERAPY_PYPPETEER_SCREENSHOT`\n\nFor example, you can configure PyppeteerRequest as:\n\n```python\nfrom gerapy_pyppeteer import PyppeteerRequest\n\ndef parse(self, response):\n    yield PyppeteerRequest(url,\n        callback=self.parse_detail,\n        wait_until='domcontentloaded',\n        wait_for='title',\n        script='() =\u003e { return {name: \"Germey\"} }',\n        sleep=2)\n```\n\nThen Pyppeteer will:\n\n- wait for document to load\n- wait for title to load\n- execute `console.log(document)` script\n- sleep for 2s\n- return the rendered web page content, get from `response.meta['screenshot']`\n- return the script executed result, get from `response.meta['script_result']`\n\nFor waiting mechanism controlled by JavaScript, you can use await in `script`, for example:\n\n```python\njs = '''async () =\u003e {\n    await new Promise(resolve =\u003e setTimeout(resolve, 10000));\n    return {\n        'name': 'Germey'\n    }\n}\n'''\nyield PyppeteerRequest(url, callback=self.parse, script=js)\n```\n\nThen you can get the script result from `response.meta['script_result']`, result is `{'name': 'Germey'}`.\n\nIf you think the JavaScript is wired to write, you can use actions argument to define a function to execute `Python` based functions, for example:\n\n```python\nasync def execute_actions(page: Page):\n    await page.evaluate('() =\u003e { document.title = \"Hello World\"; }')\n    return 1\nyield PyppeteerRequest(url, callback=self.parse, actions=execute_actions)\n```\n\nThen you can get the actions result from `response.meta['actions_result']`, result is `1`.\n\nAlso you can define proxy and proxy_credential for each Reqest, for example:\n\n```python\nyield PyppeteerRequest(\n  self.base_url,\n  callback=self.parse_index,\n  priority=10,\n  proxy='http://tps254.kdlapi.com:15818',\n  proxy_credential={\n      'username': 'xxxx',\n      'password': 'xxxx'\n})\n```\n\n`proxy` and `proxy_credential` will override the settings `GERAPY_PYPPETEER_PROXY` and `GERAPY_PYPPETEER_PROXY_CREDENTIAL`.\n\n## Example\n\nFor more detail, please see [example](./example).\n\nAlso you can directly run with Docker:\n\n```\ndocker run germey/gerapy-pyppeteer-example\n```\n\nOutputs:\n\n```shell script\n2020-07-13 01:49:13 [scrapy.utils.log] INFO: Scrapy 2.2.0 started (bot: example)\n2020-07-13 01:49:13 [scrapy.utils.log] INFO: Versions: lxml 4.3.3.0, libxml2 2.9.9, cssselect 1.1.0, parsel 1.6.0, w3lib 1.22.0, Twisted 20.3.0, Python 3.7.7 (default, May  6 2020, 04:59:01) - [Clang 4.0.1 (tags/RELEASE_401/final)], pyOpenSSL 19.1.0 (OpenSSL 1.1.1d  10 Sep 2019), cryptography 2.8, Platform Darwin-19.4.0-x86_64-i386-64bit\n2020-07-13 01:49:13 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor\n2020-07-13 01:49:13 [scrapy.crawler] INFO: Overridden settings:\n{'BOT_NAME': 'example',\n 'CONCURRENT_REQUESTS': 3,\n 'NEWSPIDER_MODULE': 'example.spiders',\n 'RETRY_HTTP_CODES': [403, 500, 502, 503, 504],\n 'SPIDER_MODULES': ['example.spiders']}\n2020-07-13 01:49:13 [scrapy.extensions.telnet] INFO: Telnet Password: 83c276fb41754bd0\n2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled extensions:\n['scrapy.extensions.corestats.CoreStats',\n 'scrapy.extensions.telnet.TelnetConsole',\n 'scrapy.extensions.memusage.MemoryUsage',\n 'scrapy.extensions.logstats.LogStats']\n2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled downloader middlewares:\n['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',\n 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',\n 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',\n 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',\n 'gerapy_pyppeteer.downloadermiddlewares.PyppeteerMiddleware',\n 'scrapy.downloadermiddlewares.retry.RetryMiddleware',\n 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',\n 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',\n 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',\n 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',\n 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',\n 'scrapy.downloadermiddlewares.stats.DownloaderStats']\n2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled spider middlewares:\n['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',\n 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',\n 'scrapy.spidermiddlewares.referer.RefererMiddleware',\n 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',\n 'scrapy.spidermiddlewares.depth.DepthMiddleware']\n2020-07-13 01:49:13 [scrapy.middleware] INFO: Enabled item pipelines:\n[]\n2020-07-13 01:49:13 [scrapy.core.engine] INFO: Spider opened\n2020-07-13 01:49:13 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)\n2020-07-13 01:49:13 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023\n2020-07-13 01:49:13 [example.spiders.book] INFO: crawling https://dynamic5.scrape.center/page/1\n2020-07-13 01:49:13 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/page/1\u003e\n2020-07-13 01:49:13 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:14 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/page/1\n2020-07-13 01:49:19 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: wait for .item .name finished\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: close pyppeteer\n2020-07-13 01:49:20 [scrapy.core.engine] DEBUG: Crawled (200) \u003cGET https://dynamic5.scrape.center/page/1\u003e (referer: None)\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/detail/26898909\u003e\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/detail/26861389\u003e\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/detail/26855315\u003e\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:20 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26855315\n2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26861389\n2020-07-13 01:49:21 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/26898909\n2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished\n2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: wait for .item .name finished\n2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: close pyppeteer\n2020-07-13 01:49:24 [scrapy.core.engine] DEBUG: Crawled (200) \u003cGET https://dynamic5.scrape.center/detail/26861389\u003e (referer: https://dynamic5.scrape.center/page/1)\n2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/page/2\u003e\n2020-07-13 01:49:24 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:25 [scrapy.core.scraper] DEBUG: Scraped from \u003c200 https://dynamic5.scrape.center/detail/26861389\u003e\n{'name': '壁穴ヘブンホール',\n 'score': '5.6',\n 'tags': ['BL漫画', '小基漫', 'BL', '『又腐又基』', 'BLコミック']}\n2020-07-13 01:49:25 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished\n2020-07-13 01:49:25 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/page/2\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: wait for .item .name finished\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: close pyppeteer\n2020-07-13 01:49:26 [scrapy.core.engine] DEBUG: Crawled (200) \u003cGET https://dynamic5.scrape.center/detail/26855315\u003e (referer: https://dynamic5.scrape.center/page/1)\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: processing request \u003cGET https://dynamic5.scrape.center/detail/27047626\u003e\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: set options {'headless': True, 'dumpio': False, 'devtools': False, 'args': ['--window-size=1400,700', '--disable-extensions', '--hide-scrollbars', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu']}\n2020-07-13 01:49:26 [scrapy.core.scraper] DEBUG: Scraped from \u003c200 https://dynamic5.scrape.center/detail/26855315\u003e\n{'name': '冒险小虎队', 'score': '9.4', 'tags': ['冒险小虎队', '童年', '冒险', '推理', '小时候读的']}\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: waiting for .item .name finished\n2020-07-13 01:49:26 [gerapy.pyppeteer] DEBUG: crawling https://dynamic5.scrape.center/detail/27047626\n2020-07-13 01:49:27 [gerapy.pyppeteer] DEBUG: wait for .item .name finished\n2020-07-13 01:49:27 [gerapy.pyppeteer] DEBUG: close pyppeteer\n...\n```\n\n## Trouble Shooting\n\n### Pyppeteer does not start properly\n\nChromium download speed is too slow, it can not be used normally.\n\nHere are two solutions:\n\n#### Solution 1 (Recommended)\n\nModify drive download source at `pyppeteer/chromium_downloader.py` line 22:\n\n```python\n# Default：\nDEFAULT_DOWNLOAD_HOST = 'https://storage.googleapis.com'\n# modify\nDEFAULT_DOWNLOAD_HOST = http://npm.taobao.org/mirror\n```\n\n#### Solution 2\n\nModify drive execution path at `pyppeteer/chromium_downloader.py` line 45:\n\n```python\n# Default：\nchromiumExecutable = {\n    'linux': DOWNLOADS_FOLDER / REVISION / 'chrome-linux' / 'chrome',\n    'mac': (DOWNLOADS_FOLDER / REVISION / 'chrome-mac' / 'Chromium.app' /\n            'Contents' / 'MacOS' / 'Chromium'),\n    'win32': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',\n    'win64': DOWNLOADS_FOLDER / REVISION / windowsArchive / 'chrome.exe',\n}\n```\n\nYou can find your own operating system, modify your chrome or chrome executable path.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerapy%2Fgerapypyppeteer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgerapy%2Fgerapypyppeteer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgerapy%2Fgerapypyppeteer/lists"}