当前位置: 首页 > news >正文

爬虫(没)入门:用 node-crawler 爬取 blog

起因

前几天想给一个项目加 eslint,记得自己曾经在博客里写过相关内容,所以来搜索。但是发现 csdn 的只能按标题,没办法搜正文,所以我没搜到自己想要的内容。

没办法只能自己又重新折腾了一通 eslint,很烦躁。迁怒于 CSDN(?),所以打算写一个爬虫,自己搜。


新建项目

只想做一个很简单的爬虫,爬取我自己的 blog,获取所有文章的标题、摘要、分类、标签、内容、发布更新时间。

一说到爬虫我就想到 python,但是懒得配置 python 的开发环境了,用 nodejs 随便搞一搞吧。简单查了一下,决定用 node-crawler

npm init -y
git init
创建 .gitignore
pnpm i crawler
创建 index.js


爬取文章内容页面

此页面可以获取标题、分类、标签、正文内容。
页面中也展示了发布和更新日期,但获取较麻烦,不在此页面爬取。

// index.js
const Crawler = require("crawler");const crawler = new Crawler();
crawler.direct({uri: `https://blog.csdn.net/tangran0526/article/details/125663417`,callback: (error, res) => {if (error) {console.error(error);} else {const $ = res.$;	// node-crawler 内置 cheerio 包。$ 是 cheerio 包提供的,用法和 jQuery 基本一致const title = $("#articleContentId").text();const content = $("#article_content").text();}},
});

获取标题、正文很简单,通过 id 直接锁定元素,然后用 text() 获取文本内容。但是分类和标签就要复杂一点了。

在这里插入图片描述

对应的 html 结构是:

在这里插入图片描述

分类和标签的 class 都是 tag-link,只能从 attr 区分。
注意:cheerio 的 map() 返回伪数组对象,需要调用 get() 获取真数组

const categories = $(`.tag-link[href^="https://blog.csdn.net/tangran0526"]`).map((_i, el) => $(el).text()).get();
const tags = $(`.tag-link[href^="https://so.csdn.net/so/search"]`).map((_i, el) => $(el).text()).get();

发布和更新日期就不在详情页面获取了。因为对有更新和无更新的文章,日期的展示形式不同,而且没有特殊的 class 能锁定元素,获取起来比较麻烦。


获取文章列表

找到文章列表页面,惊喜的发现有滚动加载,也许能找到获取列表的接口。打开浏览器控制台,Network 中看到疑似请求:

在这里插入图片描述

返回值为:

在这里插入图片描述

因为是 get 请求,所以可以直接把接口地址放到浏览器地址栏里访问。这样改参数看效果更直接。可以避免吭哧吭哧写代码试,最后发现参数无效或者哪有错的倒霉情况。

在地址栏里改参数发现 size 能正常工作。给它设大一点,就可以一次获取所有文章。
在这里插入图片描述

let url = "https://blog.csdn.net/community/home-api/v1/get-business-list";
const queryParams = {businessType: "blog",username: "tangran0526",page: 1,size: 1000, // 设大一点,一次取完所有文章
};
url +=   "?" +   Object.entries(queryParams).map(([key, value]) => key + "=" + value).join("&");
crawler.direct({uri: url,callback: (error, res) => {if (error) {console.error(error);} else {console.log(res);}},
});

这次返回值是 json,不是 html 了。不确定返回的 res 是什么结构,node-crawler 的官方文档中有写:

在这里插入图片描述
打断点查看:

在这里插入图片描述
res.body 是一段 html。内容是提示:当前访问人数过多,请完成安全认证后继续访问
应该是网站的防爬虫、防网络攻击的策略。google 了“爬虫、安全验证、验证码”等相关内容,找到了解决方法。

找到刚才用浏览器成功访问的请求,把它的 request header 全部赋给 crawler 里面的 header

crawler.direct({uri: url,headers: {   // 全部放这里},callback,
}

再次重试,res.body 是想要的结果了。现在是字符串, 再 JSON.parse 一下就可以了。

在这里插入图片描述

下面来精简 request headers。之前是把所有的 headers 都拿过来了,但应该不需要那么多。筛查哪些 headers 是必不可少的:

在这里插入图片描述

二分法排除,确认了只有 cookie 是必须的。

去浏览器中清除 cookie 后,在浏览器中重新访问接口,也弹出了这个安全验证页面。双重实锤 cookie 是关键!

在这里插入图片描述

cookies 内容也很多,根据名称猜测,二分法筛查找到了两个关键cookie:

在这里插入图片描述
只要有它们两个,就不会触发安全验证。

const cookie_yd_captcha_token =  "略";
const cookie_waf_captcha_marker =  "略";
crawler.direct({uri: url,headers: {Cookie: `yd_captcha_token=${cookie_yd_captcha_token}; waf_captcha_marker=${cookie_waf_captcha_marker}`,},callback
});

这两个 cookie 的值应该是有时效的。每次失效后都必须:打开浏览器——清除cookies后访问接口——遇到安全验证——通过后获取新的有效cookie。
所以这是一个人工爬虫——需要人力辅助的爬虫。。。。我知道这很烂,但没兴趣继续研究了,就这样吧。
如果想真正解决,应该使用 puppeteer 这类爬虫:内置 Headless Browser,可以模拟用户操作,也许能解决图形、滑块等验证。


将爬取结果输出到文件

先调用 list,再对每一篇文章获取详情。最后将结果输出到文件。

执行 node index.js,等待一会后,输出 result.json 文件:

在这里插入图片描述

完整代码如下:

// index.jsconst Crawler = require("crawler");
const { writeFile } = require("fs");
const { username, cookie_yd_captcha_token, cookie_waf_captcha_marker } = require("./configs.js");const crawler = new Crawler();// https://github.com/request/request
// https://github.com/bda-research/node-crawler/tree/master?tab=readme-ov-file#basic-usage
// https://github.com/cheeriojs/cheerio/wiki/Chinese-READMEconst articleList = [];function getArticleList() {let url = "https://blog.csdn.net/community/home-api/v1/get-business-list";const queryParams = {businessType: "blog",username,page: 1,size: 1000,};url +="?" +Object.entries(queryParams).map(([key, value]) => key + "=" + value).join("&");return new Promise((resolve, reject) => {crawler.direct({uri: url,headers: {Cookie: `yd_captcha_token=${cookie_yd_captcha_token}; waf_captcha_marker=${cookie_waf_captcha_marker}`,},callback: (error, res) => {if (error) {reject(error);} else {const { code, data, message } = JSON.parse(res.body);if (code === 200) {resolve(data);} else {reject({ code, message });}}},});});
}function getArticleDetail(articleId) {return new Promise((resolve, reject) => {crawler.direct({uri: `https://blog.csdn.net/${username}/article/details/${articleId}`,callback: (error, res) => {if (error) {reject(error);} else {const $ = res.$;const title = $("#articleContentId").text();const content = $("#article_content").text();const categories = $(`.tag-link[href^="https://blog.csdn.net"]`).map((_i, el) => $(el).text()).get();const tags = $(`.tag-link[href^="https://so.csdn.net/so/search"]`).map((_i, el) => $(el).text()).get();const articleDetail = {title,categories,tags,content,};resolve(articleDetail);}},});});
}async function start() {const { list } = await getArticleList();for (let i = 0; i < list.length; i++) {const { articleId, description, formatTime, postTime, title } = list[i];const { categories, tags, content } = await getArticleDetail(articleId);articleList.push({articleId,title,description,categories,tags,content,postTime,formatTime,});}outputToJson();
}function outputToJson() {const path = "./result.json";writeFile(path, JSON.stringify(articleList), (error) => {if (error) {console.log("An error has occurred ", error);return;}console.log("Data written successfully to disk");});
}
start();

相关文章:

爬虫(没)入门:用 node-crawler 爬取 blog

起因 前几天想给一个项目加 eslint&#xff0c;记得自己曾经在博客里写过相关内容&#xff0c;所以来搜索。但是发现 csdn 的只能按标题&#xff0c;没办法搜正文&#xff0c;所以我没搜到自己想要的内容。 没办法只能自己又重新折腾了一通 eslint&#xff0c;很烦躁。迁怒于…...

GAMES104笔记

GAMES104 文章目录 GAMES10401.从入门到实践什么是游戏引擎学习顺序 02.引擎架构分层资源层功能层核心层平台层工具层总结 03.如何构建游戏世界其它需要管理的复杂情况 04.游戏引擎中的渲染实践Rendering游戏引擎中的绘制知识点&#xff08;结合实际进化而来&#xff09;渲染基…...

ARM功耗管理架构演进及变迁

安全之安全(security)博客目录导读 目录 一、功耗管理架构演进及变迁概述 二、多核 三、big.LITTLE 四、DynamIQ...

ARM-V9 RME(Realm Management Extension)系统架构之系统能力的设备隔离和保护

安全之安全(security)博客目录导读 目录 三、设备隔离和保护 1、外设隔离 2、非pe请求者(设备)...

如何为律师制作专业的商务名片?含电子名片二维码

律师关注细节&#xff0c;律师名片也不例外。它们不仅仅是身份的象征&#xff0c;更是律师专业形象的代表&#xff0c;传递专业知识和信任。今天就来和我们一起来看看制作律师商务名片的注意事项&#xff0c;以及如何制作商务名片上的电子名片二维码&#xff1f; 一、名片的主…...

方案设计|汽车轮胎数显胎压计方案

一、引言 数显轮胎胎压计是一个专门测量车辆轮胎气压的工具&#xff0c;它具有高精度测量的功能&#xff0c;能够帮助快速准确获取轮胎气压正确数值&#xff0c;保证轮胎使用安全。本文将对数显轮胎胎压计的方案技术进行分析&#xff0c;包括其基本原理、硬件构成、软件设计等方…...

MySQL学习——选项文件的使用

MySQL 的许多程序都可以从选项文件&#xff08;有时也被称为配置文件&#xff09;中读取启动选项。选项文件提供了一种方便的方式来指定常用的选项&#xff0c;这样你就不必每次运行程序时都在命令行上输入这些选项。 要确定一个程序是否读取选项文件&#xff0c;你可以使用 -…...

Vulnhub-DC-3

joomla3.7.0的提权 信息收集 靶机IP:192.168.20.136 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 首先nmap扫端口和版本&#xff0c;dirsearch跑下目录&#xff0c;wappalyzer也可以用下 发现服务器用的ubuntu&#xff0c;JoomlaCMS…...

docker 停止重启容器命令start/stop/restart详解(容器生命周期管理教程-2)

Docker 提供了多个命令来管理容器的生命周期&#xff0c; 其中start、stop 和 restart。这些命令允许用户控制容器的运行状态。 1. docker start 命令格式&#xff1a; docker start [OPTIONS] CONTAINER [CONTAINER...]功能&#xff1a; 启动一个或多个已经停止的 Docker …...

lua字符串模式匹配

string.gmatch()不支持匹配首字符 string.gmatch(s, pattern)中&#xff0c;如果s的开头是’^字符&#xff0c;不会被当成首字符标志&#xff0c;而是被当成一个普通字符。 比如 s"hello world from lua" for w in string.gmatch(s, "^%a") doprint(w) e…...

【深度学习】温故而知新4-手写体识别-多层感知机+CNN网络-完整代码-可运行

多层感知机版本 import torch import torch.nn as nn import numpy as np import torch.utils from torch.utils.data import DataLoader, Dataset import torchvision from torchvision import transforms import matplotlib.pyplot as plt import matplotlib import os # 前…...

ChatGPT 论文翻译指南!解锁高质量翻译的秘密!

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …...

SQLserver通过CLR调用TCP接口

一、SQLserver启用CLR 查看是否开启CRL&#xff0c;如果run_value1&#xff0c;则表示开启 EXEC sp_configure clr enabled; GO RECONFIGURE; GO如果未启用&#xff0c;则执行如下命令启用CLR sp_configure clr enabled, 1; GO RECONFIGURE; GO二、创建 CLR 程序集 创建新项…...

前复权、后复权,技术分析看哪个?价值投资呢?

先说结论&#xff0c; 前复权可以实现技术指标的连续性&#xff0c;适合技术分析&#xff0c; 后复权可以实现股价走势的连续性&#xff0c;适合价值投资者 ​ 从头来说&#xff0c;一家公司盈利后&#xff0c;可以选择用盈利购买新的生产设备或者拓展生产&#xff0c;但是…...

Python正则表达式:深度解析URL匹配与操作

Python正则表达式&#xff1a;深度解析URL匹配与操作 在Python编程中&#xff0c;正则表达式&#xff08;Regular Expression&#xff0c;简称regex或regexp&#xff09;是一种强大的文本处理工具&#xff0c;它可以帮助我们快速匹配、查找、替换复杂的文本模式。在处理URL&am…...

[C][数据结构][顺序表]详细讲解+实现

目录 1.线性表2.顺序表 - SeqList3.实现4.顺序表缺点 1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构&#xff0…...

vscode运行Java utf-8文件中文乱码报错

问题现象 vscode 运行utf-8 java文,爆出如下错误 hello.java:5: &#xfffd;&#xfffd;&#xfffd;&#xfffd;: &#xfffd;&#xfffd;&#xfffd;&#xfffd;GBK&#xfffd;IJ&#xfffd;&#xfffd;&#xfffd;ӳ&#xfffd;&#xfffd;&#xfffd;ַ&a…...

Mybatis杂记

group by查询返回map类型 1,2 List<Map<String, Object>> getCount();xml: <select id"getCount" resultType"java.util.HashMap">SELECT company_id, ifnull(sum(count_a count_b),0) ctFROM test.com_countWHERE is_del 0 GROUP BY…...

修改缓存供应商--EhCache

除了我们默认的缓存形式simlpe之外, 我们其实还有许多其他种类的缓存供应 Ehcache就是其中的一种形式 Ehcache在SpringBoot当中的使用: 其实跟我们之前整合第三方的资源是一样的形式 1>导入依赖: <!-- 更换缓存, 将默认使用的 Simple 更换为Ehcache--> <depe…...

20240606更新Toybrick的TB-RK3588开发板在Android12下的内核

20240606更新Toybrick的TB-RK3588开发板在Android12下的内核 2024/6/6 10:51 0、整体编译&#xff1a; 1、cat android12-rk-outside.tar.gz* | tar -xzv 2、cd android12 3、. build/envsetup.sh 4、lunch rk3588_s-userdebug 5、./build.sh -AUCKu -d rk3588-toybrick-x0-a…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...