JavaScript爬取网页并分析

article/2025/10/11 5:03:51

JavaScript爬取网页并分析

任务分析

  1. 爬取三个网站下的新闻数据,这里选择网易新闻网(https://news.163.com/);
  2. 提取每条新闻的如下字段:标题,内容,发表日期,网址,关键词,作者,来源,评论等;
  3. 将爬取的数据写入数据库;
  4. 搭建前后端,实现对爬取数据的查询搜索分析等功能;

!!注:这篇博客只会对关键代码进行解析,完整代码在GitHub 中
代码链接:https://github.com/zgl-ai/a-crawler-about-javascript

爬虫部分

首先是爬取网易新闻网(https://news.163.com/)
引入一些必须的包

var fs = require('fs');
var myRequest = require('request')
var myCheerio = require('cheerio')
var myIconv = require('iconv-lite')
require('date-utils');

fs负责文件读写,request负责获得服务端发来的html响应,cheerio负责解析html文件,incov-lite负责html文件的编码格式的转换;

爬取网页的基本信息

var source_name = "网易新闻";
var myEncoding = "gbk";
var seedURL = 'https://news.163.com/';

要注意的是这个网页的编码格式为GBK,之前我使用的是“utf-8”发现出现了乱码。
还有一些字段格式的声明在之后解释。

爬虫的步骤一般如下:

  1. 向目标服务端发送一个种子页面请求;
  2. 服务端返回这个页面的html文件;
  3. 转码,解析文件,获得所需要的tag中信息;
  4. 如果还需要获取种子页面下一些超链接的信息,那么就需要重复以上几步;
    本次的实验也是按照以上的步骤进行的。

首先是构造相应的种子页面请求

//防止网站屏蔽我们的爬虫
var headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36'
}//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)
}

这是基于http协议构造的请求信息,包括了头部信息,网址,时间限制等信息。

得到种子页面的html文件后,解码并且解析

var html = myIconv.decode(body, myEncoding);
//console.log(html);
//准备用cheerio解析html
var $ = myCheerio.load(html, { decodeEntities: true });

我们的目的是通过网易新闻网的主页面,访问主页面中的新闻链接来获得新闻的信息。因此接下来我们要做的是,分析网易新闻网主页面中的新闻超链接,判断它是不是一个新闻的超链接(这里只提取文本类新闻)。
通过对比发现,网易所有文本类新闻的URL都有一个共同点:
三条网易的新闻url
那就是其中都有一个“article”字段。基于此来判断是不是一个有效的新闻链接
首先需要规范一下url的格式:
url不能是“undefined”,意思就是不能为空,

if (typeof(href) == "undefined") { // 有些网页地址undefined
return true;
}

url必须是以大写或者小写的“http://”或者“https://”开头,否则就在前面加上“http:”
路径名规范为https://news.163.com/……

if (href.toLowerCase().indexOf('http://') >= 0 || href.toLowerCase().indexOf('https://') >= 0) {
myURL = href; //http://开头的或者https://开头
//console.log(href);
}
else if (href.startsWith('//')) myURL = 'http:' + href; 开头的
else {
myURL = seedURL.substr(0, seedURL.lastIndexOf('/') + 1) + href; //其他
//console.log(href);
}

规范格式后就需要通过正则表达式的匹配来看有没有article字段:

var url_reg =/article/
if (!url_reg.test(myURL)){
//console.log(myURL);
return; //检验是否符合新闻url的正则表达式
} 

接下来就是提取新闻的信息(标题,内容,发表日期,网址,关键词,作者,来源,评论等);
需要分析一下网页的源文件
在这里插入图片描述
在这里插入图片描述

发现标题存放位置,在title标签中
在这里插入图片描述

var title_format = "$('title').text()";

关键词存储格式,在标签中的keywords-content键值对中

在这里插入图片描述

var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";

正文内容是在,“post_body”这一个类别中
在这里插入图片描述

var content_format = "$('.post_body').text()";

同理其他的所有的读取格式都通过对比网页源码来获得

var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
//<meta property="article:published_time" content="2021-04-26T14:47:04+08:00">
var date_format =" $('meta[property=\"article:published_time\"]').eq(0).attr(\"content\")";
//<meta name="author" content="网易">
var author_format = " $('meta[name=\"author\"]').eq(0).attr(\"content\")";
var content_format = "$('.post_body').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('.post_info').text()";

将目标信息通过evel读取并且存入fetch结构体中

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();

发表日期的写入的时候要注意
因为有的文章没有发表日期,所以需要填上“未知”,再将所有的日期转换为“YYYY-MM-DD”的格式

