nodejs+request+cheerio实现网络爬虫
2016-12-01 17:02
801 查看
nodejs是运行在服务器上的JavaScript。网络爬虫的实现由很多种方式(采用的库有很多选择),网络爬虫的类型也有很多(定向爬虫,分布式爬虫)
网页的访问需要注意的是:某些网页 需要登录才能进入,所以需要使用cherom浏览器看下网页源码,登录操作是否采用表单form提交方式,这种访问通常是POST形式,不需要登录就能访问的网页通常采用GET访问方式。
访问一些需要提交form表单的网页要加上访问参数
为了使爬虫以最大限度访问网页,最好在访问参数里面设置访问头headers;访问请求网页可以采用request库
并下载网页:
访问网页成功后,会返回HTML流,该流其实就是字符串形式。解析网页也是解析该流
返回的HTML分为:response,body
response:应答流,里面包含响应头,body和一些其他内容。通常服务器应答的session,或者传过来的cookies也是放在响应头里面(如果有的话),如果只是想解析网页正文,只需要解析body即可,body是展示到浏览器给用户能看到的内容。
解析网页:
网页下载完成之后,需要把HTML流也就是HTML字符串转换成DOM对象,然后解析(这个过程和浏览器解析网页是一样的),在解析过程中我们可以查找DOM文档中是否有我们需要的信息,访问的方式和jQuery查找,定位网页元素一样(按照元素名,按照id,按照class,具体请参考jQuery);解析网页通常采用插件或者库进行,cheerio就是一个很好的解析库。内部实现采用jQuery
分布式爬虫需要有
网页调度器
网页调度器主要负责协调其他管理器工作,处于整个爬虫中心位置
URL管理器
主要负责管理所有已经爬取过的URL和等待爬取的URL,URL的收集,URL的流出
网页解析器
主要对下载后的HTML流解析出数据
网页下载器
下载器主要是根据URL地址下载对应HTML流,下载器还要对目标网页的访问,提交from表单,接收数据
下面示例代码有部分是URL下载器的功能,另外部分是别的管理器的功能(没有来得及分离)
流程介绍
访问网页:网页的访问需要注意的是:某些网页 需要登录才能进入,所以需要使用cherom浏览器看下网页源码,登录操作是否采用表单form提交方式,这种访问通常是POST形式,不需要登录就能访问的网页通常采用GET访问方式。
访问一些需要提交form表单的网页要加上访问参数
为了使爬虫以最大限度访问网页,最好在访问参数里面设置访问头headers;访问请求网页可以采用request库
并下载网页:
访问网页成功后,会返回HTML流,该流其实就是字符串形式。解析网页也是解析该流
返回的HTML分为:response,body
response:应答流,里面包含响应头,body和一些其他内容。通常服务器应答的session,或者传过来的cookies也是放在响应头里面(如果有的话),如果只是想解析网页正文,只需要解析body即可,body是展示到浏览器给用户能看到的内容。
解析网页:
网页下载完成之后,需要把HTML流也就是HTML字符串转换成DOM对象,然后解析(这个过程和浏览器解析网页是一样的),在解析过程中我们可以查找DOM文档中是否有我们需要的信息,访问的方式和jQuery查找,定位网页元素一样(按照元素名,按照id,按照class,具体请参考jQuery);解析网页通常采用插件或者库进行,cheerio就是一个很好的解析库。内部实现采用jQuery
这里用nodejs+request+cheerio来演示 var request = require('request'); var cheerio = require('cheerio'); var username = "yangqiang" var _password = "********" var login_page = "http://192.168.2.*:8080/rdms/authorize.do?method=login" var search_bug_page = "http://192.168.2.*:8080/rdms/common/search/searchAction!search.action" var search_bug_result_page = "http://192.168.2.*:8080/rdms/qm/bug/bugAction.do?action=getBugDetail&id=" var _id = '' //设置请求头,模拟浏览器 var headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36' } function start(_bug, callback) { //console.log(" start login... ") //设置访问参数,并封装成一个json对象,里面包含URL,form表单,访问方式,请求头 //更多的参数设置参考http://www.jb51.net/article/58465.htm 。补充options 还可以设置代理(目前还不知道代理有什么用)proxy: Proxy.GetProxy(), var login_options = { url: login_page, form: { "username": username, "password": _password }, method: 'POST', headers: headers, //sendImmediately: false //默认为真,发送一个基本的认证header。设为false之后,收到401会重试(服务器的401响应必须包含WWW-Authenticate指定认证方法)。 }; //设置cookie 默认情况cookie禁止,这里使用全局cookie //request 原文参考https://github.com/request/request 中文参考http://www.open-open.com/lib/view/open1435301679966.html var _request = request.defaults({jar: true}); _request( login_options , function (error, response, body) { if (error) { console.log("longin error,please check user &password"); return console.error(error); } else { search_options = { url: search_bug_page, form: { "queryString": _bug //这里的queryString必须和要访问的网页form表单参数对应 }, method: 'POST', headers: headers } _request(search_options, function (error, response, body) { if (error) { console.log('get id query error'); return error; } //解析搜索bug返回的页面,这里只解析body //cheerio 原文参考https://www.npmjs.com/package/cheerio 中文参考https://cnodejs.org/topic/5203a71844e76d216a727d2e var $ = cheerio.load(body); $('a').each(function () { var $table_node = $(this); var href = $table_node.attr('onclick'); var id = href.trim().substring(66, href.length - 13); _id = id; }); var url = search_bug_result_page + _id + "&popup=true".trim(); callback(url.toString()) }); } }); } function get_data(_bug, callback) { var login_options = { url: login_page, form: { "username": username, "password": _password }, method: 'POST', headers: headers, //sendImmediately: false //默认为真,发送一个基本的认证header。设为false之后,收到401会重试(服务器的401响应必须包含WWW-Authenticate指定认证方法)。 }; //设置cookie 默认情况cookie禁止 var _request = request.defaults({jar: true}); _request( login_options , function (error, response, body) { if (error) { console.log("longin error,please check user &password"); return console.error(error); } else { search_options = { url: search_bug_page, form: { "queryString": _bug }, method: 'POST', headers: headers } _request(search_options, function (error, response, body) { if (error) { console.log('get id query error'); return error; } //解析搜索bug返回的页面 var $ = cheerio.load(body); $('a').each(function () { var $table_node = $(this); var href = $table_node.attr('onclick'); var id = href.trim().substring(66, href.length - 13); _id = id; }); var url = search_bug_result_page + _id + "&popup=true".trim(); //callback(url.toString()) var search_bug_result_options = { url: url, method: 'GET', headers: headers, } _request( search_bug_result_options, function (_error,_response,_body) { if (_error) { console.log("get_ bug content error"); return console.error(_error); } var results_data={}; var $ = cheerio.load(_body); var td = $('td'); td.each(function (td) { var node_td = $(this); if (node_td.text()=='处理人') { var current_handler=node_td.next().find('a').text().trim(); results_data.current_handler=current_handler; callback(results_data); } }); }); }); } }); } exports.start = start; exports.get_data = get_data;
Python代码示例 Python代码使用了很多库,需要安装 # coding:utf8 import ConfigParser import cookielib import json import re import string import urllib import urllib2 import time import bs4 class HtmlDownloader(object): login_page = "http://192.168.2.92:8080/rdms/authorize.do?method=login" search_bug_page="http://192.168.2.92:8080/rdms/common/search/searchAction!search.action" search_bug_result_page="http://192.168.2.92:8080/rdms/qm/bug/bugAction.do?action=getBugDetail&id=" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36"} cookie = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) project='' version='' title='' reporter='' handler='' causes='' exchange_record='' def download(self,url): if url is None: return None response=urllib2.urlopen(url) #if response.getcode()!=200: #return None return response.read() def login(self, username, password): print '[**mainsoft**] Login' try: data = urllib.urlencode({ "username": username, "password": password, "productCode": 'Analysis'}) urllib2.install_opener(self.opener) req = urllib2.Request(self.login_page, data, self.headers) response = self.opener.open(req) page = response.read().decode("utf-8").encode("gbk") return page except Exception, e: print str(e) def search_bug(self,bug_num): print "输入的BUG号:"+bug_num try: bug_query=urllib.urlencode({ "queryString":bug_num }) urllib2.install_opener(self.opener) req = urllib2.Request(self.search_bug_page, bug_query, self.headers) response=self.opener.open(req) bug_result_page=response.read() print "搜索BUG号返回结果页面:"+bug_result_page except Exception, e: print str(e) print "开始解析BUG搜索结果HTML" soup = bs4.BeautifulSoup(bug_result_page, 'html.parser',from_encoding="gbk") link_a_node=soup.find('a',href='#') bug_id=link_a_node['onclick'] print link_a_node.get_text() _id=bug_id[66:-13] print _id #search_result_node=[] search_result_node=soup.find_all('span',class_='highlight') #print search_result_node #print search_result_node[1] #print search_result_node[2:-1] url=self.search_bug_result_page+_id+"&popup=true" print url request = urllib2.Request(url,'',self.headers) response = self.opener.open(request) bug_detailed_html=response.read() print "根据id获取BUG详情页面....." #print bug_detailed_html print "开始解析BUG详情页面....." soup_detailed=bs4.BeautifulSoup(bug_detailed_html,'html.parser',from_encoding="gbk") bug_detailed_node=soup_detailed.find('table',align='center') print "BUG详情页面解析结果如下:" #print bug_detailed_node flag=0 for tr_line in bug_detailed_node: #print tr_line for td_line in tr_line: flag+=1 #print flag #print td_line if flag==5: self.project=td_line if flag==15: self.title=td_line if flag==95: self.reporter=td_line if flag==99: self.handler=td_line if flag==152: self.causes=td_line print "项目名称:\n" print self.project print "标题:\n" print self.title print "报告人:\n" print self.reporter print "处理人:\n" print self.handler print "原因分析:\n" print self.causes print "交流记录:\n" print self.exchange_record if __name__ == '__main__': bug='B160826-334' username='yangqiang' password='07030501310' html_down=HtmlDownloader() html_down.login(username,password) html_down.search_bug(bug)
定向爬虫
主要是针对某个特定网站/服务器进行数据采集示例如上面分布式爬虫
分布式爬虫主要是在网页里面有很多链接URL,需要分别对这些URL进行访问。由此形成一个爬虫树,分布式爬虫比定向爬虫要复杂。分布式爬虫需要有
网页调度器
网页调度器主要负责协调其他管理器工作,处于整个爬虫中心位置
# coding:utf8 #爬虫总调度程序 from baike_spider import html_downloader from baike_spider import html_outputer from baike_spider import html_parser from baike_spider import url_manager class SpiderMain(object): def __init__(self): self.urls=url_manager.UrlManager() self.downloader=html_downloader.HtmlDownloader() self.parser=html_parser.HtmlParser() self.outputer=html_outputer.HtmlOutputer() #调度器核心算法 def craw(self,root_url): count=1 self.urls.add_new_url(root_url) while self.urls.has_new_url():#继续循环的条件是URL管理器有新的URL try:#异常捕获 new_url=self.urls.get_new_url()#从URL管理器获取 print 'craw %d : %s'%(count,new_url) html_cont=self.downloader.download(new_url)#调用下载器去下载网页 new_urls,new_data=self.parser.parse(new_url,html_cont)#调用解析器解析网页 #上面的解析器返回两个数据,新的URL管理器,解析的结果 self.urls.add_new_urls(new_urls) self.outputer.collect_data(new_data) if count==100: break count+=1 except: print 'craw failed' self.outputer.output_html() #处理main函数 if __name__=="__main__": root_url="http://baike.baidu.com/view/21087.htm" #入口URL obj_spider=SpiderMain() obj_spider.craw(root_url)
URL管理器
主要负责管理所有已经爬取过的URL和等待爬取的URL,URL的收集,URL的流出
# coding:utf8 class UrlManager(object): def __init__(self): #在构造方法中初始化两个set集合 self.new_urls=set() self.old_urls=set() #new_urls表示待爬的URL集合 #old_urls表示已经爬过的URL集合 def add_new_url(self, root_url): if root_url is None: return if root_url not in self.new_urls and root_url not in self.old_urls: #检查入口URL,如果不在新URL管理器中,也不在旧的URL管理器中,这说明该URL是新的 self.new_urls.add(root_url) #add_new_urls将一个待爬URL集合添加进来,内部掉用add_new_url() #为什么先调用add_new_url()后面调用add_new_urls() #因为第一次执行程序时,只提供了一个URL入口,爬第一个一个网页,该网页里面有很多符合特定格式的URL,然后分别爬 #整个执行的过程 类似一个URL树结构,越到后面 需要爬的URL越多 def add_new_urls(self, new_urls): if new_urls is None or len(new_urls)==0:#如果待添加的URL集合为空,或者长度为0(注意这两者不一样) return for new_url in new_urls: self.add_new_url(new_url) def has_new_url(self): return len(self.new_urls) def get_new_url(self): new_url=self.new_urls.pop()#pop从集合中取出一个URL,并移除 self.old_urls.add(new_url)#将取出的URL添加到旧URL集合中 return new_url
网页解析器
主要对下载后的HTML流解析出数据
# coding:utf8 from bs4 import BeautifulSoup //注意这里的意思是说:从bs4模块中导入BeautifulSoup 这个类 import re import urlparse class HtmlParser(object): #网页解析器里面只有一个方法 def parse(self, page_url, html_cont): if page_url is None or html_cont is None: return soup=BeautifulSoup(html_cont,'html.parser',from_encoding='utf-8') #从一个网页爬出的URL集合 new_urls=self._get_new_urls(page_url,soup) #解析当前页面的元素 new_data=self._get_new_data(page_url,soup) return new_urls,new_data #python可以返回两个值 def _get_new_urls(self, page_url, soup): new_urls=set() # /view/456.htm 使用正则表达式进行模糊匹配 /view/xxx.htm xxx是数字格式 #查找所有a节点,也就是获取网页中所有的链接节点 links=soup.find_all('a',href=re.compile(r"/view/\d+\.htm")) for link in links: new_url=link['href'] #使用urlparse模块提供的URL拼接函数 new_full_url=urlparse.urljoin(page_url,new_url) new_urls.add(new_full_url) return new_urls def _get_new_data(self,page_url,soup): res_data={} #创建一个字典 存放 返回的数据集合 #URL 将URL添加到结果字典里,key是URL 作用是方便后续查看 res_data['url']=page_url #标题处理 #<dd class="lemmaWgt-lemmaTitle-title"><h1>Python</h1> 来源于百度百科Python词条源码 title_node=soup.find('dd',class_="lemmaWgt-lemmaTitle-title").find("h1") res_data['title']=title_node.get_text() #简介div处理 #<div class="lemma-summary" label-module="lemmaSummary"> Python简介div块区 summary_node=soup.find('div',class_="lemma-summary") res_data['summary']=summary_node.get_text() return res_data
网页下载器
下载器主要是根据URL地址下载对应HTML流,下载器还要对目标网页的访问,提交from表单,接收数据
下面示例代码有部分是URL下载器的功能,另外部分是别的管理器的功能(没有来得及分离)
# coding:utf8 import ConfigParser import cookielib import json import re import string import urllib import urllib2 import time import bs4 class HtmlDownloader(object): login_page = "http://192.168.2.92:8080/rdms/authorize.do?method=login" search_bug_page="http://192.168.2.92:8080/rdms/common/search/searchAction!search.action" search_bug_result_page="http://192.168.2.92:8080/rdms/qm/bug/bugAction.do?action=getBugDetail&id=" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36"} cookie = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie)) project='' version='' title='' reporter='' handler='' causes='' exchange_record='' def download(self,url): if url is None: return None response=urllib2.urlopen(url) #if response.getcode()!=200: #return None return response.read() def login(self, username, password): print '[**mainsoft**] Login' try: data = urllib.urlencode({ "username": username, "password": password, "productCode": 'Analysis'}) urllib2.install_opener(self.opener) req = urllib2.Request(self.login_page, data, self.headers) response = self.opener.open(req) page = response.read().decode("utf-8").encode("gbk") return page except Exception, e: print str(e) def search_bug(self,bug_num): print "输入的BUG号:"+bug_num try: bug_query=urllib.urlencode({ "queryString":bug_num }) urllib2.install_opener(self.opener) req = urllib2.Request(self.search_bug_page, bug_query, self.headers) response=self.opener.open(req) bug_result_page=response.read() print "搜索BUG号返回结果页面:"+bug_result_page except Exception, e: print str(e) print "开始解析BUG搜索结果HTML" soup = bs4.BeautifulSoup(bug_result_page, 'html.parser',from_encoding="gbk") link_a_node=soup.find('a',href='#') bug_id=link_a_node['onclick'] print link_a_node.get_text() _id=bug_id[66:-13] print _id #search_result_node=[] search_result_node=soup.find_all('span',class_='highlight') #print search_result_node #print search_result_node[1] #print search_result_node[2:-1] url=self.search_bug_result_page+_id+"&popup=true" print url request = urllib2.Request(url,'',self.headers) response = self.opener.open(request) bug_detailed_html=response.read() print "根据id获取BUG详情页面....." #print bug_detailed_html print "开始解析BUG详情页面....." soup_detailed=bs4.BeautifulSoup(bug_detailed_html,'html.parser',from_encoding="gbk") bug_detailed_node=soup_detailed.find('table',align='center') print "BUG详情页面解析结果如下:" #print bug_detailed_node flag=0 for tr_line in bug_detailed_node: #print tr_line for td_line in tr_line: flag+=1 #print flag #print td_line if flag==5: self.project=td_line if flag==15: self.title=td_line if flag==95: self.reporter=td_line if flag==99: self.handler=td_line if flag==152: self.causes=td_line print "项目名称:\n" print self.project print "标题:\n" print self.title print "报告人:\n" print self.reporter print "处理人:\n" print self.handler print "原因分析:\n" print self.causes print "交流记录:\n" print self.exchange_record if __name__ == '__main__': bug='B160826-334' username='yangqiang' password='07030501310' html_down=HtmlDownloader() html_down.login(username,password) html_down.search_bug(bug)
相关文章推荐
- node.js使用cheerio制作网络爬虫
- Node.js学习之网络爬虫(使用cheerio抓取网页数据)
- Node.js:request+cheerio爬虫爬取免费代理
- node.js 学习笔记003 :使用superagent和cheerio实现简单网页爬虫
- node.js实现简单的网络爬虫程序
- 详解Node.js API系列 Http模块(2) CNodejs爬虫实现
- 基于Node.js实现一个小小的爬虫
- Node.js与网络:Node.js对TCP、UDP、Socket、HTTP等协议的实现和支持
- node.js WebService异常处理(domain)以及利用domain实现request生命周期的全局变量
- 用node.js实现http小爬虫
- 利用Nodejs & Cheerio & Request抓取Lofter美女图片
- node.js基础模块http、网页分析工具cherrio实现爬虫
- 使用Node.js配合Nginx实现高负载网络
- Node.js 实现简单小说爬虫
- 使用node.js的http模块实现爬虫小工具
- node.js实现博客小爬虫的实例代码
- Node.js之网络小爬虫
- node.js基础模块http、网页分析工具cherrio实现爬虫
- NodeJs+http+fs+request+cheerio 采集,保存数据,并在网页上展示(构建web服务器)
- 使用 NodeJS + Express 從 GET/POST Request 取值 -摘自网络