持续创作,加速生长!这是我参加「日新计划 6 月更文应战」的第20天,点击查看活动概况
爬虫原理
爬取数据其实便是向对应的服务器发送恳求,相当于用浏览器拜访页面,这样会得到一个html的字符串,然后对字符串进行剖析,获取咱们想要的数据。
对于nodejs编写的爬虫来说,主要用的java怎么读两个的库是s服务器系统uperagent和cheerio。
- superagent:是用nodejs的API封装的http恳求库,即能够用在服务端也能够用在浏览器端。
- cheerio: 是jqueelement滑板ry中心功用的一个快速灵敏而又简练的完成,主要是为了用在服务器端需求对D服务器操作系统OM进行操作的当地,让你在服务器端和html愉快的玩耍。
关于JSDOM
JSDOM是一个纯粹由 javascript 完成的一系列 web 标准,专门java培训在 nodejs 中运用,方elementary翻译针是模拟一个浏览器环境,以便用于测验和发掘真实世界的 web 应用程序。java面试题也便是说 JSDOM 是一个nodejs版的浏览器。
cheerio 发生的原因是出于对JSDOM的绝望:主要体现在以下几点:
- JSDOM太过于重量级:JSDOM的方针是供给与浏览器相同的DOM环境,可是咱们往往不需求这样。咱们需求的仅仅一种简略,熟悉的办法来操作咱们的HTM爬虫代码L。
- JSDOM太elementui慢了:用JSDOM解析大的网站乃至能够发生可察觉的推迟
cheerio并非万能,当你需求一个浏览器相同的环境时,你最好还是用Jjava语言SDOM,尤其是你需求进行自动化的功用测验时。
第一版爬虫
写爬虫分为两步:一是获取网站元素html信息;二是对信息进行剖析。
前面的文章具体学习了java语言规划形式,下面遵循单一职责准则来写爬虫element是什么意思功用,即一个函数只做一件事工作:
- 运用superajava模拟器gent库发送恳求,获取网页html信息
- 运用chee设计模式23模式介绍rio来提取html中有用的信息
- 把获取的信息存储在一个文件中,分为两步:首要服务器地址按格局要求生成存储的信息,然后写入到对应爬虫搜索引擎的文件中服务器内存条可以用在台式机上吗
import superagent from 'superagent'
import cheerio from 'cheerio'
import path from 'path'
import fs from 'fs'
interface ICourse {
title: string
count: number
}
interface ICourseResult {
time: number
data: ICourse[]
}
interface IContent {
[prop: number]: ICourse[]
}
class Crowller {
private url = 'http://localhost:3004'
private filePath = path.resolve(__dirname, '../data/course.json')
constructor() {
this.init()
}
async init() {
// 运用superagent库发送恳求,获取网页html信息
const html = await this.getRawHtml()
// 运用cheerio来提取html中有用的信息
const courseResult = this.getCourseInfo(html)
// 把获取的信息存储在一个文件中,分为两步:首要生成存储的信息,然后写入到对应的文件中
const content = this.genCourseContent(courseResult)
this.writeFile(JSON.stringify(content))
}
async getRawHtml() {
const result = await superagent.get(this.url)
return result.text
}
getCourseInfo(html: string): ICourseResult {
const $ = cheerio.load(html)
const courseItems = $('li')
const courseInfos: ICourse[] = []
courseItems.map((index, element) => {
const title = $(element).find('.title').text()
const count = parseInt($(element).find('.study').text().split(':')[1])
courseInfos.push({
title,
count
})
})
return {
time: Date.now(),
data: courseInfos
}
}
genCourseContent(courseInfo: ICourseResult): IContent {
let fileContent: IContent = {}
if (fs.existsSync(this.filePath)) {
fileContent = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'))
}
fileContent[courseInfo.time] = courseInfo.data
return fileContent
}
writeFile(content: string) {
fs.writeFileSync(this.filePath, content, 'utf-8')
}
}
第二版爬虫
第一版爬虫存在如下问题:
- 爬取的url写死了,咱们需求外部传入。
- 获取hjava编译器tml的信息,以及把信息写入到文件中是爬虫公共的办法,可是提取有用的信息和生成特殊格局的存储信息每个用户element滑板的需求不相同,所以要element翻译把这两个功用独自提取出来。
crowller.ts
import superagent from 'superagent'
import path from 'path'
import fs from 'fs'
import CourseAnalyzer from './courseAnalyzer'
export interface IAnalyzer {
analyze: (html: string, filePath: string) => string
}
class Crowller {
private filePath = path.resolve(__dirname, '../data/course.json')
constructor(private url: string, private analyzer: IAnalyzer) {
this.init()
}
async init() {
const html = await this.getRawHtml()
const content = this.analyzer.analyze(html, this.filePath)
this.writeFile(content)
}
async getRawHtml() {
const result = await superagent.get(this.url)
return result.text
}
writeFile(content: string) {
fs.writeFileSync(this.filePath, content, 'utf-8')
}
}
// 爬取的url由外面传入
const url = 'http://localhost:3004'
const analyzer = new CourseAnalyzer()
new Crowller(url, analyzer)
courseAnalyz爬虫软件er.ts
import cheerio from 'cheerio'
import fs from 'fs'
import { IAnalyzer } from './crowller'
interface ICourse {
title: string
count: number
}
interface ICourseResult {
time: number
data: ICourse[]
}
interface IContent {
[prop: number]: ICourse[]
}
class CourseAnalyzer implements IAnalyzer {
private getCourseInfo(html: string): ICourseResult {
const $ = cheerio.load(html)
const courseItems = $('li')
const courseInfos: ICourse[] = []
courseItems.map((index, element) => {
const title = $(element).find('.title').text()
const count = parseInt($(element).find('.study').text().split(':')[1])
courseInfos.push({
title,
count
})
})
return {
time: Date.now(),
data: courseInfos
}
}
private genCourseContent(
courseInfo: ICourseResult,
filePath: string
): IContent {
let fileContent: IContent = {}
if (fs.existsSync(filePath)) {
fileContent = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
}
fileContent[courseInfo.time] = courseInfo.data
return fileContent
}
analyze(html: string, filePath: string) {
const result = this.getCourseInfo(html)
const content = this.genCourseContent(result, filePath)
return JSON.stringify(content)
}
}
export default CourseAnalyzer
改进点:
- 爬取的url由用户自己传入
- 把公共办法
getRawHtml设计模式的集体教学活动方案
,writeFile
仍然写在crowller类中,把需求不固定的办法getCo设计模式urseInfo
,genCourseContent
独自拆分出来放在CourseAnalyzer爬虫软件
类中,一elementary是什么意思起对外露出analyze
办法 - 在运用的时分,实例化一个Couelements中文翻译rseAnalyzer类,然后把它传入到cro设计模式之禅wller的实例当中,然后调用CourseAnalyzer.analyze办法获取爬取的有用信息
const url = 'http://localhost:3004'
const analyzer = new CourseAnalyzer()
new Crowller(url, analyzer)
假如这个时分你想爬取另爬虫是什么外一个网站,这个时分你只需求重elementary翻译新写一个类似CourseJavaAnalyzer类即可,比如:
import { IAnalyzer } from './crowller'
class FoodAnalyzer implements IAnalyzer {
analyze(html: string, filePath: string) {
// 写你需求的逻辑即可
return ''
}
}
export default FoodAnalyzer
const url = 'http://localhost:3005'
const analyzer = new FoodAnalyzer()
new Crowller(url, analyzer)
这种形式叫做组合规划形式。
上面有几个点需求留意:
- 咱们运用一个接口代替类作为类型,这样服务器内存和台式机内存区别更加通用java环境变量配置。假如你引入了一个类的实例作为变量,当给这个变量指定类型的时分,怎么办呢?咱们就能够运用接口interface来进行,具体见代码:
export interface IAnalyzer {
analyze: (html: string, filePath: string) => string
}
constructor(private url: string, private analyzer: IAnalyzer) {
this.init()
}
- 组合规划形式:咱们把爬虫的完成和数据服务器租用的剖析分红两个类来运用,服务器地址这样便是完成了一定程度的解耦,当后期修改剖析办法的时分,就能够不同改动爬虫里边的代码,只爬虫python入门需求再写一个剖析办法即可。
第三版爬虫
咱们把CourelementaryseAnalyzer
称之为剖析器,在运用的时分咱们能够屡次 new 许多实例的,可是这是没有必要的,所以咱们需求把CourseAnalyzer
改造成单例形式。
class CourseAnalyzer implements IAnalyzer {
private static instance: CourseAnalyzer
static getInstance() {
if (!this.instance) {
this.instance = new CourseAnalyzer()
}
return this.instance
}
private constructor() {}
}
const analyzer = CourseAnalyzer.getInstance()