{"id":22957454,"url":"https://github.com/lixinyang123/crawlergenerator","last_synced_at":"2025-08-13T03:33:11.739Z","repository":{"id":55752042,"uuid":"238224530","full_name":"lixinyang123/CrawlerGenerator","owner":"lixinyang123","description":"Puppeteer CrawlerGenerator","archived":false,"fork":false,"pushed_at":"2022-11-10T22:22:32.000Z","size":11529,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-01T15:56:34.612Z","etag":null,"topics":["electron","nodejs","puppeteer"],"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/lixinyang123.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}},"created_at":"2020-02-04T14:23:10.000Z","updated_at":"2023-12-21T03:25:52.000Z","dependencies_parsed_at":"2022-08-15T06:41:11.165Z","dependency_job_id":null,"html_url":"https://github.com/lixinyang123/CrawlerGenerator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lixinyang123/CrawlerGenerator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lixinyang123%2FCrawlerGenerator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lixinyang123%2FCrawlerGenerator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lixinyang123%2FCrawlerGenerator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lixinyang123%2FCrawlerGenerator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lixinyang123","download_url":"https://codeload.github.com/lixinyang123/CrawlerGenerator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lixinyang123%2FCrawlerGenerator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270175827,"owners_count":24540094,"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","status":"online","status_checked_at":"2025-08-13T02:00:09.904Z","response_time":66,"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":["electron","nodejs","puppeteer"],"created_at":"2024-12-14T17:16:53.334Z","updated_at":"2025-08-13T03:33:10.927Z","avatar_url":"https://github.com/lixinyang123.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## jsdom\n\n因为我不喜欢Python（现在感觉Python还挺香的😂），而且js操作dom效率应该更高一点吧。。。。毕竟爬自己人。。。所以我最开始选择用nodejs来写爬虫，有一个包叫 [jsdom](https://github.com/jsdom/jsdom \"jsdom\") 挺好用的。请求网页，然后丢jsdom里面就可以使用它来获取网页上的元素。\n\n```javascript\nconst jsdom = require(\"jsdom\");\nconst { JSDOM } = jsdom;\nconst dom = new JSDOM(`\u003c!DOCTYPE html\u003e\u003cp\u003eHello world\u003c/p\u003e`);\nconsole.log(dom.window.document.querySelector(\"p\").textContent);\n```\n\n简单易用\n\n不过遇到一些渐进式的网页，或者spa就比较剌蛋了。。。会比较麻烦，所以 [Puppeteer](https://github.com/puppeteer/puppeteer \"Puppeteer\") 就是一个很好的选择。\n\n## 什么是Puppeteer?\n\nPuppeteer 是一个 Node 库，它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless 模式运行，但是可以通过修改配置文件运行“有头”模式。\n\n![](https://corehome.oss-cn-shenzhen.aliyuncs.com/blogs/puppeteer.png)\n\n#### 你可以使用Puppeteer做什么？\n- 生成页面 PDF。\n- 抓取 SPA（单页应用）并生成预渲染内容（即“SSR”（服务器端渲染））。\n- 自动提交表单，进行 UI 测试，键盘输入等。\n- 创建一个时时更新的自动化测试环境。 使用最新的 JavaScript 和浏览器功能直接在最新版本的Chrome中执行测试。\n- 捕获网站的 timeline trace，用来帮助分析性能问题。\n- 测试浏览器扩展。\n\n#### Puppeteer简单使用\n\n在项目中使用 Puppeteer：\n\u003e npm i puppeteer\n\n注意：puppeteer-core 只包含无头 Chrome API 不会下载 Chromium，如果你的电脑上有Chromium可以直接执行：\n\u003e npm i puppeteer-core\n\n创建main.js\n\u003e Puppeteer 至少需要 Node v6.4.0\n\n```javascript\nconst puppeteer = require('puppeteer');\n\n(async () =\u003e {\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n  await page.goto('https://www.lllxy.net');\n  await page.screenshot({path: 'example.png'});\n\n  await browser.close();\n})();\n```\n\n接下来在命令行中执行\n\n\u003e node main.js\n\nPuppeteer 初始化的屏幕大小默认为 800px * 600px。但是这个尺寸可以通过 [Page.setViewport()](https://github.com/GoogleChrome/puppeteer/blob/v1.10.0/docs/api.md#pagesetviewportviewport \"Page.setViewport()\") 设置。\n\n接下来你就会得到一张网页截图。\n\n## 爬虫生成器\n\n上述两种方式写爬虫都不错，看情况选择就好了，但是。。。。由于我比较懒，每次都要自己写就感觉很不爽，如果说可以不需要自己写，点点鼠标就可以生成脚本在网页上做一些自动化工作，那样岂不是很爽。所以想法就产生了，来看[Puppeteer文档](https://zhaoqize.github.io/puppeteer-api-zh_CN/ \"Puppeteer文档\")。\n\n先看[选择器部分](https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer\u0026version=v3.0.2\u0026show=api-pageselector \"选择器部分\")\n\n-  **page.$(selector)**\n此方法在页面内执行 document.querySelector。如果没有元素匹配指定选择器，返回值是 null。\n\n- **page.$$(selector)**\n此方法在页面内执行 document.querySelectorAll。如果没有元素匹配指定选择器，返回值是 [ ]。\n\n看到这有没有想到点什么？只要有了 QuerySelector 就可以实现 选取/点击/截图/下载/提取属性 等等Puppeteer的任何功能。所以只需要实现点击鼠标生成 QuerySelector 的功能，其余的无非就是把 queryselector 套到各种操作的模板里面就行了。\n\n下面就是最核心的部分，生成 QuerySelector\n\n## 生成 QuerySelector\n\n如果你还不知道QuerySelector，看[文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/querySelector \"文档\")去。\n\n首先要获取鼠标指到的元素，给指到的元素加个红色边框把。。。。\n\n```javascript\ndocument.onmouseover = () =\u003e {\n    obj = event.srcElement;\n    obj.style.border = \"2px solid red\";\n}\n```\n\n随便打开个网页晃晃鼠标试试。。。\n\n![](https://corehome.oss-cn-shenzhen.aliyuncs.com/blogs/%E6%89%B9%E6%B3%A8%202020-05-07%20193017.jpg)\n\n这样肯定不行，所以要加上鼠标移出元素清除边框\n\n```javascript\nvar obj = null;\n\nfunction hightLight(){\n    obj = event.srcElement;\n    obj.style.border = \"2px solid red\";\n}\n\nfunction reset(){\n    obj.style.border = \"none\";\n    obj = null;\n}\n\ndocument.onmouseover = hightLight;\ndocument.onmouseout = reset;\n```\n\n现在应该是指谁谁红，然后鼠标移出后清除红色边框\n\n下来就是生成 QuerySelector ，生成的时候肯定要有一个优先级，如果元素有id，那么可以直接用id查找，如果没有id可以考虑用class，如果没有class那就只能考虑用TagName查找，当然TagName是万能的，可以只用TagName加上此元素在所有此TagName数组中的索引来定位元素，但是。。。经常定位不准确，比如网页少加载了点什么东西，你就无法定位到你想要的元素了。所以，还用第一个方案。\n\n说一下思路，创建一个数组，如果此元素有id，直接把 **#id** push到数组。如果没有id则判断父元素中有几个相同TagName的元素，如果只有一个，直接保存 **tagname** 。如果不止一个相同TagName的元素则判断父元素中有几个相同class的元素，只有一个直接存 **.classname** , 如果存在很多个相同class的元素 或者 元素压根没有class，那么就按TagName,父元素来个循环，判断第几个才是此元素接下来保存 **element.tagName+\":nth-child(\"+tagIndex+\")\"** 接下来将父元素设为当前元素，依次往上一级查找，直到查找到有id存在的父元素，停止循环，如果父元素一直没有id，那就会按tagname一直查到body那一级，接下来反转数组，你就会得到一个完整的QuerySelector选择器 。下面直接上代码。\n\n```javascript\nvar obj = null;\n\nfunction hightLight(){\n    obj = event.srcElement;\n    obj.style.border = \"2px solid red\";\n}\n\nfunction reset(){\n    obj.style.border = \"none\";\n    obj = null;\n}\n\nfunction selectElementWithQuerySelector(){\n    var array = new Array();\n    \n    var element = obj;\n    \n    while(element.parentElement!=undefined){\n        if(element.id!=\"\"){\n            array.push(\"#\"+element.id);\n            //array.push(\"[id='\"+element.id+\"']\");\n            break;\n        }\n        else{\n            if(element.parentElement.children.length==1){\n                array.push(element.tagName);\n            }\n            else{\n                var tagLength = element.parentElement.getElementsByTagName(element.tagName).length;\n                if(tagLength==1){\n                    array.push(element.tagName);\n                }\n                else{\n                    var fullClassName = \"\";\n                    element.classList.forEach(cName =\u003e {\n                        fullClassName += \".\"+cName;\n                    });\n                    \n                    if(element.parentElement.querySelectorAll(element.tagName+fullClassName).length==1){\n                        array.push(element.tagName+fullClassName);\n                    }\n                    else{\n                        var children = element.parentElement.children;\n                        var tagIndex = 0;\n                        for(var i=0;i\u003cchildren.length;i++){\n                            if(children[i].isEqualNode(element)){\n                                tagIndex = ++i;\n                            }\n                        }\n\n                        array.push(element.tagName+\":nth-child(\"+tagIndex+\")\");\n                    }\n                }\n\n            }\n        }\n        element = element.parentElement;\n    }\n    \n    var statement = \"\";\n\n    array = array.reverse();\n\n    for(var i=0;i\u003carray.length;i++){\n        statement += array[i]+\"\u003e\";\n    }\n    statement = statement.substring(0,statement.length-1);\n\n    console.log(statement);\n}\n\n\ndocument.onmouseover = hightLight;\ndocument.onmouseout = reset;\ndocument.oncontextmenu = selectElementWithQuerySelector;\n```\n\n接下来在元素上点击鼠标右键，控制台就会输出 QuerySelector \n\n## 生成Puppeteer脚本\n\nQuerySelector有了，接下来只需要和Puppeteer的特定操作的模板相结合，之后拼接到一起就可以实现生成爬虫。\n\n放几个操作的模板（简化了，实际肯定没这么简单。。。而且不止这几个，还有跳转啊，全屏截图什么的，一大堆。。。我就放几个打个比方）\n```javascript\n//获取文本\nvar str = await page.$eval(\"@parameter\",ele=\u003eele.innerText);\n\n//点击元素\nawait page.click(\"@parameter\");\n\n//获取图片链接\nvar src = await page.$eval(\"@parameter\",ele=\u003eele.src);\n\n//获取元素截图\nawait (await page.$(\"@parameter\")).screenshot({path: \"screenshot.jpg\"});\n\n//输入文本\nawait page.type(\"@parameter\", \"@text\");\n```\n将生成的 QuerySelector 和模板中的 parameter 进行替换并且相互组合，当然你还需要一个最基本的代码片段，创建puppeteer。。。然后把生成的内容组装，接下来导出即可。\n\n#### 为什么选择Electron？\n\n使用Electron主要原因是Puppeteer用的是Chromium浏览器，生成爬虫时使用的浏览器尽量也用Chromium，避免不同浏览器导致元素选取出问题。\n\n#### 怎么将脚本注入浏览器？如何将不同网页生成的脚本都保存起来？\n\n目前有两种解决方案\n- 客户端带一个服务器\n点击生成脚本的时候，向这个服务器发请求，然后暂存起来（当然你在不同的网页给localhost发请求，肯定涉及跨域问题，所以要处理这个问题）\n- Electron和JS交互（具体可以看[Electron文档](http://www.electronjs.org/docs/api/web-contents#contentsexecutejavascriptcode-usergesture \"文档\")，这里直接放代码）\n\n```javascript\nwebContents.executeJavaScript(\"saveScript()\").then((result)=\u003e{\n\tconsole.log(result);\n});\n```\n\n当然使用这种方法，你需要写一个SaveScript方法,返回当前页面生成的脚本，而且每当页面要刷新时，也要调用此方法。\n\n接下来将生成的所有脚本组合到一起，导出即可。\n\n## 导出脚本怎么使用\n\n给用户导出脚本，没有Puppeteer的依赖，用户依旧不能运行，但是带上项目依赖的话导出一个Chromium浏览器又太大了，目前采用的方式是在服务器跑chrome，然后脚本都在服务器上运行，给用户的脚本只包含了puppeteer-core，至于怎么在服务器跑chrome来供puppeteer来使用，可以了解一下 [browserless](https://github.com/browserless/chrome \"这个\") 。\n\n当然我自己用的时候肯定没有这样干，不过我测试了一下，这样子完全可行，而且给用户生成的脚本体积较小。\n\n## 最后\n\n如果你对这个小破软件感兴趣，可以 [点击这里](https://github.com/lixinyang123/CrawlerGenerator \"了解一下\") 了解一下，当然现在还是早期版本。。。。比较劣质\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flixinyang123%2Fcrawlergenerator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flixinyang123%2Fcrawlergenerator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flixinyang123%2Fcrawlergenerator/lists"}