听Resoul手把手教你使用nodejs+es6+co书写一个爬虫程序
bigegpt 2024-10-08 00:54 9 浏览
注意事项:
这里的爬虫不做太复杂的处理..
考虑到并发问题.这里的爬虫仅仅是爬完上一个后再爬下一个. 爬完当页后再去爬取下一页,效率虽然低..但是胜在不用同一时间发请大量请求避免被ban
本文以admin5.com为案例来爬取200页的文章title和content
本文涉及到的es6语法这里只会简单的说明一下.如果看不懂...来打我啊(笑)
涉及框架
crawler
co
cheerio
crawler:为一个封装好的nodejs爬虫库,免去你用request框架发请请求然后处理一大堆的返回代码问题.本文只把crawler当做请求工具用.内容的处理将会用cheerio框架来完成
co:能够把异步代码写成跟同步一样,号称es6的async.
cheerio:nodejs版的jQuery
分析目标网站url
目标网站的url都是
http://www.admin5.com/browse/19/list_${i}.shtml
${i}<=965
那么这就好办了.生成965个链接然后每次去爬一个链接
分享目标网站DOM结构
目标网站的每篇文字的链接都在一个class为sherry_title的a标签里
<a href="http://www.admin5.com/article/20161209/700550.shtml" class="sherry_title" target="_blank">我是如何通过论坛推广产品的?</a>
那么每次爬的时候获取当页的所有文章链接然后再去爬取
文章内容DOM结构
标题放在一个class为sherry_title的div下的h1标签中
<div class="sherry_title"> <h1>我是如何通过论坛推广产品的?</h1></div>
内容则放在一个class为content的div标签中
<div class='content'> </div>
那么内容中的图片如何爬取呢?这个也简单...不过这篇文章暂时不说..哈哈哈哈哈
爬取分析
分析完目标网站后.那么就开始分析如何去爬.
封装一个获取html的Promise函数
封装一个获取目录的Promise函数
获取一个获取文章内容的Promise函数
开始爬取函数
<!-- more -->
关于promise与co模块
首先我们知道关于最初的解决异步方案是callback(回调),当异步请求完毕后再去通知你的callback然后我们只能在callback里去做数据处理.这样很容易引起回调地狱.
a(function{ b(function{ c(function{ d(function{ }) }) })})
后来出现了promise.实际上也是改善了写法而已,promise会返回两种状态,成功(resolve)和失败(reject).就像你做事情一样,只有成功或者失败
function a(id){ return new Promise(function(resolve,reject){ setTimeout(function{ if(id>10){ reject(id) } resolve(id) },1000) }) } a(8).then((id)=>{ id+=10; return a(id) }) .then((id)=>{ }) .catch((id)=>{ })
上述封装了一个a函数,这个a函数执行的时候不可能立即返回一个id给你,因为有个定时器,等一秒后才会返回. 这个就是很明显的异步.然后我们把他封装成promise
当你调用a(id)的时候,实际上就已经开始执行这个函数了,不过因为我们a函数返回的是一个promise,这个promise会有个then方法.那么我们可以在then方法里面拿到1秒以后的id
promise有个特性是,你可以返回无限的promise,然后一直then,then,then下去.这算是改善了一种写法.不过重点不在于此.因为后面的co模块和Generator函数都是基于promise来完成的
Generator函数
这个说起来太长...篇幅问题.下次再谈
Co模块
其实简单点,我们并不需要知道内部调用.我们最终想要的效果仅仅是 让异步的写法变得优雅最好能够变成同步函数.ok.co函数和未来的async可以满足你这个需求
拿上述的a函数来说,在co中是这样处理的
co(function*{ let id=yield a(10) let id1=yield a(id); })
爽吧.只需要包裹在co里面,就可以达到同步写法的效果,那么这个yield后面的函数满足什么条件呢?
很简单,yield后面的函数只要是promise函数即可. 上述我们说过,promise有两种状态,一种是成功,一种是拒绝成功当然你就可以直接拿到let id=yield a(10);这个id值咯,假如失败如何监听呢? 也很简单
try{ let id=yield a(10); }catch(e){ }
用try,catch即可. 那么我没用try,catch 但是又返回了一个失败的状态.那错误在哪里?
说实在话..你如果不去捕捉的话..你这个错误会消失..对..就会消失掉. 如果你某一天发现你的程序无论如何也run不起来.但是莫名其妙又没报错.相信我兄弟..这锅promise绝对要背..
那我这种懒癌晚期的患者怎么办?不可能每次都要写try,catch吧?
在nodejs中有两个事件,可以监听到未捕捉的报错信息 那就是
process.on('unhandledRejection', function (err) { console.error(err.stack); }); process.on(`uncaughtException`, console.error);
其实不用管这个事件是啥意思.你每次加上就行了..程序运行起来的时候有很多问题都是我们考虑不到的..但是错误又被吞了.我们又不能进一步处理.这时候我们可以监听这两个事件.就算没写try catch 你都可以找到错误的源头.
说多了.咋们继续爬虫
获取html的Promise函数
let c=new Crawler({ retries:1, //超时重试次数 retryTimeout:3000 //超时时间});let contentJson=;const getHtml=co.wrap(function*(html){ return new Promise((resolve,reject)=>{ setTimeout(=>{ c.queue({ url:html, forceUTF8:true, callback:function (error,result,$) { if(error||!result.body){ errorCount++; return resolve({result:false}); } result=result.body; resolve({error,result,$}) } }) },2000) }) });
这里的let c=new Crawler 为初始化爬虫引擎,返回的是这个爬虫引擎的实例.
c.queue为爬取函数.
{ url:html, //爬取目标网站的url forceUTF8:true, // 强制转码为UTF-8 callback:function (error,result,$) { //error为如果爬取超时或者返回错误HTTP代码时会出现 if(error||!result.body){ return resolve({result:false}); } result=result.body; resolve({error,result,$}) } }
这里的$是框架已经调用了cheerio.不过我们这里不用框架封装好的cheerio.
获取目录的Promise函数
const getSubHtml=co.wrap(function*(body){ let $=cheerio.load(body); //字符串转为DOM let UrlElems=$("a.sherry_title"); //获取到目录中所有文章的url let subUrlList=; //链接存储数组 UrlElems.each((i,e)=>{ //循环获取链接并且存储起来 let url=$(e).attr('href'); let href=`${url}`; subUrlList.push(href); }); for(let item of subUrlList){ let {result}=yield getHtml(item); //获取每篇文章的body内容 if(!result){ continue; } let {title,content}=yield getContent(result); //获取标题和内容 console.log(`${title}获取完毕`); contentJson.push({ //最终存储到JSON数组中 title, content }) } });
获取每篇文章内容的Promise函数
嗯..实际上这里并不是异步的.只是从DOM中去获取内容.但是为了保持好看一致..这里也就用co来封装了一下
const getContent=co.wrap(function*(body){ let $=cheerio.load(body); //字符串转DOM let title=$(".sherry_title>h1").text; //获取标题 let content=$(".content").text; //获取内容 return Promise.resolve({title,content}) });
start函数
let urlList=;for(let i=1;i<=250;i++){ urlList.push(`http://www.admin5.com/browse/19/list_${i}.shtml`) } co(function*{ for (let url of urlList){ let {result}=yield getHtml(url); //获取目录body if(!result){ continue; } //console.log("result",result); //获取当页所有SUB yield getSubHtml(result); } console.info(`全部爬取完毕`,contentJson); });
添加全局错误监听函数
相关推荐
- pyproject.toml到底是什么东西?(py trim)
-
最近,在Twitter上有一个Python项目的维护者,他的项目因为构建失败而出现了一些bug(这个特别的项目不提供wheel,只提供sdist)。最终,发现这个bug是由于这个项目使用了一个pypr...
- BDP服务平台SDK for Python3发布(bdp数据平台)
-
下载地址https://github.com/imysm/opends-sdk-python3.git说明最近在开发和bdp平台有关的项目,用到了bdp的python的sdk,但是官方是基于p...
- Python-for-Android (p4a):(python-for-android p4a windows)
-
一、Python-for-Android(p4a)简介Python-for-Android(p4a),一个强大的开发工具,能够将你的Python应用程序打包成可在Android设备上运行...
- Qt for Python—Qt Designer 概览
-
前言本系列第三篇文章(QtforPython学习笔记—应用程序初探)、第四篇文章(QtforPython学习笔记—应用程序再探)中均是使用纯代码方式来开发PySide6GUI应用程序...
- Python:判断质数(jmu-python-判断质数)
-
#Python:判断质数defisPrime(n):foriinrange(2,n):ifn%i==0:return0re...
- 为什么那么多人讨厌Python(为什么python这么难)
-
Python那么棒,为什么那么多人讨厌它呢?我整理了一下,主要有这些原因:用缩进替代大括号许多人抱怨Python完全依赖于缩进来创建代码块,代码多一点就很难看到函数在哪里结束,那么你就需要把一个函数拆...
- 一文了解 Python 中带有 else 的循环语句 for-else/while-else
-
在本文中,我们将向您介绍如何在python中使用带有else的for/while循环语句。可能许多人对循环和else一起使用感到困惑,因为在if-else选择结构中else正常...
- python的numpy向量化语句为什么会比for快?
-
我们先来看看,python之类语言的for循环,和其它语言相比,额外付出了什么。我们知道,python是解释执行的。举例来说,执行x=1234+5678,对编译型语言,是从内存读入两个shor...
- 开眼界!Python遍历文件可以这样做
-
来源:【公众号】Python技术Python对于文件夹或者文件的遍历一般有两种操作方法,一种是至二级利用其封装好的walk方法操作:import osfor root,d...
- 告别简单format()!Python Formatter类让你的代码更专业
-
Python中Formatter类是string模块中的一个重要类,它实现了Python字符串格式化的底层机制,允许开发者创建自定义的格式化行为。通过深入理解Formatter类的工作原理和使用方法,...
- python学习——038如何将for循环改写成列表推导式
-
在Python里,列表推导式是一种能够简洁生成列表的表达式,可用于替换普通的for循环。下面是列表推导式的基本语法和常见应用场景。基本语法result=[]foriteminite...
- 详谈for循环和while循环的区别(for循环语句与while循环语句有什么区别)
-
初九,潜龙勿用在刚开始使用python循环语句时,经常会遇到for循环和while循环的混用,不清楚该如何选择;今天就对这2个循环语句做深入的分析,让大家更好地了解这2个循环语句以方便后续学习的加深。...
- Python编程基础:循环结构for和while
-
Python中的循环结构包括两个,一是遍历循环(for循环),一是条件循环(while循环)。遍历循环遍历循环(for循环)会挨个访问序列或可迭代对象的元素,并执行里面的代码块。foriinra...
- 学习编程第154天 python编程 for循环输出菱形图
-
今天学习的是刘金玉老师零基础Python教程第38期,主要内容是python编程for循环输出菱形※。(一)利用for循环输出菱形形状的*号图形1.思路:将菱形分解为上下两个部分三角形图案,分别利用...
- python 10个堪称完美的for循环实践
-
在Python中,for循环的高效使用能显著提升代码性能和可读性。以下是10个堪称完美的for循环实践,涵盖数据处理、算法优化和Pythonic编程风格:1.遍历列表同时获取索引(enumerate...
- 一周热门
- 最近发表
-
- pyproject.toml到底是什么东西?(py trim)
- BDP服务平台SDK for Python3发布(bdp数据平台)
- Python-for-Android (p4a):(python-for-android p4a windows)
- Qt for Python—Qt Designer 概览
- Python:判断质数(jmu-python-判断质数)
- 为什么那么多人讨厌Python(为什么python这么难)
- 一文了解 Python 中带有 else 的循环语句 for-else/while-else
- python的numpy向量化语句为什么会比for快?
- 开眼界!Python遍历文件可以这样做
- 告别简单format()!Python Formatter类让你的代码更专业
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- linux安装minio (74)
- ubuntuunzip (67)
- vscode使用技巧 (83)
- secure-file-priv (67)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)