基于 Go
语言的分布式网页爬虫。
爬取数据后可使用 Hello Search 进行搜索。
此项目初衷在于学习
Go
语言以及Elasticsearch
。
- 将种子 URL 入队。
- 从队列获取待抓取 URL。
- 判断此 URL 是否有效。(包括检查 URL 是否已经被抓取过)
- 抓取网页信息,并记录此 URL 已经被抓取。
- 储存网页摘要,并将此网页的超链接(URL)进行入队。
- 执行步骤
2
。
- 使用
RabbitMQ
消息队列实现分布式下的 URL 队列的持久化、优先级等。 - 使用
RedisBloom
的布隆过滤器实现分布式下的 URL 去重。 - 使用
goquery
解析 HTML。 - 使用
Elasticsearch
储存数据。
另外可以使用 Kibana
对抓取状况进行实时的可视化分析。
go version go1.16.3
RabbitMQ 3.8.12
RedisBloom latest
$ go run . -h
Usage of hellospider:
-config string
File path of configuration. (default "config.json")
-namespace string
Namespace of task.
-priority string
Priority policy: 0-9 means that the priority is constant, url-len means that the priority is calculated according to the length of the URL (the shorter the priority),
path-len means that the priority is calculated according to the length of the URL path (the shorter the priority).
-reset
Reset queue, storage and filter before begin task.
-seed string
The seeds URL is comma-separated. Such as: 'https://a.com/, https://b.com/'. And the seeds in the configuration file will be ignored.
-config
配置文件路径-namespace
命名空间(区分不同任务)-priority
优先级策略:0-9 表示优先级为常数,url-len 表示根据 URL 长度计算优先级(越短越优先),path-len 表示根据 URL 路径长度计算优先级(越短越优先)。-reset
开始前是否重置命名空间-seed
替换配置文件中的 URL 种子(英文逗号分隔)
修改配置文件 config.json
中各服务的地址、端口以及用户名及密码等。
{
"namespace": "default",
"workers": 8,
"priority": "path-len",
"seeds": [
"https://bing.com/"
],
"redis": {
"host": "localhost:6379",
"auth": null
},
"rabbitMq": {
"url": "amqp://guest:guest@localhost:5672/",
"exchange": "spider",
"maxLength": 999999999
},
"elasticsearch": {
"address": [
"http://localhost:9200"
],
"username": "elastic",
"password": "123456"
},
"accepts": [
"text/html",
"text/plain"
],
"rules": {
"allows": [
".*"
],
"forbid": null
},
"userAgents": [
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
"..."
],
"responseHeaders": [
"Content-Type",
"Content-Length",
"Content-Language",
"Server",
"X-Powered-By"
]
}
go run .
./hellospider
Go 语言默认字符编码为 UTF-8
,在解析网页时为了避免乱码需要判断该网页的字符编码并进行转换。目前发现包含字符编码的内容如下:
- HTTP 响应头中的
Content-Type
,如:text/html; charset=gbk
- HTML 头部的 meta 标签,如:
<meta charset='gbk'>
,<meta http-equiv='Content-Type' content='text/html; charset=gb2312'>
如果在响应头中能够判断字符编码,则直接将 Reader
进行转换即可。否则需要先解析 HTML,判断后再进行转换。后者需要储存响应的数据再次构造 Reader
以提供重复读取。
每抓取一个网页,往往会产生几十个新的 URL,如果不为 URL 设定优先级策略,或许很难达到预期的结果。
URL 无优先级时,根据队列先进先出的特性,搜索将会是广度优先。 此时的广度优先并非指的是网站广度,而是相当于树的层次遍历。 搜寻结果根据网页的链接相关性有不同结果。
如果网页内的大部分链接都指向某一个网站,假设是它本站,那么抓取的网页大部分将是此网站的内容或者是与其密切相关的其他站点的内容。
此策略适合挖掘某个站点
一个 URL 越长,通常可能表示它处在站点越深的地方。在某些搜索场景下,它可能并不那么重要。反过来,一个 URL 越短那么它就处于站点越浅的地方,对于想要发现更多站点的搜索场景来说更好。
简单通过一万条 URL 的采样发现 URL 的平均全长在 70 左右,仅 URI 路径的平均长度在 35 左右。
简单设计一个优先级函数:f(x)=(e^((-(x-340))/50))/100
函数图像:
经过实验发现,使用此优先级函数进行的任务能够抓取更多的网站(域名)。
此策略适合快速发现更多站点
- 入队前筛选有效 URL ,避免消息队列臃肿。 √
- 使用协程并发抓取。 √
- 消息队列 Qos。 √
- HTTP 连接复用。 ×
- 维护本地布隆过滤器,避免频繁查询 RedisBloom。×
- HTTP 重定向时,将重定向过程中的 URL 也加入过滤器。 ×
RedisBloom
用于分布式下的 URL
去重。
使用 RabbitMQ
存放待抓取的 URL
,并从队列获取 URL
进行消费。
使用 Elasticsearch
存储网页数据。
使用 Kibana
进行可视化分析。