if (date_format != "") {
fetch.publish_date = eval(date_format); //刊登日期 
if (!fetch.publish_date){
fetch.publish_date = "未知";
}else{
//fetch.publish_date = regExp.exec(fetch.publish_date)[0];
fetch.publish_date = fetch.publish_date.replace('年', '-')
fetch.publish_date = fetch.publish_date.replace('月', '-')
fetch.publish_date = fetch.publish_date.replace('日', '')
fetch.publish_date = new Date(fetch.publish_date).toFormat("YYYY-MM-DD");
}
}

来源部分也需要通过正则表达式来提取

if (source_format == "") fetch.source = fetch.source_name;
else {
fetch.source = eval(source_format).replace("\r\n", ""); //来源
var matchReg = /(?<=来源: ).*?(?=.\n)/gi;
fetch.source=(fetch.source.match(matchReg));
if(fetch.source==null){
fetch.source=source_name;
}
}

完成上面所有的步骤,就可以提取新闻的信息了,一下选取一篇新闻提取的信息来展示
在这里插入图片描述

将爬取到的所有信息存储到mysql数据库中

这需要已经提前下载好的mysql数据库,并且创建好crawl库

create database crawl;
use crawl; 

创建表fetches

CREATE TABLE `fetches` (
`id_fetches` int(11) NOT NULL AUTO_INCREMENT,
`url` varchar(200) DEFAULT NULL,
`source_name` varchar(200) DEFAULT NULL,
`source_encoding` varchar(45) DEFAULT NULL,
`title` varchar(200) DEFAULT NULL,
`keywords` varchar(200) DEFAULT NULL,
`author` varchar(200) DEFAULT NULL,
`publish_date` date DEFAULT NULL,
`crawltime` datetime DEFAULT NULL,
`content` longtext,
`createtime` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id_fetches`),
UNIQUE KEY `id_fetches_UNIQUE` (`id_fetches`),
UNIQUE KEY `url_UNIQUE` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;

JavaScript连接数据库需要相应的包

var mysql = require('./mysql.js');

插入数据库的语句,和插入的变量

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写入

至此,从网易新闻官网爬取数据,并且存入数据库的任务已经完成,数据库里的数据展示如下
在这里插入图片描述

同理还爬取了搜狐(https://www.sohu.com)、腾讯新闻(https://www.qq.com)的新闻。总共的数据有三百多条。工作量不大,就是需要向上文那样,产源码的结构,找出目标字段所在的标签,然后提取出来存入数据库。
因此写了三个爬虫文件分别爬取这三个代码
在这里插入图片描述

搜索和分析

这里需要一个前后端交互的功能,用到了express脚手架,具体的搭建方法这里就不详述了。最后可以创建一个路由架构。
在这里插入图片描述
主要的工作量是search.html文件,time.html文件和index.js文件。

search.html文件:实验按标题搜索关键词的前端网页。
首先是需要一个表单,来接受用户输入的关键词并传输给后端

<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>

接下来需要通过javascript语言来与后端交互数据
与后段交互数据:
构造路由与出入数据

$va='/process_get?title=' + $("input:text").val();

传入数据后需要接受后端传来的数据,在data这个变量中

$.get($va, function(data) {

解析data数据存入表record2中

$("#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>');
for (let list of data) {
let table = '<tr class="cardLayout"><td>';
$i=0;
Object.values(list).forEach(element => {
if($i==0){
table += ('<a href='+element+'>'+element+'</a>'+ '</td><td>');
}else{
table += (element+'</td><td>');
}
$i++;
});
$("#record2").append(table + '</td></tr>');

这里是一列一列的读取data,然后对每列再一个一个元素的读取。存入的时候需要了解html文件中表格的格式在这里插入图片描述

除此之外,我还把url变成超链接的形式。url位于每一列的第一个元素中。上图的红框所示。
引入一个超链接指向time.html

<a href='time.html'>点击进行时间热度分析</a>

index.js:负责后端的,与数据库交互,与前端交互
首先是search.html路由到的函数

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();
});
});

收到前端传来的title数据之后,向数据库发送查询命令

Select url,source_name,title,author,publish_date
From fetches 
Where title like%……%

(这个是查询包含关键词$title的语法)
查询之后,结果写到了result变量中,然后以json格式传到前端的data中。

这两个part运行的结果如下:
工作目录下运行node bin/www
浏览器输入:localhost:3000/search.html
在这里插入图片描述

time.html文件:实现按照时间热度搜索关键词的前端页面。逻辑和search.html差不多
inde.js中负责处理time.html文件的函数

router.get('/time_get', function(request, response) {
//sql字符串和参数
var fetchSql = "select publish_date,COUNT(keywords) " +
"from fetches where keywords like'%" + request.query.title + "%' GROUP BY publish_date ORDER BY COUNT(keywords) DESC";
mysql.query(fetchSql, function(err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json"
});
console.log(result);response.write(JSON.stringify(result));
response.end();
});
});

它在接受到title之后,向数据库发送查询指令

Select publish_date,count(keywords)
From fetches 
Where keywords like%……%Group by publish_date
Order by count(keywords) desc;

这样可以做到使搜索结果按照发表日期做好统计。

点击这个之后就可以看到运行结果
在这里插入图片描述
发现目前统计的新闻数据中,4月27发表的新闻最多。
搜索“裙子”关键词
在这里插入图片描述

总结

这次项目的几个难点

  1. 为了找到新闻url,需要查看很多新闻的url,找到相似处,由于每个企业对网页的格式定义不同,我们也需要相应的改写相关的正则表达式;
  2. 读取相关字段,如标题,日期作者等等。在选取本次实验的三个种子网页(腾讯,网易,搜狐)之前,我还尝试爬取过虎扑,东方财经等其他新闻网页。但是在爬取过程中总是碰到一些奇怪的问题。比如有的字段藏在了很复杂的标签块中,在爬虫代码中不好以统一的格式定义,因此在代码运行的过程中总是出现爬取到空字段的情况。还有就是每个网站的编码格式不一样,有的是utf-8有的又是gbk,所以在爬取的时候需要都试一试。
    虽然如此,这些网页还是会采用一些通用的标准来编写,比如“标题,关键字,摘要”等信息。
  3. 统一每个网页爬取到的信息。在爬取“时间”这一个字段是,有的网页是“2021-4-29”,有的又是“4/29/2021”,有的又是“2021年4月29号”,虽然统一起来不是很难吧,但也很麻烦。
  4. 前后端路由。由于对Javascript语法不是很熟悉,所以在编写代码中也遇到很多问题,就比如前后端路由的时候。具体一点,就是前端怎么向后端传数据,后端接受数据后怎么读取出来。后端怎么向前端传数据,前端接收到之后,怎么将json格式的数据读取出来。在搞清语法上花了很多时间。
  5. 本来之前还想做很多任务的,比如对展示结果进行分页,用css做一个好看一点的前端,在交互上做的好一点。但是自己的这方面知识确实太贫瘠了,作业提交时间也快到了。因此也就完成了这些基本功能。
    好在之前有数据库,python flask,php等相关基础,因此还是完成了这次实验。

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

相关文章

js - 爬虫的实现

爬虫的原理 爬虫&#xff0c;就是一个自动爬取网页上展示的信息的工具。我们要写一款爬虫&#xff0c;就要满足下面的条件&#xff1a; 网络的请求。首先我们要进行网络请求&#xff0c;让目标给我们返回信息&#xff08;常用的模块有http、http2、https、request、axios、pu…

Node.js实现简单爬虫 讲解

一、什么是爬虫 网络爬虫(又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在FOAF社区中间&#xff0c;更经常的称为网页追逐者)&#xff0c;是一种按照一定规则&#xff0c;自动的抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕…

爬虫攻略(一)

互联网 Web 就是一个巨大无比的数据库&#xff0c;但是这个数据库没有一个像 SQL 语言可以直接获取里面的数据&#xff0c;因为更多时候 Web 是供肉眼阅读和操作的。如果要让机器在 Web 取得数据&#xff0c;那往往就是我们所说的“爬虫”了。现在项目需要&#xff0c;所以研究…

javascript爬虫

用js爬虫&#xff0c;使用到nodejs 例子中爬取的是中国新闻网时政频道的最新10条新闻的title和链接 事先准备&#xff1a; 1、先去node官网下载安装一下 2、推荐vs code&#xff0c;需要通过终端下载插件&#xff0c;不想的话用cmd小黑框做终端&#xff0c;记事本写代码也行…

用js写个爬虫

如何用js完成爬虫项目 前言一、node.js的安装二、mysql的安装三、确定爬取网页四、查看分析网页源码五、开始写爬虫npmNode调用mysql定义要访问的网站定义新闻页面里具体的元素的读取方式定义哪些url可以作为新闻页面构造一个模仿浏览器的request读取种子页面解析出种子页面里所…

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;在想…