當前爬蟲項目開發所需中間件:
-
cheerio: 則能夠對請求結果進行解析,解析方式和jquery的解析方式幾乎完全相同 cheerio中文文檔 開發參考node - cheerio模塊
-
superagent: 能夠實現主動發起get/post/delete等請求
-
superagent-charset: 解決爬蟲數據中文亂碼問題,早期版本單獨使用,現配合superagent使用
-
koa2: 搭建服務器環境等等
-
koa-router: koa路由,用於根據路由訪問對應代碼塊,邏輯編寫等作用(把他理解為像日常API接口就好)
-
knex: 操作數據庫,支持多種數據庫,這里使用mysql,需要mysql中間件 開發參考knex筆記
搭建開發環境
- 在項目根目錄下
npm init
一路回車,初始化項目環境,出現package.json文件,然后執行以下命令安裝項目依賴
npm i --save cheerio superagent superagent-charset koa-router koa knex mysql
- 在項目根目錄下創建app.js文件,編寫coding
const Koa = require('koa'),
Router = require('koa-router'),
cheerio = require('cheerio'),
charset = require('superagent-charset'),
superagent = charset(require('superagent')),
app = new Koa(),
router = new Router();
- 然后編寫路由和搭建服務器環境
router.get('/', function(ctx, next) {
ctx.body = "搭建好了,開始吧";
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3010, () => {
console.log('[服務已開啟,訪問地址為:] http://127.0.0.1:3010/');
});
- 啟動服務
node app.js
, 打開瀏覽器http://127.0.0.1:3010/ 就可以訪問了 如看到搭建好了,開始吧
就意味着搭建環境success
爬蟲-目標網站
const Koa = require('koa'),
Router = require('koa-router'),
cheerio = require('cheerio'),
charset = require('superagent-charset'),
superagent = charset(require('superagent')),
app = new Koa(),
router = new Router();
let arr;
router.get('/', (ctx, next) => {
url = 'http://shop.bytravel.cn/produce/index226.html'; //target地址
superagent.get(url)
.charset('gbk') // 當前頁面編碼格式
.buffer(true)
.end((err, data) => { //頁面獲取到的數據
if (err) {
// return next(err);
console.log('頁面不存在', err)
}
let html = data.text,
$ = cheerio.load(html, {
decodeEntities: false,
ignoreWhitespace: false,
xmlMode: false,
lowerCaseTags: false
}), //用cheerio解析頁面數據
obj = {};
arr = [];
// cheerio的使用類似jquery的操作
$("table tbody").each((index, element) => {
let $element = $(element);
$element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
arr.push({
'title': $element.find('a.blue14b').text(),
'image': $element.find('#bright img').attr('src'),
'summary': $element.find('#tctitle').next().text(),
'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
})
})
})
ctx.body = arr
// console.log(arr)
})
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3010, () => {
console.log('[服務已開啟,訪問地址為:] http://127.0.0.1:3010/');
});
命令行重啟服務
node app.js
, 頁面出現一個數組,有大量數據,success;
- 注: 如果出現亂碼,可能就是代碼的編碼格式和要抓取目標頁面的編碼格式不一樣導致的,需要留心下;
- 當前目標網站編碼-- gb2312
- 如果是utf-8 可以 不使用 superagent-charset
superagent.get(url)
.charset('gbk') // 當前頁面編碼格式
.buffer(true)
.end(async (err, data) => { //頁面獲取到的數據
······
})
分析頁面數據
- 通過cheerio在服務器端需要對DOM進行操作解析頁面獲取數據
$("table tbody").each((index, element) => {
let $element = $(element);
$element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text(''); //去掉簡介中鏈接接【詳情】
arr.push({
'title': $element.find('a.blue14b').text(),
'image': $element.find('#bright img').attr('src'),
'summary': $element.find('#tctitle').next().text(),
'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
})
})
數據庫
sudo npm install knex mysql --save
當前自定義數據庫
native_symbol
,數據表products
CREATE DATABASE native_symbol;
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號',
`title` varchar(100) NOT NULL COMMENT '名稱',
`image` varchar(100) NOT NULL COMMENT '圖片',
`summary` varchar(1000) NOT NULL COMMENT '簡介',
`tags` varchar(100) DEFAULT NULL COMMENT '標簽',
`is_cgiia` tinyint(1) DEFAULT NULL COMMENT '是否是地標特產',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
- 初始化(knex配置連接)
var knex = require('knex')({
client: 'mysql', //指明數據庫類型,還可以是pg,sqlite3等等
connection: { //指明連接參數
host: '127.0.0.1',
user: 'root',
password: 'root',
database: 'native_symbol'
},
debug: true, //指明是否開啟debug模式,默認為true表示開啟
pool: { //指明數據庫連接池的大小,默認為{min: 2, max: 10}
min: 0,
max: 7,
},
acquireConnectionTimeout: 10000, //指明連接計時器大小,默認為60000ms
migrations: {
tableName: 'migrations' //數據庫遷移,可選
}
});
把數據庫類型和連接相關的參數配置好之后,才可以正確的連接到數據庫,connection的配置信息通常寫到config文件中。
目前node開發服務端最優解決異步回調是
koa2 + es7(async/await)
- 例如;向users表中寫入數據
// 寫入庫
knex('users')
.returning('id')
.insert({
name: 'charblus',
age: 18,
sex: 1
})
.then(res => {
console.log('success', res)
})
}
$("table tbody").each((index, element) => {
let $element = $(element);
$element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
arr.push({
'title': $element.find('a.blue14b').text(),
'image': $element.find('#bright img').attr('src'),
'summary': $element.find('#tctitle').next().text(),
'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
})
})
for (let i of arr) {
const findRes = await knex('products').select().where('title', i.title)
if (findRes.length) {
console.log('數據已存在')
} else {
// 寫入庫
await knex('products')
.returning('id')
.insert(i)
.then(res => {
console.log('success', res)
})
}
}
這里讀寫數據庫是異步操作 使用
async/await
, 如上knex讀寫數據庫時都用了await
,需要在當前函數前加async
- 根據目標網站鏈接的特性,這里加了個定時器,修改URL地址,並重新superagent請求數據,cheerio分析數據,knex存入數據
app.js
const Koa = require('koa'),
Router = require('koa-router'),
cheerio = require('cheerio'),
charset = require('superagent-charset'),
superagent = charset(require('superagent')),
app = new Koa(),
router = new Router();
let arr;
var knex = require('knex')({
client: 'mysql', //指明數據庫類型,還可以是pg,sqlite3等等
connection: { //指明連接參數
host: '127.0.0.1',
user: 'root',
password: 'root',
database: 'native_symbol'
},
debug: true, //指明是否開啟debug模式,默認為true表示開啟
pool: { //指明數據庫連接池的大小,默認為{min: 2, max: 10}
min: 0,
max: 7,
},
acquireConnectionTimeout: 10000, //指明連接計時器大小,默認為60000ms
migrations: {
tableName: 'migrations' //數據庫遷移,可選
}
});
var idx = 100;
router.get('/', (ctx, next) => {
var timer = setInterval(() => {
idx++;
if (idx > 10000) {
clearInterval(timer)
return
}
url = `http://shop.bytravel.cn/produce/index${idx}.html`; //爬蟲地址
timePlay(url)
console.log('頁面抓包記錄', idx)
}, 100);
timePlay = (url) => {
superagent.get(url)
.charset('gbk')
.buffer(true)
.end(async (err, data) => { //頁面獲取到的數據
// if (err) {
// // return next(err);
// console.log('頁面不存在', err)
// }
let html = data.text,
$ = cheerio.load(html, {
decodeEntities: false,
ignoreWhitespace: false,
xmlMode: false,
lowerCaseTags: false
}), //用cheerio解析頁面數據
obj = {};
arr = [];
$("table tbody").each((index, element) => {
let $element = $(element);
$element.find('#tctitle').next().find('a').addClass('link').attr('class', 'link').text('')
arr.push({
'title': $element.find('a.blue14b').text(),
'image': $element.find('#bright img').attr('src'),
'summary': $element.find('#tctitle').next().text(),
'is_cgiia': $element.find('#tctitle font').attr('color') === 'green' ? 1 : 0
})
})
for (let i of arr) {
const findRes = await knex('products').select().where('title', i.title)
if (findRes.length) {
console.log('數據已存在')
} else {
// 寫入庫
await knex('products')
.returning('id')
.insert(i)
.then(res => {
console.log('success', res)
})
}
}
});
}
ctx.body = arr;
// console.log(arr)
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3010, () => {
console.log('[服務已開啟,訪問地址為:] http://127.0.0.1:3010/');
});