爬虫抓取网页数据(WebSpiderWebSpider2021/05/23更新演示)
优采云 发布时间: 2022-01-14 22:03爬虫抓取网页数据(WebSpiderWebSpider2021/05/23更新演示)
网络蜘蛛
2021/05/23 更新
演示爬虫目前部署在heroku上,请酌情测试使用
爬虫模块可以单独调用。有关调用示例,请参见 crawl.test.js。或者 HttpProxy 项目。数据库使用mlab提供的免费远程数据库,速度较慢,请耐心等待。
操作平台:Linux、MacOS ... Windows 未测试
基于NodeJS的在线爬虫系统。支持提供在线数据API。
1、当你想给你的网站添加一个小新闻模块时,可以使用WebSpider爬虫爬取指定网站的数据,然后请求后面的数据接口-end 或 front-end,然后将获取的数据构造到您的网页中。
2、当你想做一个聚合网站或者聚合app时,可以使用WebSpider爬取各大站点的数据,然后调用API将数据构造成自己的APP。
...
于是,WebSpider 诞生了。
内容目录功能
*简单方便。只要对网页有简单的了解,就可以使用WebSpider在线爬虫系统。简单配置后,即可进行数据采集和预览。*强大的。支持抓取预览、自定义输出、生成API、API管理、查看分享、登录注册等功能。*快速响应。爬取结果保存在数据库中,根据用户配置更新响应数据。
原则
为了满足爬虫的通用性(即通过页面配置可以爬取多种网站),原理比较简单,就是下载HTML页面进行分析。分析库使用使用 JQuery 核心库的cheerio。可以通过标签选择器和属性选择器获取数据。这样做的限制是无法捕获 Ajax 异步获取的数据。但是,考虑到当今互联网上的大部分Web应用程序还没有过渡到现代应用程序架构,爬虫仍然具有很大的实用价值。
本地测试
1、安装 Nodejs、MongoDB、Git、Redis
2、确保程序有写文件权限
3、运行代码
git clone https://github.com/LuckyHH/WebSpider.git
cd WebSpider
npm install
4、启动 Redis、MongoDB
5、 修改配置文件(src/config)——自定义启动端口、数据库名、Redis等。
6、运行 npm start 启动项目
7. 开放:3000
项目目录
|---docs 模块说明文档
| |---env.md 环境说明
| |---issues.md 相关问题说明
| |---proxy.md 代理说明
| |---router.md 接口文档
| |---history.md 更新日志
| |---panel.md 前端面板说明
|---log 日志文件(按天新建日志文件)
| |---error 错误日志
| |---running 运行日志
|---src 源代码
| |---config 配置
| |---index.js 配置项出口控制
| |---dev 开发模式配置项
| |---index.js 开发模式配置项出口
| |---crawl.js 爬虫相关配置项
| |---db.js 数据库配置项
| |---proxy.js 代理配置项
| |---session.js 会话配置项
| |---redis.js redis配置项
| |---api.js API请求频率限制配置
| |---prod 生产模式配置项
| |---test 测试模式配置项
| |---crawl 爬虫
| |---index.js 爬虫主控文件
| |---mapReqUrl.js 并发请求
| |---fetchResult 爬虫核心
| |---proxy 代理
| |---index.js 可用代理检测模块
| |---data 数据目录
| |---config.json 爬虫配置文件
| |---model 数据模型
| |---index.js 模型出口
| |---user.js 用户模型
| |---crawl.js 爬虫模型
| |---statistics.js API统计模型
| |---router Web应用路由
| |---utils 路由部分需要的辅助函数
| |---verification.js 用户输入验证
| |---index.js 路由出口
| |---user.js 用户路由
| |---crawl.js 爬虫接口路由
| |---proxy.js 代理检测路由
| |---utils 辅助函数
| |---index.js 辅助函数出口
| |---debug.js 调试模块
| |---filter.js 用户输入过滤模块
| |---sha.js 加密模块
| |---splice.js 多维数组转化为一维数组
| |---time.js 格式化时间
| |---uuid.js 获取ID模块
| |---isNaN.js 判断参数是否能转化为数字
| |---test 测试文件
| |---crawl.test.js 爬虫测试
| |---router.test.js 路由测试
| |---utils.test.js 功能函数测试
| |---index.js 应用出口
|---static 静态资源文件夹
|---app.js 应用入口
核心代码
{superagent.get('').end(function (err, _res) {if (err) {reject(err)}const $=cheerio.load(_res.text)$('.topic_title').each(function (idx, element) {var $element = $(element)items.push({title: $element.attr('title'),url: $element.attr('href'),})})resolve(items )})}) } 否则 {next() }})app.listen(3000)">
const Koa = require('koa')
const superagent = require('superagent')
const cheerio = require('cheerio')
const app = new Koa()
app.use(async function (ctx, next) {
if (ctx.request.path == '/' && ctx.request.method == 'GET') {
ctx.body = await new Promise((resolve, reject) => {
superagent.get('https://cnodejs.org/').end(function (err, _res) {
if (err) {
reject(err)
}
const $ = cheerio.load(_res.text)
$('.topic_title').each(function (idx, element) {
var $element = $(element)
items.push({
title: $element.attr('title'),
url: $element.attr('href'),
})
})
resolve(items)
})
})
} else {
next()
}
})
app.listen(3000)
使用 1. 抓取深度
爬取深度是指从初始 URL 到数据所在 URL 的层数。最大支持爬行深度为3,推荐最大爬行深度为2
2.网页代码
登陆页面的编码格式,默认为UTF-8
3.抓取模式
普通模式和分页模式
4.页面范围
在分页模式下,要获取的开始和结束页码
5.目标网址
目标URL是要爬取的目标网站的URL。
普通模式下,只需要填写要爬取的URL即可。
在分页模式下:
填写网址时,将网址中的页码改为*。
例如:
CNode 的分页 URL
改成
*
6.选择器
选择器用于指示数据的位置,可以通过“输出格式”获取目标数据。填写需要用户具备基本的前端知识。
这里,为了描述方便,将标签选择器分为两种,一种是标签选择器,一种是数据标签选择器。(当然如果你要的数据在一个标签中,那么a标签选择器就是数据标签选择器)
当爬取深度为1时,可以在一级选择器中填充数据选择器。当爬取深度为2时,一级选择器填充到达二级页面的a标签选择器,二级选择器填充数据标签选择器。当爬取深度为3时,一级选择器填充到达二级页面的a标签选择器,二级选择器填充到达三级页面的a标签选择器,第三级选择器填充到达二级页面的a标签选择器-level 选择器填充数据选择器就是这样。
填写示例:
深度 2
一级选择器:$(".topic_title a")
辅助选择器:$(".topic .content")
$(".topic_title a") 指目标页面中类名为topic_title的所有元素中的a元素
$(".topic .content") 指目标页面中类名topic的元素下类名content的后代元素
填写二级选择器,表示目标数据在当前页面的下一层(即配置页面的“目标URL”填写的URL),那么一级选择器需要指示到达页面下一层的标签选择器。次要选择器用下一页的数据标签选择器填充
更多选择器填充规则,参考cheerio。
7.输出格式
输出格式是指输出哪些结果。
在标签选择器指出数据的位置后,需要进一步使用标签选择器和属性选择器来获取数据。
这需要以 JSON 格式编写。参考写作如下:
{
"name": "$element.find('.c-9 .ml-20 a').text()",
"age": "$element.children('.c-9').next().text()"
}
关键部分可以任意指定,值部分需要一定的规则。
$element 指的是“选择器”中填写的数据标签选择器。(结合"selector"给出的例子,$element指的是$(".topic .content"))
带键名的值是指被“选择器”过滤的元素下的类名为c-9的元素下的类名ml-20下的a元素中的文本
键为age的值是指“选择器”过滤的元素下类名为c-9的元素旁边的元素的文本内容
值得注意的是,当你只需要一种类型的数据时,可以在“Selector”中填写标签选择器时直接将标签定位到目标元素,在“Output Format”中填写属性选择器那就是灿。
但是很多时候我们需要的数据不止一种,所以在填写“选择器”部分的时候,需要填写的数据标签选择器应该把所有需要的数据都包裹起来,所以你甚至可以填写$("body")比如数据选择器。在“输出格式”的值部分,需要填写一些选择器来表示数据的详细位置,最后使用属性选择器来获取数据。
同样结合上面给出的例子,
如果我想获取“名称”值之类的数据,
那么“选择器”可以这样写
一级选择器:$(".topic_title a")
次要选择器:$(".topic .content .c-9 .ml-20 a")
'输出格式'可以这样写
{
"name": "$element.text()"
}
或者
“选择器”:
一级选择器:$(".topic_title a")
次要选择器:$(".topic").find('.content .c-9 .ml-20 a')
'输出格式':
{
"name": "$element.text()"
}
或者
“选择器”:
一级选择器:$(".topic_title a")
次要选择器:$(".topic")
'输出格式':
{
"name": "$element.find('.content .c-9 .ml-20 a').text()"
}
或者
“选择器”:
一级选择器:$(".topic_title a")
辅助选择器:$("body")
'输出格式':
{
"name": "$element.find('.topic .content .c-9 .ml-20 a').text()"
}
常用的属性选择器有 3 种:text()、html() 和 attr()。
text() 选择目标元素中的文本内容
html() 选择目标元素的 HTML 代码
attr() 在目标元素标签中选择一个属性值。参数需要填写,例如$element.attr('url')是指获取目标元素标签中的url属性值
8.代理模式
即是否使用HTTP代理来获取数据。有3种模式,内置代理,无代理和自定义代理。
内置代理使用 thorn 代理或 FreeProxyList 提供的代理地址发出请求。
自定义代理模式需要用户自己填写可用的代理。
无代理使用部署服务器 IP 进行请求
笔记:
(1)如果自定义代理地址与普通IP地址不匹配,系统默认使用无代理模式。
(2)代理的质量参差不齐,所以响应可能会失败。所以当响应失败时,请重新提交。
9.结果预览
返回结果
state 表示抓取状态,值为真或假
时间值是数据的更新时间。
数据值为抓取结果,格式为数组。
味精备注
10.生成API
数据接口仅在用户登录时生成。点击“生成API”按钮,后台会记录当前爬虫配置,当请求API时,会在适当的情况下调用爬虫进行响应。
11.更新间隔(后台管理配置项)
如果两次API调用的间隔在“更新间隔”周期内,则直接调用数据库数据响应,否则调用爬虫响应。所有初始生成的 API 的默认更新间隔为 0,即不更新数据,直接从数据库中读取数据。
请注意,此项已根据您的需要正确配置。如果“Update Interval”配置值较大或为0,则API平均响应速度快,但时效性较差;配置值小,API平均响应速度慢,但时效性好。
注意:点击“Generate API”后,在API“Edit”操作中调整“Update Interval”之前,一定要到“Admin Panel”点击一次生成的API链接,完成抓取数据的初始化. 否则,调用 API 返回的结果始终为空。补救措施:当发现返回结果为空时,可以再次进行API“编辑”操作,将“更新间隔”调整为0,点击API链接初始化一次数据,调整“更新间隔”初始化成功后。
12.标签(后台管理配置项)
为了方便用户查找某类API,添加标签功能
13.开启权限(后台管理配置项)
控制 API 是否共享。API权限开启后,可以在“API Store”面板中看到API信息
14.描述信息(后台管理配置项)
API 描述信息。
数据接口调用示例
Node.js 后端
{if (_res.data.state) {res.render('douban', { title: 'douban', content: _res.data.data })} else {res.send('Request failed')}})。catch((err) => {console.error(err)})})">
const express = require('express')
const axios = require('axios')
const router = express.Router()
router.get('/douban/movie', function (req, res, next) {
axios
.get('http://splider.docmobile.cn/interface?name=luckyhh&cid=1529046160624')
.then((_res) => {
if (_res.data.state) {
res.render('douban', { title: 'douban', content: _res.data.data })
} else {
res.send('请求失败')
}
})
.catch((err) => {
console.error(err)
})
})
注意:程序后台限制了API调用的频率。为方便起见,示例直接将 API 链接请求结果构造到模板中。实际调用时,请将请求结果保存到redis或数据库中,否则数据响应会失败。
示例配置参考
*WebSpider 参考配置
变更日志
WebSpider 更新日志
注意
https://spider.docmobile.cn/
对于预览地址,不建议在实际项目中使用。由此造成的损失由用户承担
TODO协议
麻省理工学院