这个网站有反爬机制,使用 axios 请求直接返回 403,用 puppeteer 爬取也会很快就给你报超时,我就爬取了 80 多条数据,就请求不下来了。这里记录一下我的代码,基本和之前的代码差不多,只是多做了一些处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
let puppeteer = require('puppeteer')
let url = require('url')
let mysql = require('mysql')

// 目标:抓取https://sobooks.cc/网站下 所有电子书的书名和下载链接
// 实现思路:
// 1.进入网站,获取整个网站的列表页的页数
// 2.获取列表页所有的连接
// 3.进入每个电子书的详情页获取下载电子书的网盘地址
// 4.将获取的数据保存到book.txt

// 目标地址:
let httpUrl = 'https://sobooks.cc/'
let sqlOptions = {
host: 'localhost',
port: '3306',
user: 'root',
password: '123456',
database: 'book',
}
let con = mysql.createConnection(sqlOptions)
con.connect()
// 浏览器
;(async function () {
// 配置options
let debugOptions = {
// 设置视窗宽高
defaultViewport: {
width: 1400,
height: 720,
},
// 设置为有界面,无界面为true
headless: false,
// 设置每个每个步骤的延迟时间
slowMo: 300,
}
let options = { headless: true }
// 启动浏览器
let browser = await puppeteer.launch(options)

function wait(millSeconds) {
return new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('成功执行延迟函数,延迟时间:' + millSeconds)
}, millSeconds)
})
}

async function getAllNum(link) {
let page = await browser.newPage()
await page.goto(link)
await page.setRequestInterception(true)
// 监听请求事件,并对请求进行拦截
page.on('request', (interceptedRequest) => {
// 通过URL模块对请求的地址进行解析
let urlObj = url.parse(interceptedRequest.url())
if (urlObj.hostname == 'googleads.g.doubleclick.net') {
// 如果是谷歌的广告太慢了
interceptedRequest.abort()
} else {
interceptedRequest.continue()
}
})
// 获取总页数
let pageNum = await page.$eval(
'.pagination li:last-child span',
(element) => {
let text = element.innerHTML
.substring(1, element.innerHTML.length - 2)
.trim()
return text
}
)
return pageNum
}

// 进入列表页
async function pageList(num) {
let pageListUrl = 'https://sobooks.cc/page/' + num
// 打开一个新页面
let page = await browser.newPage()
// 在新页面中访问列表页地址
await page.goto(pageListUrl)
await page.setRequestInterception(true)
// 监听请求事件,并对请求进行拦截
page.on('request', (interceptedRequest) => {
// 通过URL模块对请求的地址进行解析
let urlObj = url.parse(interceptedRequest.url())
if (urlObj.hostname == 'googleads.g.doubleclick.net') {
// 如果是谷歌的广告太慢了
interceptedRequest.abort()
} else {
interceptedRequest.continue()
}
})
let arrPage = await page.$$eval(
'.card .card-item .thumb-img>a',
(elements) => {
let arr = []
elements.forEach((element, i) => {
var obj = {
href: element.getAttribute('href'),
title: element.getAttribute('title'),
}
arr.push(obj)
})
return arr
}
)
page.close()
// 通过获取的数组的地址和标题请求详情页
arrPage.forEach(async (pageObj, i) => {
await wait(500 * i)
getPageInfo(pageObj)
})
}

async function getPageInfo(pageObj) {
let page = await browser.newPage()
await page.goto(pageObj.href)
// 截取谷歌请求
await page.setRequestInterception(true)
// 监听请求事件,并对请求进行拦截
page.on('request', (interceptedRequest) => {
// 通过URL模块对请求的地址进行解析
let urlObj = url.parse(interceptedRequest.url())
if (urlObj.hostname == 'googleads.g.doubleclick.net') {
// 如果是谷歌的广告太慢了
interceptedRequest.abort()
} else {
interceptedRequest.continue()
}
})
let eleA = await page.$('.dltable tr:nth-child(3) a:last-child')
let aHref = await eleA.getProperty('href')
aHref = aHref._remoteObject.value
// 书籍链接地址
let bookUrl = pageObj.href
// 书籍下载地址
let download = aHref.split('?url=')[1]
// 书籍名字
let bookname = pageObj.title
// 分类
let cataory = await page.$eval('.meta .muted:nth-child(1) a', (element) => {
let cataory = element.innerHTML
return cataory
})
// 作者
let author = await page.$eval('.meta .muted:nth-child(2) a', (element) => {
let author = element.innerHTML
return author
})
// 书籍图片
let bookimg = await page.$eval(
'.article-content .bookpic img',
(element) => {
let bookimg = element.getAttribute('src')
return bookimg
}
)
// 浏览次数
let viewcount = await page.$eval(
'.article-content .bookinfo li:nth-child(3)',
(element) => {
let viewcount = element.innerHTML
let reg = /<strong>浏览:<\/strong>(.*?)次/gis
viewcount = reg.exec(viewcount)[1]
return viewcount
}
)
// 标签
let tag = await page.$$eval(
'.article-content .bookinfo li:nth-child(4) a',
(elements) => {
let arr = []
elements.forEach((element, i) => {
let tag = element.innerHTML
arr.push(tag)
})
return arr
}
)
// 时间
let pubtime = await page.$eval(
'.article-content .bookinfo li:nth-child(5)',
(element) => {
let pubtime = element.innerHTML.split('<strong>时间:</strong>')[1]
return pubtime
}
)
// 简介
let brief = await page.$eval('.article-content>p', (element) => {
let brief = element.innerHTML
return brief
})
let infoList = [
bookname,
author,
viewcount,
`${tag}`,
pubtime,
bookimg,
cataory,
brief,
bookUrl,
download,
]
// 插入数据库
let strSql =
'insert into book (bookname,author,viewcount,tag,pubtime,bookimg,cataory,brief,bookUrl,download) values (?,?,?,?,?,?,?,?,?,?)'
con.query(strSql, infoList, (err, res) => {
console.log(err)
console.log(res)
})
}
async function spider(link) {
let allPageNum = await getAllNum(link)
console.log('成功获取页面总数:' + allPageNum)

for (let i = 1; i <= allPageNum; i++) {
await wait(4000 * i) // 每个页面延迟3秒
pageList(i)
}
}
spider(httpUrl)
})()

b2dd5cb1d44b978a4ddb35e1556f4df