nodejs eggjs框架 爬虫 readhub.me
最近做了一款 高仿ReadHub小程序 微信小程序 canvas 自动适配 自动换行,保存图片分享到朋友圈 https://gitee.com/richard1015/News
具体代码已被开源,后续我会继续更新,欢迎指正
https://github.com/richard1015/egg-example
https://gitee.com/richard1015/egg-example
你可能会像我一样,平常对科技圈发生的热点新闻很感兴趣。每天利用刚打开电脑的时候,又或者是工作间隙,浏览几个更新及时的科技资讯网站。但是,科技圈每天发生的热点新闻就那么几件。看来看去,新闻的重复度高,硬广软文还看了不少。不仅浪费时间,还抓不住重点。
ReadHub 通过爬虫各种科技新闻 大数据过滤筛选 (个人猜想,大概是这一个意思),所以自己写个爬虫把数据爬到自己mysql数据库中
代码思路:
通过网上各种调用 动态网站数据 爬虫分为两种解决方案
1.模拟浏览器请求 使用 相应框架 比如:Selenium
、PhantomJs
。
2.精准分析页面,找到对应请求接口,直接获取api数据。
- 优点:性能高,使用方便。我们直接获取原数据接口(换句话说就是直接拿取网页这一块动态数据的API接口),肯定会使用方便,并且改变的可能性也比较小。
- 缺点:缺点也是明显的,如何获取接口API?有些网站可能会考虑到数据的安全性,做各种限制、混淆等。这就需要看开发者个人的基本功了,进行各种分析了。
- 我个人在爬取ReadHub时,发现《热门话题》 列表是 无混淆,所以找到请求规律,爬取成功 ,剩下 开发者资讯、科技动态、区块链快讯、招聘行情 请求index被混淆,所以暂无成功。
我在本次采用第二种解决方案 chrome浏览器分析
1.使用chrome 调试工具 Mac 按 alt + cmd+ i Windows 按 F12 或者 右键检查 或 审查元素 找到Network 选中 xhr模块
可通过图片中看到 每次滚动加载数据时 都会有api请求数据, 我们发现 下次触发滚动加载时,的lastCursor的值 为 上次请求的 数组中最后一个对象中的order值
所以我们发现 只是的请求 url地址为 https://api.readhub.me/topic?lastCursor=53058&pageSize=20 中 的lastCursor 动态设置,即可完成抓取数据
那么接下来 我们需要 建立mysql数据库
CREATE DATABASE `news` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */; CREATE TABLE `news` (`id` varchar(11) COLLATE utf8_bin NOT NULL,`order` double NOT NULL,`title` varchar(200) COLLATE utf8_bin NOT NULL,`jsonstr` json DEFAULT NULL,`createdAt` varchar(255) COLLATE utf8_bin DEFAULT NULL,`updatedAt` varchar(255) COLLATE utf8_bin DEFAULT NULL,`insertTime` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
然后就是编写 nodejs 中代码逻辑 我在下面的抓取冲采用 eggjs 框架中的 egg-mysql 进行连接数据库 https://eggjs.org/zh-cn/tutorials/mysql.html#egg-mysql
使用定时任务来 执行爬取数据
1.news.service 中代码实现
// app/service/news.js const Service = require('egg').Service;class NewsService extends Service {async list(pageIndex = '', pageSize = '20') {try {// read configconst { serverUrl } = this.config.readhub;// 热门话题const topic = `${serverUrl}topic?lastCursor=${pageIndex}&pageSize=${pageSize}`;// use build-in http client to GET hacker-news apiconst result = await this.ctx.curl(topic,{dataType: 'json',});if (result.status === 200) {return result.data;}return [];} catch (error) {this.logger.error(error);return [];}}async saveDB(list) {try {const newsClient = this.app.mysql.get("news");list.data.forEach(item => {// 插入newsClient.insert('news', {id: item.id,order: item.order,title: item.title,jsonstr: JSON.stringify(item),createdAt: new Date(item.createdAt).getTime(),updatedAt: new Date(item.updatedAt).getTime(),}).then(result => {// 判断更新成功const updateSuccess = result.affectedRows === 1;this.logger.info(item.id + " > " + updateSuccess);}).catch(error => {//入库失败错误机制触发this.app.cache.errorNum += 1;})});} catch (error) {this.logger.error(error);}} }module.exports = NewsService;
2.定时任务代码实现
update_cache.js
const Subscription = require('egg').Subscription;class UpdateCache extends Subscription {// 通过 schedule 属性来设置定时任务的执行间隔等配置 static get schedule() {return {interval: '5s', // 隔单位 m 分 、 s 秒、 ms 毫秒 type: 'all', // 指定所有的 worker 都需要执行immediate: true, //配置了该参数为 true 时,这个定时任务会在应用启动并 ready 后立刻执行一次这个定时任务。disable: false//配置该参数为 true 时,这个定时任务不会被启动。 };}// subscribe 是真正定时任务执行时被运行的函数 async subscribe() {let ctx = this.ctx;ctx.logger.info('update cache errorNum = ' + ctx.app.cache.errorNum);// errorNum 当错误数量 > 50时 停止抓取数据if (ctx.app.cache.errorNum > 50) {ctx.logger.info('errorNum > 50 stop ');return;}ctx.logger.info('update cache begin ! currentLastCursor = ' + ctx.app.cache.lastCursor);const pageIndex = ctx.app.cache.lastCursor || '';const pageSize = '20';const newsList = await ctx.service.news.list(pageIndex == 1 ? '' : pageIndex, pageSize);if (newsList.data.length == 0) {//没有数据时错误机制触发this.app.cache.errorNum += 1;ctx.logger.info('no data stop ! currentLastCursor = ' + ctx.app.cache.lastCursor);} else {ctx.service.news.saveDB(newsList)ctx.app.cache.lastCursor = newsList.data[newsList.data.length - 1].order;ctx.logger.info('update cache end ! currentLastCursor set = ' + ctx.app.cache.lastCursor);}} }module.exports = UpdateCache;
update_cache_init.js
const Subscription = require('egg').Subscription;class UpdateCacheInit extends Subscription {// 通过 schedule 属性来设置定时任务的执行间隔等配置 static get schedule() {return {interval: 60 * 24 + 'm', // 隔单位 m 分 、 s 秒、 ms 毫秒 type: 'all', // 指定所有的 worker 都需要执行immediate: true, //配置了该参数为 true 时,这个定时任务会在应用启动并 ready 后立刻执行一次这个定时任务。disable: false//配置该参数为 true 时,这个定时任务不会被启动。 };}// subscribe 是真正定时任务执行时被运行的函数 async subscribe() {let ctx = this.ctx;ctx.logger.info('update cache init');if (ctx.app.cache.errorNum > 50) {//初始化内置缓存ctx.app.cache = {lastCursor: '',errorNum: 0 //错误数量 };}} }module.exports = UpdateCacheInit;
项目运行图
具体代码已被开源,后续我会继续更新,欢迎指正
https://github.com/richard1015/egg-example
https://gitee.com/richard1015/egg-example
转载于:https://www.cnblogs.com/richard1015/p/9203597.html
nodejs eggjs框架 爬虫 readhub.me相关推荐
- nodejs和python爬虫 哪个好_nodejs有哪些爬虫框架?
nodejs有哪些爬虫框架?下面本篇文章给大家介绍几款nodejs爬虫框架.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. node-spider 基于nodejs的通用爬虫框架,得 ...
- Crawler之Scrapy:Python实现scrapy框架爬虫两个网址下载网页内容信息
Crawler之Scrapy:Python实现scrapy框架爬虫两个网址下载网页内容信息 目录 输出结果 实现代码 输出结果 后期更新-- 实现代码 import scrapy class Dmoz ...
- python多线程爬虫框架_普通爬虫vs多线程爬虫vs框架爬虫,Python爬对比
前言 本文的文字及图片过滤网络,可以学习,交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 基本开发环境 Python 3.6 皮查姆 目标网页分析 网站就选择发表情这个网站吧 网站是静 ...
- Nodejs ORM框架Sequelize
Nodejs ORM框架Sequelize (模型,关联表,事务,循环,及常见问题) 建立连接 const Sequelize = require('sequelize'); const sequel ...
- Python基础知识回顾及scrapy框架爬虫基础
1.函数 函数参数:必须 默认 关键 可变 函数种类:外部 内部 匿名 lambda 装饰函数:@语法糖 函数总是要返回的 ,若没有return,None总是被返回 2.面向对象: 对象:已存在, ...
- nodeJS实现简易爬虫
nodeJS实现简易爬虫 需求:使用nodeJS爬取昵图网某个分类下的图片并存入本地 运用nodeJS自带系统模块http.fs 示例代码: var http =require('http'); va ...
- eggjs框架学习心得
前言: eggjs作为阿里开源的企业级 Node.js 框架,其官网教程https://eggjs.org/zh-cn/tutorials/index.html介绍的很详细,可以帮助初学开发者快速搭建 ...
- 一次使用NodeJS实现网页爬虫记 - huanping - 博客园
一次使用NodeJS实现网页爬虫记 - huan&ping - 博客园
- nodejs MVC框架:Adonisjs框架入门-001概述
Adonisjs是一个MVC结构的Nodejs后端框架,可以用来创建WEB应用.API服务,包含处理 HTTP adonisjs是一个后端mvc框架,基于nodejs,使用typescript语言编写 ...
- nodejs实现新闻爬虫
nodejs实现新闻爬虫 作为费德勒的铁杆粉丝,每天早上都会在新浪体育里面的网球频道浏览费德勒新闻.由于只关注费德勒的新闻,所以每次都要在网页中大量的新闻中筛选相关信息,感觉效率好低,所以用node写 ...
最新文章
- Logback 配置文件这样优化,TPS提高 10 倍
- 千亿级携程酒店AWS实践
- SQL点滴5—产生时间demention,主要是时间转换
- python2.7爬虫实例-Python2.7爬虫-爬取简书文章-入门
- 合肥市电力大数据应用工程技术研究中心成立
- aws搭建java项目_AWS下S3之java开发
- 六、乘胜追击,将剩下的Git知识点搞定
- Django学习笔记之——Forms
- JQuery中样式标签的处理
- 计组之中央处理器:7、指令流水线基本概念性能指标、影响因素
- oracle 自定义函数 返回一个表类型
- javascript 的预解释机制
- 光纤中传导模式matlab仿真,光纤通信实验指导书
- vue 浏览器页面刷新
- MySQL什么情况会导致索引失效?
- Python网络爬虫(二):小说下载器
- 三十六计第三计 借刀杀人
- HBase批量写入数据
- H3C服务器带外默认账号和密码,H3C产品的默认密码是多少?
- 知识付费直播间即时通讯
热门文章
- Android工具类篇 清理APP应用缓存
- 系统上电后 bootloader的执行流程
- 8255芯片控制发光二极管模拟步进电机汇编实验
- 记一次golang memory leak的解决过程
- 通过 ANE(Adobe Native Extension) 启动Andriod服务 推送消息(三)
- 帝国 php 7.0 默认 后台用户名及认证码,帝国CMS忘记后台登陆用户名 密码 认证码 安全提问答案 数据库用户名及密码的解决方法 | 坐倚北风...
- 静态路由原理及配置(8)
- 行转列 和	链接查询
- 802.1Q封装的VLAN数据帧格式
- sogou/workflow入门(windows版)