爬虫过程中存储URLs
我用Python写了一个小爬虫,用来收集网址。我对内容不感兴趣。目前我把所有访问过的网址保存在内存中的一个集合里,因为我不想让爬虫重复访问同一个网址。当然,这种方法很有限。
那么,跟踪我访问过的网址的最佳方法是什么呢?
我应该使用数据库吗?
- 用哪个数据库好呢?MySQL、SQLite还是PostgreSQL?
- 我应该怎么保存网址?作为主键,在访问每个网址之前先尝试插入它吗?
还是说我应该把它们写到一个文件里呢?
- 一个文件就够了吗?
- 还是多个文件?我该怎么设计文件结构呢?
我相信关于这个或类似主题的书籍和论文很多。你能给我一些建议,让我知道该读些什么吗?
6 个回答
这要看你打算爬取多少网页,以及你用的机器性能如何。假设一个普通的网页链接大约是60个字节,存储这些链接的内存集合每个链接大约需要100多个字节(因为在Python中,集合和字典的容量不能超过60%,这样速度会更快)。如果你有一台64位的机器,内存大约有16GB,你可以把10GB以上的内存分配给这个重要的集合,这样你就能轻松爬取大约1亿个网页链接;但如果你用的是一台32位的机器,只有3GB内存,那你显然不能分配超过1GB给这个集合,这样你最多只能爬取大约1000万个网页链接。使用Sqlite可以帮助你在32位机器无法处理的情况下,64位机器可以处理的范围内,比如说100到200百万个网页链接。
如果你需要处理更多的链接,我建议使用PostgreSQL,它的一个优点是可以在另一台机器上运行(在快速的局域网中几乎没有问题),这样你可以把主机器专门用来爬取网页。我觉得MySQL等也可以,但我更喜欢PostgreSQL的标准兼容性和稳定性;-)。这样的话,你可以处理几十亿个网页链接而不会有问题(只要有快速的硬盘,最好是RAID配置,当然还有尽可能多的内存来加速处理)。
如果你想通过使用固定长度的哈希值来节省内存或存储空间,而不是直接使用可能很长的网页链接,这样做是可以的,但前提是你能接受偶尔会出现误判,导致你无法爬取实际上是新链接的情况。这种“碰撞”并不一定会经常发生:即使你只用8个字节来表示哈希值,当你要处理数十亿个网页链接时,碰撞的风险也不会太大(这是一个著名问题的“平方根启发式”)。
用8字节的字符串来表示网页链接的话,像上面提到的那种高性能机器应该能轻松支持超过10亿个网页链接。
那么,你大概想爬取多少个网页链接呢?你能腾出多少内存呢?-)
我写了很多爬虫程序。对我来说,比内存不足更大的问题是,如果代码出错、机器崩溃,或者你决定修改代码,你已经爬取的所有网址可能会丢失。如果你的内存用完了,现在的机器和操作系统通常会进行分页处理,这样虽然会变慢,但仍然可以继续运行。可是,如果你需要花费几个小时重新收集那些网址,那可真是让人头疼,效率会大大降低。
把你不想丢失的信息放在内存里是个坏主意。显然,这时候使用数据库是最好的选择,因为你需要快速随机访问,以查看你是否已经找到某个网址。当然,内存中的查找速度更快,但要决定哪些网址需要保留在内存中,这个过程会增加额外的负担。与其写代码来判断哪些网址需要或不需要,我选择把它们存储在数据库中,这样我可以专注于让我的代码更简洁、易于维护,同时让我的SQL查询和数据库结构更合理。把网址字段设置为唯一索引,数据库管理系统就能快速找到它们,同时自动避免重复链接。
你连接互联网和访问的网站的速度,可能比你连接内部网络上数据库的速度要慢得多。在同一台机器上使用SQLite数据库可能是最快的,尽管SQLite本身没有Postgres那么复杂,而Postgres是我最喜欢的数据库。我发现把数据库放在与爬虫机器在同一个交换机上的另一台机器上,速度非常快;让一台机器处理爬虫、解析,然后进行数据库的读写操作是相当密集的工作。如果你有一台旧电脑,可以安装Linux,装上Postgres,开始使用。如果需要更快的速度,可以给那台机器加点内存。拥有一台专门用来处理数据库的机器会非常不错。
我觉得这里有几个重要的点:
- 你不能把网址都放在内存里,因为那样会占用太多的内存。
- 你需要快速查找网址,至少要做到 O(logn) 的速度。
- 你还需要快速插入网址。
有很多方法可以做到这一点,具体要看你的数据库会有多大。我觉得使用 SQL 数据库可以很好地解决这个问题。
你可能只需要一个 SQLite 数据库。通常,检查字符串是否存在是个比较慢的操作。为了加快这个过程,你可以对网址创建一个 CRC 哈希值,并把这个哈希值和网址一起存储在数据库里。你可以在这个 CRC 字段上建立索引。
- 插入时:你把网址和哈希值一起插入。
- 想要检查网址是否存在时:你先对可能的新网址计算 CRC,然后检查这个哈希值是否已经在数据库里。
当然,网址的哈希值可能会发生碰撞,但如果你不在乎 100% 的覆盖率,那碰撞导致某个网址不在数据库里的情况也是可以接受的。
你也可以通过多种方式来减少碰撞。例如,你可以增加 CRC 的大小(用 CRC8 而不是 CRC4),或者使用更大尺寸的哈希算法。还可以同时使用 CRC 和网址的长度来降低碰撞的概率。