用js写个爬虫

article/2025/10/11 4:58:55

如何用js完成爬虫项目

  • 前言
  • 一、node.js的安装
  • 二、mysql的安装
  • 三、确定爬取网页
  • 四、查看分析网页源码
  • 五、开始写爬虫
    • npm···
    • Node调用mysql
    • 定义要访问的网站
    • 定义新闻页面里具体的元素的读取方式
    • 定义哪些url可以作为新闻页面
    • 构造一个模仿浏览器的request
    • 读取种子页面
    • 解析出种子页面里所有的链接
    • 遍历种子页面里所有的链接
    • 规整化所有链接,如果符合新闻URL的正则表达式就爬取
    • 读取具体的新闻页面,构造一个空的fetch对象用于存储数据
    • 读取新闻页面中的元素并保存到fetch对象里
  • 到这里爬虫已经差不多写好了
  • 六、构建网站访问数据库中爬取到的内容
    • 首先创建一个7.03.html作为网页端(前端)
    • 再创建一个7.03.js作为后端
    • Node运行7.03.js后访问http://127.0.0.1:8080/7.03.html
    • 用express脚手架来创建一个网站框架
    • 用vscode打开文件search_site/routes/index.js
    • 在search_site/public/下创建一个search.html
    • 在search_site文件夹下cmd运行(node bin/www),然后用浏览器打开网址http://127.0.0.1:8080/search.html

前言

这是我第一次尝试写爬虫,爬虫代码肯定有很多地方写得不太好,不过思路大致是这个样子,之后我还会做些优化。
在这里我用的vscode写的一个新闻网站的爬虫。
可以自行安装vscode。
开始之前还要做些准备工作。


一、node.js的安装

Windows下https://nodejs.org/ 官网下载安装最新的LTS版本。
MAC OS下载相应的pkg包安装。
Linux下载tar.gz包解压运行。
特殊linux版本或者嵌入式系统(比如树莓派系统)可下载源码编译。
具体安装例子见:
https://www.runoob.com/nodejs/nodejs-install-setup.html

二、mysql的安装

https://dev.mysql.com/downloads/mysql/
在这里下载安装包后将其解压到c:\mysql文件夹。
然后以管理员身份打开命令行。
在这里插入图片描述
然后自己动手安装配置好后就可以开始使用了
进入mysql后可创建一个数据库crawl,然后再创建一个表fetches。
新建文件fetches.sql。
在这里插入图片描述
然后就可以拷贝到命令行里。
在这里插入图片描述

三、确定爬取网页

我选取的是新华网http://www.xinhuanet.com/

四、查看分析网页源码

在该新闻网页右键查看源代码。
如:
分析我们需要爬取的内容。

五、开始写爬虫

npm···

在这里插入图片描述
我们需要先在终端输入npm i request npm i cheerio npm i iconv-lite npm i data-utils
来安装这些我们需要的包。

下面就是爬虫里我们要引用的包
代码如下(示例):

var fs = require('fs');
var myRequest = require('request')//获取网页内容
var myCheerio = require('cheerio')//筛选网页信息
var myIconv = require('iconv-lite')
require('date-utils');

Node调用mysql

npm install mysql;

创建mysql.js文件

代码如下(示例):

