跳转到主要内容

用于获取DNS域及其相关服务的分布式爬虫。

项目描述

dns-crawler

用于获取(可能数量庞大的)DNS域信息的爬虫

CZ.NIC       ADAM

PyPI version shields.io PyPI pyversions PyPI license PyPI downloads per week

它做什么?

尽管名称如此,该爬虫获取的信息远不止DNS服务

  • DNS
    • 所有A/AAAA记录(对于二级域名和“www.”子域名),可选地带有GeoIP注释
    • TXT记录(带有解析SPF和DMARC以简化过滤)
    • TLSA(对于二级域名和“www.”子域名)
    • MX
    • DNSSEC验证
    • 名称服务器
      • 每个服务器IP可选地带有GeoIP注释
      • HOSTNAME.BIND, VERSION.BIND, AUTHORS.BIND和fortune(以及所有IP)
    • 用户可以在配置文件中添加自定义的附加RR
  • 电子邮件(来自MX记录的每个服务器)
    • SMTP服务器横幅(可选,端口可配置)
    • TLSA记录
  • 网页
    • 每个IP上80和443端口的HTTP状态和头信息(包括解析的cookies)
    • HTTPS证书信息(可选,包括整个证书链)
    • 网页内容(可选)
    • 上述所有内容都保存为重定向历史中的每个步骤 – 爬虫会跟随重定向,直到它获得非重定向状态或达到可配置的限制

域名服务器和邮件服务器的答案被缓存,因此爬虫不会对托管提供商进行重复查询。

如果您需要配置防火墙,爬虫将连接到端口53(UDP和TCP),25(TCP),80(TCP),和443(目前为TCP,但我们可能会添加UDP与HTTP3…)。

查看result-example.json以了解生成的JSON的外观。

它到底有多快?

一台相当现代的笔记本电脑在约50Mbps的连接下,可以整夜爬取整个.cz区域(约130万个二级域名),具体时间可能会有所不同,使用每个CPU线程8个工作者。

由于爬虫被设计成并行,实际速度几乎完全取决于工作者数量。它几乎可以无限地跨多台机器扩展,所以如果您需要在1小时内爬取一百万个域名,您总是可以简单地投入更多硬件(见下文)。

CZ.NIC在生产中使用4台机器(8核Xeon Bronze 3106,16 GB RAM,千兆线)来爬取整个.cz区域,耗时不到3小时。

安装

创建并激活一个虚拟环境

mkdir dns-crawler
cd dns-crawler
python3 -m venv .venv
source .venv/bin/activate

安装dns-crawler

pip install dns-crawler

根据您的操作系统/发行版,您可能需要安装一些系统软件包。在Debian/Ubuntu上,apt install libicu-dev pkg-config build-essential python3-dev应该可以解决问题(当然,假设您已经安装了python3)。

基本使用

要运行单线程爬虫(适用于少量域名),只需传递一个域名列表

$ echo -e "nic.cz\nnetmetr.cz\nroot.cz" > domain-list.txt
$ dns-crawler domain-list.txt > results.json
[2019-12-03 11:03:54] Reading domains from domain-list.txt.
[2019-12-03 11:03:54] Read 3 domains.
[2019-12-03 11:03:55] 1/3
[2019-12-03 11:03:55] 2/3
[2019-12-03 11:03:56] 3/3
[2019-12-03 11:03:56] Finished.

结果打印到stdout - 每个域的JSON,由\n分隔

$ cat results.json
{"domain": "nic.cz", "timestamp": "2019-12-03 10:03:55", "results": {…}}
{"domain": "netmetr.cz", "timestamp": "2019-12-03 10:03:55", "results": {…}}
{"domain": "root.cz", "timestamp": "2019-12-03 10:03:56", "results": {…}}

如果您想获得格式化的JSON,只需将输出通过jq或您选择的工具:dns-crawler domain-list.txt | jq

多线程爬取

首先,您需要一个正在运行并监听的Redis服务器。

爬虫可以运行多个线程以加快处理大量域名的速度。控制器和工作者之间的通信通过Redis完成(这使得如果需要,在多台机器上运行工作者变得容易,见下文)。

启动Redis。具体命令取决于您的系统。如果您想使用不同的机器来运行Redis和爬虫控制器,请参阅dns-crawler控制器的CLI参数

