今天在这里分享一些关于爬虫技术的介绍,主要以增量式爬虫介绍为主。
一、爬虫概念
所谓爬虫,其本质是一种计算机程序,它的行为看起来就像是蜘蛛在网上面爬行一样,顺着互联网这个“网”,一条线一条线地“爬行”。所以爬虫在英文中又叫作“Spider”,正是蜘蛛这个单词。
爬虫
的主要目的是获取网页内容并解析。
在互联网发展的势头下,各种开发技术变得日新月异,面对不同场景的需求衍生了类型众多的网络爬虫。网络爬虫按照系统结构
和实现技术
大致可以分为4种类型,分别是通用网络爬虫、聚焦网络爬虫、增量式网络爬虫(广度优先)、深层网络爬虫(深度优先)。
有关其它爬虫技术,请参考:【由浅入深】爬虫技术,值得收藏,来了解一下~
1.1、增量式爬虫
增量式网终爬虫(Incremental Web Crawler)是指对已下载的网页采取增量式更新,只抓取新产生或者已经发生变化的网页的网络爬虫。
增量式网络爬虫只会抓取新产生的或内容变化的网页,并不会重新抓取内容未发生变化的网页,这样可以有效地减少网页的下载量,减少访问时间和存储空间的耗费,但是增加了网页抓取算法的复杂度和实现难度。
1.2、深层爬虫
深层网络爬虫(Deep Web Crawler)是指抓取深层网顷的网络爬虫,它要抓取的网页层次比较深,需要通过一定的附加策略才能够自动抓取,实现难度较大。
二、增量式爬虫的实现步骤
编写增量爬虫的一般步骤如下:
-
定义一个数据存储结构,记录已经爬取过的网页和相关信息,比如最后访问时间、页面内容哈希等。可以使用数据库或文件系统等方式进行存储。
-
确定需要爬取的网站,设计好爬虫的起始点。
-
从起始点开始爬取页面,并从页面中提取需要的信息。
-
对提取到的信息进行处理和存储,同时更新已经爬取过的页面的相关信息。
-
针对更新过的页面和可能的新页面,重复以上步骤,直到完成所有的爬取任务。
-
可以添加一些限制条件,比如爬取深度、爬取速度等,以避免给目标网站造成过大的负担。
需要注意的是,增量爬虫需要针对已经爬取过的页面进行更新和去重,可以根据页面的哈希值或者其他唯一标识进行比较和判断。同时,增量爬虫可能需要处理一些更新频率较高的页面,需要根据实际情况进行设计和优化。
三、增量式爬虫案例
下面是一个简单的增量式爬虫案例,用于从某个新闻网站抓取新闻文章的标题和链接,并实现增量爬取功能:
import requests
import hashlib
import time
import pymysql
from bs4 import BeautifulSoup
# 定义爬虫起始点和目标站点
start_url = 'https://news.example.com'
target_site = 'example.com'
# 定义数据库连接和数据表结构
conn = pymysql.connect(host='localhost', user='root', password='password', database='news_db', charset='utf8mb4')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS news (
id INT PRIMARY KEY AUTO_INCREMENT,
title TEXT,
url TEXT,
hash TEXT,
last_modified BIGINT
)
''')
# 定义页面哈希函数
def get_hash(url, content):
return hashlib.sha1(f'{url}{content}'.encode('utf-8')).hexdigest()
# 定义页面访问函数
def fetch_page(url):
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
return None
# 定义页面解析函数
def parse_page(content):
soup = BeautifulSoup(content, 'html.parser')
news_list = soup.find_all('a', {'class': 'news-link'})
for news in news_list:
title = news.text.strip()
url = news['href'].strip()
yield (title, url)
# 定义增量爬取函数
def crawl_incremental():
last_modified = None
cursor.execute('SELECT MAX(last_modified) FROM news')
row = cursor.fetchone()
if row[0]:
last_modified = row[0]
current_url = start_url
while True:
content = fetch_page(current_url)
if content is None:
break
hash_value = get_hash(current_url, content)
if hash_value in visited_urls:
break
visited_urls.add(hash_value)
for title, url in parse_page(content):
if target_site in url:
cursor.execute('SELECT * FROM news WHERE url = %s', (url,))
row = cursor.fetchone()
if row:
# 已经爬取过该页面,检查是否需要更新
if row[4] < last_modified:
# 页面已经更新,重新爬取
cursor.execute('DELETE FROM news WHERE url = %s', (url,))
conn.commit()
crawl_url(url)
else:
# 页面没有更新,跳过
pass
else:
# 第一次爬取该页面
cursor.execute('INSERT INTO news (title, url, hash, last_modified) VALUES (%s, %s, %s, %s)',
(title, url, get_hash(url, fetch_page(url)), int(time.time())))
conn.commit()
current_url = get_next_url(content)
# 定义获取下一个页面链接的函数
def get_next_url(content):
# 略去具体实现
pass
# 开始爬取
visited_urls = set()
crawl_incremental()
# 关闭数据库连接
conn.close()
具体来说,增量爬取函数crawl_incremental()
中,我们通过查询数据库中已有的新闻页面的最后修改时间,以及使用SELECT语句检查数据库中是否已有某个页面的记录,来实现增量爬取。如果数据库中已有该页面的记录,就检查该页面的最后修改时间,如果该时间早于已有的最后修改时间,则说明页面已经更新,需要重新爬取,否则跳过。如果数据库中没有该页面的记录,则说明该页面是新页面,需要将其插入到数据库中。
需要注意的是,MySQL数据库的操作需要建立连接、打开游标、提交事务等步骤,因此需要在合适的地方进行相关操作,确保数据能够正确地插入或删除。另外,MySQL数据库需要指定字符集,以支持中文等非ASCII字符的存储和查询,因此需要在连接数据库时指定字符集为utf8mb4。
综上所述,以上是一个使用MySQL数据库实现增量爬虫的示例代码。实际应用中,需要根据具体的爬取需求和数据存储方式进行相应的修改和优化。
注意:
yield是Python中的关键字,用于生成器函数中的语句。当函数执行到yield语句时,会返回一个值,并暂停函数的执行,等待下一次调用时继续执行。因此,生成器函数可以返回多个值,并且每次返回一个值时可以暂停函数的执行,避免一次性生成所有值所带来的性能问题。
在上面的代码中,parse_page函数是一个生成器函数,用于解析新闻网站的HTML页面,返回新闻标题和链接。当调用parse_page函数时,它并不会一次性返回所有新闻标题和链接,而是通过yield关键字逐个返回。具体来说,parse_page函数会首先使用BeautifulSoup库解析HTML页面,然后通过find_all方法找到所有
<a class="news-link">
元素,再逐个提取元素的text和href属性作为新闻标题和链接,并通过yield关键字逐个返回。在爬虫函数中,可以通过for … in …循环逐个遍历parse_page函数的返回值,获得每个新闻的标题和链接,并进行后续的处理。
总之,yield关键字是生成器函数中的重要语句,它可以使函数的返回值逐个生成并返回,避免一次性生成所有值所带来的性能问题,也方便进行迭代处理