var mysql = require("mysql");
var pool = mysql.createPool({host: '127.0.0.1',user: 'root',password: 'root',database: 'crawl'
});
var query = function(sql, sqlparam, callback) {pool.getConnection(function(err, conn) {if (err) {callback(err, null, null);} else {conn.query(sql, sqlparam, function(qerr, vals, fields) {conn.release(); //释放连接 callback(qerr, vals, fields); //事件驱动回调 });}});
};
var query_noparam = function(sql, callback) {pool.getConnection(function(err, conn) {if (err) {callback(err, null, null);} else {conn.query(sql, function(qerr, vals, fields) {conn.release(); //释放连接 callback(qerr, vals, fields); //事件驱动回调 });}});
};
exports.query = query;
exports.query_noparam = query_noparam;

定义要访问的网站

var source_name = "新华网";
var myEncoding = "utf-8";
var seedURL = 'http://www.xinhuanet.com/';

定义新闻页面里具体的元素的读取方式

定义哪些url可以作为新闻页面


var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
var author_format = "$('.editor').text()";
var content_format = "$('.left_zw').text()";
var date_format = "$('.info').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('#source_baidu').text()";
var url_reg = /\d{4}-\d{2}\/\d{2}\//;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/

url_reg后面的正则表达式可以根据新闻网页链接的特征表示。
我这里通过链接上有年月日的时间这个特征来表示的。

构造一个模仿浏览器的request

//request模块异步fetch url
function request(url, callback) {var options = {url: url,encoding: null,//proxy: 'http://x.x.x.x:8080',headers: headers,timeout: 10000 //}myRequest(options, callback)
}

读取种子页面

解析出种子页面里所有的链接

request(seedURL, function (err, res, body) { //读取种子页面// try {//用iconv转换编码var html = myIconv.decode(body, myEncoding);//console.log(html);//准备用cheerio解析htmlvar $ = myCheerio.load(html, { decodeEntities: true });// } catch (e) { console.log('读种子页面并转码出错:' + e) };var seedurl_news;try {seedurl_news = eval(seedURL_format);//console.log(seedurl_news);} catch (e) { console.log('url列表所处的html块识别出错:' + e) };

遍历种子页面里所有的链接

规整化所有链接,如果符合新闻URL的正则表达式就爬取

request(seedURL, function (err, res, body) { //读取种子页面// try {//用iconv转换编码var html = myIconv.decode(body, myEncoding);//console.log(html);//准备用cheerio解析htmlvar $ = myCheerio.load(html, { decodeEntities: true });// } catch (e) { console.log('读种子页面并转码出错:' + e) };var seedurl_news;try {seedurl_news = eval(seedURL_format);//console.log(seedurl_news);} catch (e) { console.log('url列表所处的html块识别出错:' + e) };seedurl_news.each(function (i, e) { //遍历种子页面里所有的a链接var myURL = "";try {//得到具体新闻urlvar href = "";href = $(e).attr("href");if (typeof (href) == "undefined") {  // 有些网页地址undefinedreturn true;}if (href.toLowerCase().indexOf('http://') >= 0 || href.toLowerCase().indexOf('https://') >= 0) myURL = href; //http://开头的或者https://开头else if (href.startsWith('//')) myURL = 'http:' + href; 开头的else myURL = seedURL.substr(0, seedURL.lastIndexOf('/') + 1) + href; //其他} catch (e) { console.log('识别种子页面中的新闻链接出错:' + e) }if (!url_reg.test(myURL)) return; //检验是否符合新闻url的正则表达式//console.log(myURL);newsGet(myURL); //读取新闻页面});
});

读取具体的新闻页面,构造一个空的fetch对象用于存储数据

function newsGet(myURL) { //读取新闻页面request(myURL, function (err, res, body) { //读取新闻页面//try {var html_news = myIconv.decode(body, myEncoding); //用iconv转换编码//console.log(html_news);//准备用cheerio解析html_newsvar $ = myCheerio.load(html_news, { decodeEntities: true });myhtml = html_news;//} catch (e) {    console.log('读新闻页面并转码出错:' + e);};console.log("转码读取成功:" + myURL);//动态执行format字符串,构建json对象准备写入文件或数据库var fetch = {};fetch.title = "";fetch.content = "";fetch.publish_date = (new Date()).toFormat("YYYY-MM-DD");//fetch.html = myhtml;fetch.url = myURL;fetch.source_name = source_name;fetch.source_encoding = myEncoding; //编码fetch.crawltime = new Date();

读取新闻页面中的元素并保存到fetch对象里

if (keywords_format == "") fetch.keywords = source_name; // eval(keywords_format);  //没有关键词就用sourcenameelse fetch.keywords = eval(keywords_format);if (title_format == "") fetch.title = ""else fetch.title = eval(title_format); //标题if (date_format == "") fetch.publish_date = ""else fetch.date = eval(date_format); try {if (author_format == "") fetch.author = source_name; //eval(author_format);  //作者else fetch.author = eval(author_format);if (content_format == "") fetch.content = "";else fetch.content = eval(content_format).replace("\r\n" + fetch.author, ""); //内容,是否要去掉作者信息自行决定if (source_format == "") fetch.source = fetch.source_name;else fetch.source = eval(source_format).replace("\r\n", ""); //来源if (desc_format == "") fetch.desc = fetch.title;else fetch.desc = eval(desc_format).replace("\r\n", ""); //摘要    } catch (e) { return; }// var filename = source_name + "_" + (new Date()).toFormat("YYYY-MM-DD") +//     "_" + myURL.substr(myURL.lastIndexOf('/') + 1) + ".json";// 存储json// fs.writeFileSync(filename, JSON.stringify(fetch));var fetchAddSql = 'INSERT INTO fetches(url,source_name,source_encoding,title,' +'keywords,author,publish_date,crawltime,content) VALUES(?,?,?,?,?,?,?,?,?)';var fetchAddSql_Params = [fetch.url, fetch.source_name, fetch.source_encoding,fetch.title, fetch.keywords, fetch.author, fetch.publish_date,fetch.crawltime.toFormat("YYYY-MM-DD HH24:MI:SS"), fetch.content];//执行sql,数据库中fetch表里的url属性是unique的,不会把重复的url内容写入数据库mysql.query(fetchAddSql, fetchAddSql_Params, function(qerr, vals, fields) {if (qerr) {console.log(qerr);}}); //mysql写入});
}

我这里try{}catch(e){return;}就是如果抓取不到的就跳过了;

到这里爬虫已经差不多写好了

六、构建网站访问数据库中爬取到的内容

首先创建一个7.03.html作为网页端(前端)

代码如下(示例):

<!DOCTYPE html>
<html><body><form action="http://127.0.0.1:8080/process_get" method="GET"><br> 标题:<input type="text" name="title"><input type="submit" value="Submit"></form><script></script>
</body></html>

再创建一个7.03.js作为后端

代码如下(示例):

var express = require('express');
var mysql = require('./mysql.js')
var app = express();
var cors = require('cors');
app.use(cors());
//app.use(express.static('public'));
app.get('/7.03.html', function(req, res) {res.sendFile(__dirname + "/" + "7.03.html");
})
app.get('/7.04.html', function(req, res) {res.sendFile(__dirname + "/" + "7.04.html");
})
app.get('/process_get', function(req, res) {res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' }); //设置res编码为utf-8//sql字符串和参数var fetchSql = "select url,source_name,title,author,publish_date from fetches where title like '%" +req.query.title + "%'";console.log(fetchSql);mysql.query(fetchSql, function(err, result, fields) {console.log(result);res.end(JSON.stringify(result));});
})
var server = app.listen(8080, function() {console.log("访问地址为 http://127.0.0.1:8080/7.03.html")})

Node运行7.03.js后访问http://127.0.0.1:8080/7.03.html

在这里插入图片描述
点击submit后得到
在这里插入图片描述

用express脚手架来创建一个网站框架

在命令行中之前文件的目录下

express –e search_site

生成出一个search_site的文件夹。
由于我们需要使用mysql,因此将mysql.js拷贝进这个文件夹。
mysql.js拷贝后还需要在search_site文件夹内cmd运行。
依次进行:

npm install mysql –save
npm install

用vscode打开文件search_site/routes/index.js

添加

router.get('/process_get', function(request, response) {//sql字符串和参数var fetchSql = "select url,source_name,title,author,publish_date " +"from fetches where title like '%" + request.query.title + "%'";mysql.query(fetchSql, function(err, result, fields) {response.writeHead(200, {"Content-Type": "application/json"});response.write(JSON.stringify(result));response.end();});
});

在search_site/public/下创建一个search.html

<!DOCTYPE html>
<html>
<header><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</header><body><form><br> 标题:<input type="text" name="title_text"><input class="form-submit" type="button" value="查询"></form><div class="cardLayout" style="margin: 10px 0px"><table width="100%" id="record2"></table></div><script>$(document).ready(function() {$("input:button").click(function() {$.get('http://127.0.0.1:8080/process_get', {title : $("input:text").val()}, function(data) {$("#record2").empty();$("#record2").append('<tr class="cardLayout"><td>url</td><td>source_name</td>' +'<td>title</td><td>author</td><td>publish_date</td></tr>');data = JSON.parse(data);for (let list of data) {let table = '<tr class="cardLayout"><td>';Object.values(list).forEach(element => {table += (element + '</td><td>');});$("#record2").append(table + '</td></tr>');}});});});</script>
</body></html>

在search_site文件夹下cmd运行(node bin/www),然后用浏览器打开网址http://127.0.0.1:8080/search.html

在这里插入图片描述
差不多就成品就是这样了,还有些欠缺的地方,之后还要补充改进。


http://chatgpt.dhexx.cn/article/RSbIR60o.shtml

相关文章

Docker—容器数据卷

目录 1.什么是容器数据卷 2.数据卷的使用 3.实战MySQL同步数据 4.卷 常用命令 5.匿名挂载与具名挂载 5.1 匿名挂载 5.2 具名挂载 6.数据卷容器 1.什么是容器数据卷 Docker将运用与运行的环境打包形成容器运行&#xff0c; Docker容器产生的数据&#xff0c;如果不通过d…

docker删除已停止的容器

一、docker删除已停止的容器 1、根据容器的状态&#xff0c;删除Exited状态的容器 先停止容器、再删除镜像中的容器、最后删除none的镜像。执行命令如下&#xff1a; docker stop $(docker ps -a | grep "Exited" | awk {print $1 }) #停止容器 docker rm $(docke…

Docker与容器的区别

容器 虚拟机和容器结构主要区别在于中间两层&#xff0c;虚拟机自带了操作系统&#xff0c;这个操作系统可以和宿主机一样&#xff0c;也可以不一样&#xff0c;而容器是和宿主机共享一个操作系统。我们知道&#xff0c;操作系统的内容是很多的&#xff0c;就像我们自己电脑上的…

docker 删除 容器/镜像 方法

docker的删除有两种&#xff0c;一个是rm 删除容器&#xff0c;一个是rmi删除镜像 想要删除运行过的images(镜像) 必须首先删除它的container(容器) 首先查看并找到要删除的镜像 docker images 这里我们要删除hello-world镜像 使用rmi 加 镜像id docker rmi fce289e99eb9 报…

docker多容器操作与强制删除容器的方法步骤

简介&#xff1a; 这是一篇有关【doker的多容器操作和强制删除容器的方法】的文章&#xff0c;用最精简的语言去表达给前端读者们。 1、创建多个容器 在WIndows环境下我们来作这个&#xff0c;先打开三个PowerShell窗口&#xff0c;然后在每个窗口中输入创建容器的命令&#…

Docker删除镜像和容器

一、删除容器 首先需要停止所有的容器&#xff08;只停止单个时把后面的变量改为image id即可&#xff09; docker stop $(docker ps -a -q) 删除所有的容器&#xff08;只删除单个时把后面的变量改为image id即可&#xff09; docker rm $(docker ps -a -q) 二、删除镜像…

如何删除docker镜像与容器

目录 删除示例&#xff1a; 1.查看docker中要删除的镜像 2.删除镜像&#xff0c;使用命令“docker rmi image id” 3.查看docker中正在运行的容器 4.停止容器 5.删除容器 6.查看容器 7.删除镜像 8.最后查看所有镜像 删除示例&#xff1a; 1.查看docker中要删除的镜像 …

docker删除容器日志

废话不多数,看步骤 查看容器日志命令 docker logs -f 容器id 1.进入docker容器目录 cd /var/lib/docker/containers/ 2.查看容器的id&#xff0c;获取容器id&#xff0c;容器id就是容器目录名字 docker ps -a 3.进入容器&#xff0c;删除以.log结尾的文件就是日志文件了&a…

Docker 删除容器

用docker一段时间后&#xff0c;host上可能会有大量已经退出了的容器&#xff0c; 这些容器依然会占用host的文件系统资源&#xff0c;如果确认不会再重启此类容器&#xff0c;可以通过docker rm删除 docker rm一次可以指定多个容器&#xff0c;如果希望批量删除所有已经退出的…

如何优雅地删除Docker镜像和容器(超详细)

一、前言 大家是怎么删除Docker中的镜像和容器的呢&#xff0c;有没有考虑过如何优雅地删除呢&#xff1f;本教程详细指导如何在优雅地删除Docker容器和镜像。如需了解如何在Centos7系统里面安装Docker&#xff0c;可以参考教程【最新Docker在Centos7下安装部署&#xff08;参考…

AirPlay

最近一直在研究苹果的AirPlay协议。真是个好东西&#xff0c;基本上现在可以把任何屏幕内容&#xff0c;包括音频&#xff0c;视频和屏幕投射到支持AirPlay接收的设备上&#xff0c;目前是Apple TV或者AirExpress。 网上资料很少&#xff0c;比较有用的是这个哥们写的一篇AirPl…

aplay amixer用法详解

aplay aplay -t raw -c 1 -f S16_LE -r 8000 test2.pcm -t: type raw表示是PCM -c: channel 1 -f S16_LE: Signed 16bit-width Little-Endian -r: sample rate 8000 PCM是最raw的音频数据&#xff0c;没有任何头信息。WAV文件就是PCM头信息&#xff0c;头信息就是上述的声道…

aplay amixer arecord说明

arecord声音录制 arecord -l列出声卡和数字音频设备 -D参数用于指定音频设备PCM 以hwx,x开头 根据上面l列出的设备,如果选择tvp5158来录制声音的话那么pcm设备就位hw0,0,如果是tlv320aic3x则pcm设备为hw0,1,sii9135则pcm设备为hw0,2。 声卡号,设备号 -r指定采样频率:5512/8000…

【全志T113-S3_100ask】9-音频输入与输出(amixer、aplay、arecord)

【全志T113-S3_100ask】9-音频输入与输出 背景&#xff08;一&#xff09;amixer 的使用&#xff08;二&#xff09;amixer 设置音频输出参数&#xff08;三&#xff09;使用 aplay 播放音乐&#xff08;四&#xff09;使用 arecord 录音 背景 之前查看系统命令&#xff0c;发…

如何使用Aplayer播放器

Aplayer 一、部署 1、Aplayer 官网体验网址&#xff1a;https://aplayer.js.org/ Aplayer的GitHub网址&#xff1a; https://github.com/MoePlayer/APlayer 中文文档手册网址&#xff1a;首页 – APlayer 使用方法很简单&#xff0c;加载Aplayer的js和css&#xff0c;在想…

aplay,arec,amix使用

标准的alsa命令为&#xff0c;aplay&#xff0c;amixer&#xff0c;arecord 如下是高通在mdm9x07芯片上做了客制化的命令 aplay aplay -D hw:0,17 -P &arec arec -D hw:0,17 -P -R 8000 -C 1 &-D card 声卡 -d device 设备 -c channels 通道 -r rate 采样率 -b bits…

Splay

Spaly 众所周知&#xff0c;Splay 是一种平衡二叉查找树&#xff08;不要告诉我你不知道二叉查找树是什么qwq 不知道什么是二叉查找树的看过来&#xff1a; 关于二叉查找树&#xff09;。在这篇东西的最后我们也解释了为什么我们需要用到平衡二叉查找树而不是直接查找&#xff…

aplay 源码分析

ffmpeg -formats ffmpeg -sample_fmts ffmpeg -i ../english14.mp3 -ar 44100 -ac 2 -sample_fmt s16 -f wav english14.wav ffmpeg -i ../english14.mp3 -ar 44100 -ac 2 -sample_fmt s16 -f s16le english14.pcm其中针对PCM个数的数据aplay正确的播放格式为&#xff1a; apl…

Qt,Linux: 播放声音(aplay)

Linux下&#xff0c;Qt开发&#xff0c;使用的电脑情况比较复杂&#xff0c;开发机是Intel cpu, 常用的验证机是飞腾&#xff08;arm&#xff09;cpu, 客户的目标机也是飞腾&#xff08;arm&#xff09;cpu, 但验证机和目标机上情况还不太一样。 需要用到播放声音的功能&#x…

ALSA音频架构 -- aplay播放流程分析

引言 上文ALSA音频架构 – snd_pcm_open函数分析已经获取了aplay中对应的播放API writei_func snd_pcm_writei;&#xff0c;本文将具体分析该API在哪里调用。 aplay处理数据流顺序图 代码详细分析 1、对参数进行解析&#xff0c;存储在全局变量hwparams&#xff0c;配置回调…