将域名喂入队列并等待结果

$ dns-crawler-controller domain-list.txt > result.json

(在另一个shell中)启动工作者,处理域名并将结果返回给控制器

$ dns-crawler-workers

使用控制器还可以免费获得重复查询的缓存(邮件服务器横幅和nameservers的hostname.bind/version.bind)。

Redis配置

不需要特殊配置,但如果有大量域名要处理,请增加内存限制(例如maxmemory 2G)。您还可以禁用磁盘快照以节省一些I/O时间(取消注释save …行)。如果您还没有使用Redis做其他事情,请阅读它的日志 – 常常有关于性能改进的建议。

结果

结果打印到主进程的stdout(dns-crawlerdns-crawler-controller) – 每个域的JSON,由\n分隔

…
[2019-05-03 07:38:17] 2/3
{"domain": "nic.cz", "timestamp": "2019-09-24T05:28:06.536991", "results": {…}}
…

进度信息带时间戳打印到stderr,因此您可以轻松保存输出 – dns-crawler list.txt > results

输出JSON的JSON模式已包含在仓库中: result-schema.json,以及nic.cz的示例: result-example.json

有多个用于模式验证、查看甚至代码生成的工具。

要验证结果与模式(CI已配置自动执行)

$ pip install check-jsonschema
$ check-jsonschema --schemafile result-schema.json result-example.json

或者,如果你不讨厌JS,ajv有更好的输出

$ npm i -g ajv-cli
$ ajv validate -s result-schema.json -d result-example.json

存储爬虫结果

在生产中,CZ.NIC使用Hadoop集群在爬虫运行完成后存储结果文件——见utils/crawler-hadoop.sh脚本(将结果文件推送到Hadoop并通知Mattermost频道)。

你甚至可以直接将输出重定向到Hadoop,而不需要在你的磁盘上存储它

dns-crawler-controller domain-list.txt | ssh user@hadoop-node "HADOOP_USER_NAME=… hadoop fs -put - /path/to/results.json;"

处理结果

在Python代码中的使用

只需像这样导入并使用process_domain函数:

