{"id":13630207,"url":"https://github.com/christophebe/serp","last_synced_at":"2025-04-09T23:19:01.744Z","repository":{"id":2352144,"uuid":"46297598","full_name":"christophebe/serp","owner":"christophebe","description":"Google Search SERP Scraper ","archived":false,"fork":false,"pushed_at":"2023-06-13T16:49:49.000Z","size":456,"stargazers_count":108,"open_issues_count":9,"forks_count":24,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-02T16:58:38.473Z","etag":null,"topics":["google","scraper","seo","serp","serps"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/christophebe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2015-11-16T19:38:25.000Z","updated_at":"2025-04-02T12:36:10.000Z","dependencies_parsed_at":"2023-07-05T19:02:01.382Z","dependency_job_id":null,"html_url":"https://github.com/christophebe/serp","commit_stats":{"total_commits":47,"total_committers":4,"mean_commits":11.75,"dds":"0.14893617021276595","last_synced_commit":"6ac3bd802f42045a2cc326f94ed26b331d57fbee"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebe%2Fserp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebe%2Fserp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebe%2Fserp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/christophebe%2Fserp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/christophebe","download_url":"https://codeload.github.com/christophebe/serp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125649,"owners_count":21051778,"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":["google","scraper","seo","serp","serps"],"created_at":"2024-08-01T22:01:33.735Z","updated_at":"2025-04-09T23:19:01.713Z","avatar_url":"https://github.com/christophebe.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# serp\n\nThis module allows to get the result of a Google search based on a keyword.\n\nIt provides different options for scraping the google results called SERP (Search Engine Result Page) : \n- delay between requests\n- retry if error \n- with or without proxy, proxies or scrape API. \n\nThis module uses Playwright in order to accept the cookie consent popup before making a new search. \n\n# Installation\n\n``` bash\n$ npm install serp -S\n```\n\n\n# Simple usage\n\n``` javascript\nconst serp = require(\"serp\");\n\nvar options = {\n  host : \"google.fr\",\n  qs : {\n    q : \"test\",\n    filter : 0,\n    pws : 0\n  },\n  num : 100\n};\n\nconst links = await serp.search(options);\n```\n\n*Understanding the options structure :*\n- For google.com, the param host is not necessary.\n- qs can contain the usual Google search parameters : https://moz.com/ugc/the-ultimate-guide-to-the-google-search-parameters.\n- options.qs.q is the keyword\n- num is the number of desired results (defaut is 10).\n\n## Delay between requests\n\nIt is possible to add a delay between each request made on Google with the option *delay* (value in ms).\nThe delay is also applied when the tool read the next result page on Google.\n\n\n``` javascript\nconst serp = require(\"serp\");\n\nvar options = {\n\n  qs : {\n    q : \"test\"\n  },\n  num : 100,\n  delay : 2000 // in ms\n};\n\nconst links = await serp.search(options);\n```\n\n## Retry if error\n\nIf an error occurs (timeout, network issue, invalid HTTP status, ...), it is possible to retry the same request on Google. If a proxyList is set into the options, another proxy will be used.\n\n``` javascript\nconst serp = require(\"serp\");\n\nvar options = {\n\n  qs : {\n    q : \"test\"\n  },\n  num : 100,\n  retry : 3,\n  proxyList : proxyList\n};\n\nconst links = serp.search(options);\n```\n\n## Get the number of results\n\nYou can get the number of indexed pages in Google by using the following code.\n\n\n``` javascript\nconst serp = require(\"serp\");\n\nvar options = {\n  host : \"google.fr\",\n  numberOfResults : true,\n  qs : {\n    q   : \"site:yoursite.com\"\n  },\n  proxyList : proxyList\n};\n\nconst numberOfResults = await serp.search(options);\n```\n\n\n\n## With proxy\n\nYou can add the proxy reference in the options\n\n``` javascript\nconst serp = require(\"serp\");\n\nvar options = {\n  qs : {\n    q : \"test\",\n  },\n  proxy : {\n        server: 'hots:port',\n        username: 'username',\n        password: 'password'\n  }\n  \n};\n\n\nconst links = await serp.search(options);\n```\n\n# With multiple proxies\n\nYou can also use the module simple proxy if you have several proxies (see : https://github.com/christophebe/simple-proxies).\nIn this case, a different proxies (choose randomly) will be used of each *serp.search* call. \n\n\nSee [this unit test](https://github.com/christophebe/serp/blob/master/test/test-proxies.js) to get the complete code. \nThe proxies have to be in a txt file : one line for each proxy with the following structure : host:port:user:password\n\n``` javascript\nconst  serp = require(\"serp\");\n\nvar options = {\n  qs : {\n    q : \"test\",\n  },\n  proxyList : proxyList\n};\n\nconst links = await serp.search(options);\n```\n\n# with a scrape API\n\nThis module can use a scrape API instead of a list of proxies.\n\nThis is an example with [scrapeapi.com](https://www.scraperapi.com/?fp_ref=christophe65)\n\n``` javascript\nconst options = {\n      num: 10,\n      qs: {\n        q: 'test'\n      },\n      scrapeApiUrl: `http://api.scraperapi.com/?api_key=${ accessKey }`\n    };\n\n    try {\n      const links = await serp.search(options);\n\n      // console.log(links);\n      expect(links).to.have.lengthOf(10);\n    } catch (e) {\n      console.log('Error', e);\n      expect(e).be.null;\n    }\n```     \n\n## Proxies or Scrape API ? \n\nIf you make many requests at the same time or over a limited period of time, Google may ban your IP address.  This can happen even faster if you use particular search commands such as:  intitle, inurl, site:, ... \n\nIt is therefore recommended to use proxies. The SERP module supports two solutions: \n- Datacenter proxies  like for example those proposed by [Mexela](https://mexela.com/aff.php?aff=191). Shared proxies are more than enough. \n\n- Scrape APIs such as [scrapeapi.com](https://www.scraperapi.com/?fp_ref=christophe65)\n\n**What to choose? Datacenter proxies or Scrape API ?** \n\nIt all depends on what you are looking for. Datacenter proxies will provide the best performance and are generally very reliable. You can use the \"retry\" option to guarantee even more reliability. It's also a solution that offers a good quality/price ratio but it will require more effort in terms of development, especially for the rotation of proxies. If you want to use rotation with datacenter proxies, see [this unit test](https://github.com/christophebe/serp/blob/master/test/test-proxies.js).\n\nAlthough slower, the scrape APIs offer other features such as the geolocation of IP addresses over a larger number of countries and the ability to scrape dynamic pages. Using such an API can also simplify the code. Unfortunately, this solution is often more expensive than data center proxies. So, scrape APIs becomes interesting if you have other scrape needs. \n\nIn all cases, make a test with [shared proxies](https://mexela.com/aff.php?aff=191) in order to check it is suffisiant for your use cases.Those proxies are really cheap. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophebe%2Fserp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchristophebe%2Fserp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchristophebe%2Fserp/lists"}