-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
10分钟教你撸一个nodejs爬虫系统 #7
Comments
抓取首页文章列表20条数据根目录创建一个app.js文件。 实现思路步骤
1. 引入依赖:
2. 定义一个地址
3. 发起请求
这个时候我们会向简书首页发一个请求,只要不抛错,走if,那么就可以继续往下看了。 4. 页面数据解析
注释已经说明这行代码的意思,就不在说明了。就下了就比较难了。 5. 分析页面数据你需在浏览器打开简书官网,简书是后台渲染部分可见的数据,后续数据是通过ajax请求,使用js填充。我们爬数据,一般只能爬到后台渲染的部分,js渲染的是爬不到,如果ajax,你可以直接去爬api接口,那个日后再说。 言归正传,简书首页文章列表,默认会加载20条数据,这个已经够我用了,你每次刷新,如果有更新就会更新,最新的永远在最上面。 这20条数据存在页面一个类叫.note-list的ul里面,每条数据就是一个li,ul父级有一个id叫list-container,学过html的都知道id是唯一,保证不出错,我选择id往下查找。
上面就是cheerio帮我们获取到说有需要的文章列表的li,是不是和jq写一样。我要获取li里面内容就需要遍历
以上都比较简单,复杂的是下面的,数据结构。我们需要怎么拼装数据,我大致看了一下页面,根据经验总结了一个结构,还算靠谱。
基本数据结构有了,先定义一个数组data,来存放拼装的数据,留给后面使用。 随便截取一条文章数据
我们就拿定义的数据结构和实际的页面dom去一一比对,去获取我们想要的数据。 id: 每条文章id li上有一个 data-note-id="12732916"这个东西就是文章的id, slug:每条文章访问的id (加密的id) 如果你点文章标题,或者带缩略图的位置,都会跳转一个新页面 title: 标题 这个简单,$(elem).find('.title').text()就好了。 abstract: 描述 这个简单,$(elem).find('.abstract').text()就好了。 thumbnails: 缩略图 (如果文章有图,就会抓第一张,如果没有图就没有这个字段) 这个存在.wrap-img这a标签里面img里,如果没有就不显示,$(elem).find('.wrap-img img').attr('src'),如果取不到就是一个undefined,那正合我意。 下面4个都在.meta的div里面 (我没有去打赏的数据,因为我不需要这个数据) collection_tag:文集分类标签 有对应的class,$(elem).find('.collection-tag').text() reads_count: 阅读计数 这个就比较麻烦了,它的结构是这样的
还要有一个字体图标的class可以使用,不然还真不好玩,那需要怎么获取了,$(elem).find('.ic-list-read').parent().text(),先去查找这个字体图标i标签,然后去找它的父级a标签,获取里面text文本,标签就不被获取了,只剩下数字。 接下来2个一样处理的。 comments_count: 评论计数 $(elem).find('.ic-list-comments').parent().text() likes_count:喜欢计数 $(elem).find('.ic-list-like').parent().text() 接来就是会员信息,全部都在.author这个div里面 id:没有找到 slug: 每个用户访问的id (加密的id) 这个处理方式和文章slug一样,$(elem).find('.avatar').attr('href').replace(//u//, ""),唯一不同的需要吧p换成u。 avatar:会员头像 $(elem).find('.avatar img').attr('src') nickname:会员昵称(注册填的那个) 昵称存在一个叫.blue-link标签里面,$(elem).find('.blue-link').text() sharedTime:发布日期 这个发布日期,你看到页面是个性化时间,xx小时前啥的,如果直接取就是一个坑爹的事了,在.time的span上有一个data-shared-at="2017-05-24T08:05:12+08:00"这个才是正真的时间,你会发现它一上来是空的,是js来格式化的。$(elem).find('.time').attr('data-shared-at') 以上就是所有字段来源的。接下来要说一个坑爹的事,text()获取出来的,有回车符/n和空格符/s。所以需要写一个方法把它们去掉。
组装起来的数据代码:
let _this = 有几个*1是吧数字字符串转成数字,js小技巧,不解释。 6. 生成数据数据已经可以获取了,都存在data这个数据里面,现在是20条数据,我们理想的数据,那么放在node里面,我们还是拿不到,怎么办,一个存在数据库(还没有弄到哪里,我都还没有想好怎么建数据库表设计),一个就存在本地json文件。 那就存在本地json文件。nodejs是一个服务端语言,就说可以访问本地磁盘,添加文件和访问文件。需要引入nodejs内置的包fs。
它的其他用法不解释了,只说一个创建一个文件,并且在里面写内容 这是写文件的方法:
我们需要这样来写了:
以上基本就完成一个列表页面的抓取。看下完整代码:
一个简书首页文章列表的爬虫就大工告成了,运行代码,打开Terminal运行node app.js或者node app都行。或者在package.json的scripts对象下添加一个"dev": "node app.js",然后用webstorm的npm面板运行。
|
抓取首页文章列表对应的20条详情数据有了上面抓取文章列表的经验,接下来就好办多了,完事开头难。 实现思路步骤
1. 引入依赖这个就不用引入,在一个文件里面,因为比较简单的,代码不多,懒得分文件写。导入导出模块麻烦,人懒就这样的。 但我们需要写一个函数,来处理爬详情的方法。
2. 定义一个地址注意这个地址,是有规律的,不是随便的地址,随便点开一篇文章就可以看到地址栏, 3. 发起请求
你懂的 4. 页面数据解析
5. 分析页面数据你可能会按上面的方法,打开一个页面,然后就去获取标签上面的class,id。我开始也在这个上面遇到一个坑,页面上有阅读 ,评论 ,喜欢 这三个数据,我一开始以为都是直接load页面就有数据,在获取时候,并没有数据,是一个空。我就奇怪,然后我就按了几次f5刷新,发现问题了,这几个数据的是页面加载完成以后才显示出来的,那么就是说这个有可能是js渲染填充的。那就说明的我写的代码没有错。 有问题要解决呀,如果是js渲染,要么会有网络加载,刷新几次,没有这个数据,那就只能存在页面里,写的内联的script标签里面了,右键查看源码,ctrl+f搜索,把阅读 ,评论 ,喜欢的数字,随便挑一个,找到了最底部data-name="page-data"的script标签里面,有一个json对象,里面有些字段,和我文章列表定义很像,就是这个。有了这个就好办了,省的我去截取一大堆操作。 解析script数据
script里面数据
把script里面内容都获取出来,然后用 JSON方法,字符串转对象。 接下来依旧是要定义数据结构:
还要专题分类和评论列表我没有累出来,有兴趣可以自己去看看怎么爬出来。它们是单独api接口,数据结构就不需要了。 因为有了note 这个对象很多数据都简单了,还是一个一个说明来源 article 文章信息 id: 文章id 主要信息都存在note.note里面,文章id就是note.note.id, slug: 每条文章访问的id (加密的id) note.note.slug title: 标题 content: 正文(记得要带html标签的) 注意正文不是获取text文本是要获取html标签,需要用到html来获取而不是text,$('div.post').find('.article .show-content').html() 返回都是转义字符。到时候前端需要处理就会显示了。虽然我们看不懂,浏览器看得懂就行了。 publishTime: 更新时间 这时间直接显示出来了,不是个性化时间,直接取就好了$('div.post').find('.article .publish-time').text() wordage: 字数 这个是一个标签里面<字数 1230>这样的,我们肯定不能要这样的,需要吧数字提取出来,$('div.post').find('.article .wordage').text().match(/\d+/g)[0]*1 用正则获取数字字符串,然后转成数字。 views_count: 阅读计数 note.note.views_count comments_count: 评论计数 note.note.comments_count likes_count: 喜欢计数 note.note.likes_count author 用户信息 id: 用户id 前面的文章列表我们并没有拿到用户id,note.note发现了一个user_id,反正不管是不是先存了再说,别空着,note.note.user_id slug: 每个用户访问的id (加密的id) 文章列表怎么获取,这个就怎么获取$('div.post').find('.avatar').attr('href').replace(//u//, "") avatar: 会员头像 $('div.post').find('.avatar img').attr('src') nickname: 会员昵称(注册填的那个) $('div.post').find('.author .name a').text() signature: 会员昵称签名 这个签名在上面位置了,就在文章正文下面,评论和打赏上面,有个很大关注按钮那个灰色框框里面,最先一段文字。$('div.post').find('.signature').text() total_wordage: 总字数 note.note.author.total_wordage followers_count: 总关注计数 note.note.author.followers_count total_likes_count: 总喜欢计数 note.note.author.total_likes_count 有些字段命名就是从note.note这个json对象里面获取的,一开始我也不知道取什么名字。 最终拼接的数据
6. 生成数据和列表生成数据基本一样,有一个区别。文件需要加一个标识,article_+ item.slug(文章访问的id)
基本就撸完了,看获取详情的完整代码:
你肯定要问了,在哪里调用了,
运行,你就会在data文件夹里看到21个json文件。源文件,欢迎指正Bug。 备用地址:
|
pl |
通俗易懂! |
感谢分享 |
@titianqx 感谢支持 |
感谢分享! |
@FabiusChiang 感谢支持! |
感謝分享 |
感谢分享 |
403 Forbidden,这个情况怎么处理啊,感觉被网站针对了,别的网站比如百度就没事 |
@hehaijian 换个ip就可以了,可能是被人限制你的ip,这篇文字只是教你爬虫怎么写,能试着爬其他网站的内容,得到你想要你才真正学会,我这只是一个栗子而已。 |
@jiayisheji 感谢您的回复,我可能没有说清楚,我的疑问是,我用chrome浏览是能出来东西的,但是用咱们这个请求返回就是403,这种情况是为什么呢? |
@hehaijian 请求返回403 没有权限,简书访问是有限制的,如果你没有登录,不会让你频繁刷新,再说你用爬虫本身就是一个非法操作,推荐使用谷歌 Puppeteer,它目前做爬虫,自动化测试,都很不错的工具。有兴趣可以去了解一下。 |
感谢,一起进步
jiayi <[email protected]> 于2019年3月29日周五 上午9:06写道:
… @hehaijian <https://github.com/hehaijian> 请求返回403
没有权限,简书访问是有限制的,如果你没有登录,不会让你频繁刷新,再说你用爬虫本身就是一个非法操作,推荐使用谷歌 Puppeteer
<https://github.com/GoogleChrome/puppeteer>
,它目前做爬虫,自动化测试,都很不错的工具。有兴趣可以去了解一下。
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#7 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AI0ZM2fuhx3zi3ZphE9j5l9fHUvgZlaoks5vbWchgaJpZM4Nks42>
.
|
项目初始化
安装nodejs,官网, 中文网。根据自己系统安装,这里跳过,表示你已经安装了nodejs。
选择一款顺手拉风的编辑器,用来写代码。推荐webstorm最近版。
webstorm创建一个工程,起一个喜欢的名字。创建一个package.json文件,webstorm快捷创建package.json非常简单。还是用命令行创建,打开Terminal,默认当前项目根目录,npm init,一直下一步。
主要技术栈
这是2个npm包,我们先下载在接着继续,下载需要时间的。
接下啦简单说说这2个是啥东西
superagent 页面数据下载
请求方式
写法:
设置Content-Type
设置方式:
以上三种方效果一样。
设置参数
query
设置请求参数,可以写json对象或者字符串形式。
json对象{key,value}
可以写多组key,value
字符串形式key=value
可以写多组key=value,需要用&隔开
sned
设置请求参数,可以写json对象或者字符串形式。
json对象{key,value}
可以写多组key,value
字符串形式key=value
可以写多组key=value,需要用&隔开
上面两种方式可以使用在一起
响应属性Response
Response text
Response.text包含未解析前的响应内容,一般只在mime类型能够匹配text/json、x-www-form-urlencoding的情况下,默认为nodejs客户端提供,这是为了节省内存,因为当响应以文件或者图片大内容的情况下影响性能。
Response header fields
Response.header包含解析之后的响应头数据,键值都是node处理成小写字母形式,比如res.header('content-length')。
Response Content-Type
Content-Type响应头字段是一个特列,服务器提供res.type来访问它,默认res.charset是空的,如果有的化,则自动填充,例如Content-Type值为text/html;charset=utf8,则res.type为text/html;res.charset为utf8。
Response status
http响应规范
cheerio 页面数据解析
cheerio是一个node的库,可以理解为一个Node.js版本的jquery,用来从网页中以 css selector取数据,使用方式和jquery基本相同。
需要先loading一个需要加载html文档,后面就可以jQuery一样使用操作页面了。
基本所有选择器基本和jQuery一样,就不一一列举。具体怎么使用看官网。
The text was updated successfully, but these errors were encountered: