获取页码规律网页特定内容的方法探讨

最近发现一个网站,比较简单,也挺有意思。自己花了一些时间,对其源代码进行了分析,并尝试多种方法进行特定内容的抓取。

源代码分析

在Chrome中对这个网站的一个网页进行『检查』,发现关键部分如下:

<div class="test">
<h3>1、烘箱(干燥箱)在加热时,门可以开启。</h3>
<ul class="xuanxiang_panduan">
<li>
<input type="radio" name="ti_1" value="1" id="ti_1_1" />
<label for="ti_1_1"></label>
</li>
<li>
<input type="radio" name="ti_1" value="0" id="ti_1_0" />
<label for="ti_1_0"></label>
</li>
</ul>
</div>
<span>(标准答案: 错误 )</span>
<div class="test">
<h3>2、电源插座、接线板、电线的容量应满足电器功率的需要。</h3>
<ul class="xuanxiang_panduan">
<li>
<input type="radio" name="ti_2" value="1" id="ti_2_1" />
<label for="ti_2_1"></label>
</li>
<li>
<input type="radio" name="ti_2" value="0" id="ti_2_0" />
<label for="ti_2_0"></label>
</li>
</ul>
</div>
<span>(标准答案: 正确 )</span>
<div class="test">
<h3>3、为避免线路负荷过大,而引起火灾,功率1000瓦以上的设备不得共用一个接线板。</h3>
<ul class="xuanxiang_panduan">
<li>
<input type="radio" name="ti_3" value="1" id="ti_3_1" />
<label for="ti_3_1"></label>
</li>
<li>
<input type="radio" name="ti_3" value="0" id="ti_3_0" />
<label for="ti_3_0"></label>
</li>
</ul>
</div>
<span>(标准答案: 正确 )</span>
<div class="test">
<h3>4、对于触电事故,应立即切断电源或用有绝缘性能的木棍棒挑开和隔绝电流,如果触电者的衣服干燥,又没有紧缠住身上,可以用一只手抓住他的衣服,拉离带电体;但救护人不得接触触电者的皮肤,也不能抓他的鞋。</h3>
<ul class="xuanxiang_panduan">
<li>
<input type="radio" name="ti_4" value="1" id="ti_4_1" />
<label for="ti_4_1"></label>
</li>
<li>
<input type="radio" name="ti_4" value="0" id="ti_4_0" />
<label for="ti_4_0"></label>
</li>
</ul>
</div>
<span>(标准答案: 正确 )</span>
<div class="test">
<h3>7、实验室内的电线、开关、灯头、插头、插座等一切电器用具,要经常检查是否完好,有无漏电、潮湿、霉烂等情况。一旦有问题应立即报修。 </h3>
<ul class="xuanxiang_panduan">
<li>
<input type="radio" name="ti_7" value="1" id="ti_7_1" />
<label for="ti_7_1"></label>
</li>
<li>
<input type="radio" name="ti_7" value="0" id="ti_7_0" />
<label for="ti_7_0"></label>
</li>
</ul>
</div>
<span>(标准答案: 正确 )</span>

可以看到,试题和答案部分的DOM节点特征非常明显,单页获取并不难。

jQuery实现单页获取

在Chrome的Console中写入下面一段代码:

var script = document.createElement('script');
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
setTimeout(function() {
var test = "";
$("div.test").each(function() {
test = test + $(this).children("h3").text() + "\n" + $(this).next("span").text() + "\n";
});
console.log(test);
}, 1000);

代码的思路是先引入jQuery库,1s后用jQuery函数操作DOM节点。打印结果如下:

1、烘箱(干燥箱)在加热时,门可以开启。
(标准答案: 错误 )
2、电源插座、接线板、电线的容量应满足电器功率的需要。
(标准答案: 正确 )
3、为避免线路负荷过大,而引起火灾,功率1000瓦以上的设备不得共用一个接线板。
(标准答案: 正确 )
4、对于触电事故,应立即切断电源或用有绝缘性能的木棍棒挑开和隔绝电流,如果触电者的衣服干燥,又没有紧缠住身上,可以用一只手抓住他的衣服,拉离带电体;但救护人不得接触触电者的皮肤,也不能抓他的鞋。
(标准答案: 正确 )
7、实验室内的电线、开关、灯头、插头、插座等一切电器用具,要经常检查是否完好,有无漏电、潮湿、霉烂等情况。一旦有问题应立即报修。
(标准答案: 正确 )

Node.js实现多页获取

出于某种明显的意图,网页的设计者将几百道题目分成100多页,每页只有5道题。而一换页,页面刷新,上面的代码就不行了。

这里尝试使用Node.js,Node.js本身的强大和方便无需多说。在服务器端解析HTML代码,可以采用cheerio,官方对其有这样的描述:

Fast, flexible & lean implementation of core jQuery designed specifically for the server.

我们先从简单的来,用这种方法先实现单页的,代码如下:

var http = require('http');
var iconv = require('iconv-lite');
var cheerio = require('cheerio');
var url = "http://biebu.xin"; // 实际使用换成真正的链接
http.get(url, function(res) {
res.setEncoding('binary');
var source = "";
res.on('data', function(data) {
source += data;
});
res.on('end', function() {
var buf = new Buffer(source, 'binary');
var str = iconv.decode(buf, 'GBK');
// console.log(str);
var $ = cheerio.load(str);
var test = "";
$('div.test').each(function() {
test = test + $(this).children('h3').text() + "\n" + $(this).next('span').text() + "\n";
});
console.log(test);
});
}).on('error', function() {
console.log("获取数据出现错误");
});

iconv-lite是负责字符编码转换的,运行前注意将调用的模块先npm install一下,运行结果如下:

1、烘箱(干燥箱)在加热时,门可以开启。
(标准答案: 错误 )
2、电源插座、接线板、电线的容量应满足电器功率的需要。
(标准答案: 正确 )
3、为避免线路负荷过大,而引起火灾,功率1000瓦以上的设备不得共用一个接线板。
(标准答案: 正确 )
4、对于触电事故,应立即切断电源或用有绝缘性能的木棍棒挑开和隔绝电流,如果触电者的衣服干燥,又没有紧缠住身上,可以用一只手抓住他的衣服,拉离带电体;但救护人不得
接触触电者的皮肤,也不能抓他的鞋。
(标准答案: 正确 )
7、实验室内的电线、开关、灯头、插头、插座等一切电器用具,要经常检查是否完好,有无漏电、潮湿、霉烂等情况。一旦有问题应立即报修。
(标准答案: 正确 )

如果想获取多页的,比如3页,并将结果保存到txt文件中,可以这样:

var http = require('http');
var iconv = require('iconv-lite');
var cheerio = require('cheerio');
var fs = require("fs");
var urls = [];
var pageNum = 3;
for(i=1; i<=pageNum; i++) {
urls.push('http://biebu.xin'+i); // 实际使用换成真正的链接
}
var count = 0;
var test = "";
urls.forEach(function(url) {
http.get(url, function(res) {
res.setEncoding('binary');
var source = "";
res.on('data', function(data) {
source += data;
});
res.on('end', function() {
var buf = new Buffer(source, 'binary');
var str = iconv.decode(buf, 'GBK');
// console.log(str);
var $ = cheerio.load(str);
$('div.test').each(function() {
test = test + $(this).children('h3').text() + "\n" + $(this).next('span').text() + "\n";
});
count++;
if(count==pageNum) {
console.log(test);
fs.writeFile('test.txt', test, function(err) {
if (err) {
return console.error(err);
}
});
}
});
}).on('error', function() {
console.log("获取数据出现错误");
});
});

上述代码只考虑了是非题这么一种情况,对单选题没有考虑,并且输出结果比较『粗糙』。修改后的代码如下:

var http = require('http');
var iconv = require('iconv-lite');
var cheerio = require('cheerio');
var fs = require("fs");
var urls = [];
var startPageNum = 1; // 设置起始页码,默认为1
var pageNum = 3; // 设置页码总数
for(i=startPageNum; i<=-1+startPageNum+pageNum; i++) {
urls.push('http://biebu.xin'+i); // 实际使用换成真正的链接
}
var count = 0;
var html = "";
urls.forEach(function(url) {
http.get(url, function(res) {
res.setEncoding('binary');
var source = "";
res.on('data', function(data) {
source += data;
});
res.on('end', function() {
var buf = new Buffer(source, 'binary');
var str = iconv.decode(buf, 'GBK');
// console.log(str);
var $ = cheerio.load(str);
$('div.test').each(function() {
var title = $(this).children('h3').text();
var titlestart = title.indexOf('、');
title = title.substring(titlestart + 1);
if($(this).children('ul').hasClass('xuanxiang_panduan')) {
var answer = $(this).next('span').text().substring(13,15);
html += "<p>(是非题 )" + title + "\n\t\t<br />正确答案是 " + answer +"\n\t\t<br /><strong>提示</strong>:请注意选项是否被调换 ——别不信biebu.xin\n</p>\n";
}
if($(this).children('ul').hasClass('xuanxiang_xuanze')) {
var options = ""
$(this).children('ul').children('li').each(function() {
options += "\t\t<br />" + $(this).children('label').text() + "\n";
});
var answer = $(this).next('span').text().substring(13,14);
html += "<p>(单选题 )" + title + "\n" + options + "\t\t<br />正确答案是 " + answer +"\n\t\t<br /><strong>提示</strong>:请注意选项是否被调换 ——别不信biebu.xin\n</p>\n";
}
});
count++;
if(count==pageNum) {
// 设置输出文件名称
fs.writeFile('test.txt', html, function(err) {
if (err) {
return console.error(err);
}
});
}
});
}).on('error', function() {
console.log("获取数据出现错误");
});
});

最后,声明一下,仅供学习交流哦。