$ python
>>> from dns_crawler.crawl import process_domain
>>> result = process_domain("nic.cz")
>>> result
{'domain': 'nic.cz', 'timestamp': '2019-09-13T09:21:10.136303', 'results': { … 
>>>
>>> result["results"]["DNS_LOCAL"]["DNS_AUTH"]
[{'value': 'a.ns.nic.cz.'}, {'value': 'b.ns.nic.cz.'}, {'value': 'd.ns.nic.cz.'}]

process_domain函数返回Python dict。如果你需要JSON,请使用from dns_crawler.crawl import get_json_result

$ python
>>> from dns_crawler.crawl import get_json_result
>>> result = get_json_result("nic.cz")
>>> result
# same as above, just converted to JSON

此函数只是调用crawl_domain并将dict转换为JSON字符串。它由工作者使用,因此转换由他们完成,以减轻控制器进程的压力。

配置文件

GeoIP数据库路径、DNS解析器IP、超时以及其他一些内容来自工作目录中的config.yml(如果存在)。

默认值列在config.yml中,其中包含解释性注释。(config.yml

如果你使用多线程爬虫(dns-crawler-controllerdns-crawler-workers),配置由控制器加载并与工作者通过Redis共享。

如果需要,你可以在工作者机器上覆盖它——只需在工作目录中创建一个config.yml(例如,为设置不同的解析器IP或GeoIP路径在每个机器上)。然后配置将被合并——未在工作者配置中定义的指令将从控制器中加载(如果在那里也没有定义,则使用默认值)。但是——根据你更改的值——你可能会从每个工作者机器得到不同的结果。

GeoIP注释

为此功能正常工作,你需要获取爬虫使用的GeoIP数据库。它支持付费和免费数据库(注册后可在此下载)。

爬虫期望它们在/usr/share/GeoIP(Maxmind的geoipupdate默认将其放置在那里),但这可以在配置文件中轻松更改

geoip:
  enabled: True
  country: /path/to/GeoLite2-Country.mmdb
  asn: /path/to/GeoLite2-ASN.mmdb

使用商业(GeoIP2 Country和ISP)数据库而不是免费(GeoLite2 Country和ASN)数据库

geoip:
  enabled: True
  country: /usr/share/GeoIP/GeoLite2-Country.mmdb
  #  asn: /usr/share/GeoIP/GeoLite2-ASN.mmdb  # 'asn' is the free DB
  isp: /usr/share/GeoIP/GeoIP2-ISP.mmdb  # 'isp' is the commercial one

(使用绝对路径或相对于工作目录的路径)

如果同时定义了,则首选付费的ISP(付费)数据库而不是免费的ASN(免费)数据库。差异在Maxmind网站上描述:https://dev.maxmind.com/faq/what-is-the-difference-between-the-geoip-isp-and-organization-databases/

免费的GeoLite2-Country似乎有点不准确,特别是在IPv6的情况下(它将一些CZ.NIC名称服务器放置在乌克兰等地方)。

获取额外的DNS资源记录

你可以轻松获取一些额外的RR(对于二级域名),这些RR默认情况下不包括在爬虫中

dns:
  additional:
    - SPF
    - CAA
    - CERT
    - LOC
    - SSHFP

DNS记录类型列表以获得一些想法。但是,像OPENPGPKEY这样的东西将不起作用,因为它们旨在在子域中使用(在这种情况下,它们是电子邮件地址部分的散列)。

您可以通过向dns_utils.py文件中的additional_parsers枚举中添加一个函数来为记录插入解析器。默认情况下只包含SPF(因为已弃用的SPF记录格式与TXT中的SPF格式相同,而爬虫默认获取的就是TXT中的SPF)。

命令行参数

dns-crawler

dns-crawler - a single-threaded crawler to process a small number of domains without a need for Redis

Usage: dns-crawler <file>
       file - plaintext domain list, one domain per line, empty lines are ignored

dns-crawler-controller

dns-crawler-controller - the main process controlling the job queue and printing results.

Usage: dns-crawler-controller <file> [redis]
       file - plaintext domain list, one domain per line, empty lines are ignored
       redis - redis host:port:db, localhost:6379:0 by default

Examples: dns-crawler-controller domains.txt
          dns-crawler-controller domains.txt 192.168.0.22:4444:0
          dns-crawler-controller domains.txt redis.foo.bar:7777:2
          dns-crawler-controller domains.txt redis.foo.bar # port 6379 and DB 0 will be used if not specified

当您提供大量域名(>1000×CPU核心数)时,控制器进程会使用线程(每个CPU核心4个)来更快地创建作业。

在(更)现代的机器上运行速度要快得多——例如,笔记本电脑中的i7-7600U(带HT)每秒大约处理19k个作业,而服务器上的Xeon X3430(不带HT)仅约处理7k个(两者都使用16个线程,因为它们都显示为4个核心)。

要取消进程,只需发送一个终止信号或在任何时候按Ctrl-C。进程将执行清理并退出。

dns-crawler-workers

dns-crawler-workers - a process that spawns crawler workers.

Usage: dns-crawler-workers [count] [redis]
       count - worker count, 8 workers per CPU core by default
       redis - redis host:port:db, localhost:6379:0 by default

Examples: dns-crawler-workers 8
          dns-crawler-workers 24 192.168.0.22:4444:0
          dns-crawler-workers 16 redis.foo.bar:7777:2
          dns-crawler-workers 16 redis.foo.bar # port 6379 and DB 0 will be used if not specified

尝试为每个CPU核心使用超过24个工作者将导致警告(并在实际启动工作者之前开始倒计时)

$ dns-crawler-workers 999
Whoa. You are trying to run 999 workers on 4 CPU cores. It's easy toscale
across multiple machines, if you need to. See README.md for details.

Cancel now (Ctrl-C) or have a fire extinguisher ready.
5 - 4 - 3 -

停止工作与控制器进程相同——按Ctrl-C(或终止信号)将完成当前作业并退出。

恢复工作

停止工作者不会从Redis中删除作业。因此,如果您停止dns-crawler-workers进程然后启动一个新的进程(可能是因为要使用不同的工作者数量...),它将获取未完成的作业并继续。

这也可以用来调整工作者数量,如果发现对于您的机器或网络来说太低或太高

  • 要减少工作者数量,只需停止dns-crawler-workers进程并启动一个新进程,带有新的计数
  • 要增加工作者数量,要么使用相同的方法,要么在另一个shell中启动第二个dns-crawler-workers进程,工作者数量将相加
  • 扩展到多台机器的方式相同,见下文

在多台机器上运行

由于控制器和工作者之间的所有通信都是通过Redis完成的,因此很容易将爬虫扩展到任意数量的机器

machine-1                     machine-1
┬───────────────────────────┐         ┬─────────────────────┐
│    dns-crawler-controller │ ------- │ dns-crawler-workers │
│             +             │         └─────────────────────┘
│           redis           │
│             +             │
│        DNS resolver       │
└───────────────────────────┘
                                      machine-2
                                      ┬─────────────────────┐
                              ------- │ dns-crawler-workers │
                                      └─────────────────────┘
                                      …
                                      …

                                      machine-n
                                      ┬─────────────────────┐
                              _______ │ dns-crawler-workers │
                                      └─────────────────────┘

只需告诉工作者连接到主服务器上的共享Redis,例如。

$ dns-crawler-workers 24 192.168.0.2:6379
                    ^            ^
                    24 threads   redis host

请确保在这些机器上使用与~相同的Python版本运行工作者,否则您将得到不支持的pickle协议错误。请参阅Python文档中的pickle协议版本

当然,DNS解析器不需要与dns-crawler-controller位于同一台机器上——只需在config.yml中设置其IP即可。爬虫主要使用CZ.NIC的Knot Resolver进行测试,但应与支持DNSSEC的任何合理的解析器兼容。不过,Systemd的systemd-resolved似乎非常慢。

Redis也是如此,您可以将控制器和工作者指向运行Redis的单独机器(如果您使用Redis做其他事情,除了dns-crawler,不要忘记将其指向一个空DB,它默认为0)。

更新依赖项

MaxMind在星期二更新GeoIP数据库,因此设置一个cron作业以保持它们更新可能是个好主意。更多关于这一点可以在maxmind.com:GeoIP2的自动更新上找到。

如果您使用多台机器运行工作者,不要忘记在这些机器上更新GeoIP(或者设置一个共享位置,例如通过sshfs或nfs)。

监控

命令行

$ rq info
default      |████████████████████ 219458
1 queues, 219458 jobs total

0 workers, 1 queues

Web界面

$ pip install rq-dashboard
$ rq-dashboard
RQ Dashboard version 0.4.0                                                 
 * Serving Flask app "rq_dashboard.cli" (lazy loading)                            
 * Environment: production                                                
   WARNING: Do not use the development server in a production environment. 
   Use a production WSGI server instead.                                          
 * Debug mode: off                            
 * Running on http://0.0.0.0:9181/ (Press CTRL+C to quit)
RQ Dashboard screenshot RQ Dashboard screenshot

测试

一些基本测试位于此仓库中的tests目录中。如果您想手动运行它们,请查看.gitlab-ci.yml中的test阶段作业。基本上它只是下载免费的GeoIP数据库,告诉爬虫使用它们,并爬取一些域名,检查JSON输出中的值。它运行两次测试——首先使用默认的DNS解析器(ODVR),然后使用系统解析器。

如果您正在考虑编写一些额外的测试,请注意,GitLab CI 中使用的某些Docker容器没有配置IPv6(即使主机机器上可以工作),因此检查例如 WEB6_80_www_VENDOR 将会失败,除非进行额外设置。

操作系统支持

爬虫主要针对Linux开发,但它应该可以在Python支持的任何操作系统上工作——至少是工作部分(但如果你在你的操作系统上成功运行Redis服务器,控制器也应该可以工作)。

一个例外是Windows,因为它 不支持 fork(),但在WSL(Windows Subsystem for Linux)下可以运行。

win10 screenshot

……因此,您可以很容易地将一台游戏机变成一个互联网爬虫。

错误报告

请在此Gitlab仓库中创建 问题

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源代码分发

dns_crawler-1.6.5.tar.gz (45.2 kB 查看散列)

上传时间 源代码

构建分发

dns_crawler-1.6.5-py3-none-any.whl (49.6 kB 查看散列)

上传时间 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面