一、爬虫协议
与其它爬虫不同,全站爬虫意图爬取网站所有页面,由于爬虫对网页的爬取速度比人工浏览快几百倍,对网站服务器来说压力山大,很容易造成网站崩溃。 为了避免双输的场面,大家约定,如果网站建设者不愿意爬虫访问某些页面,他就按照约定的格式,把这些页面添加到 robots.txt 文件中,爬虫应该主动避免访问这些页面。除此之外,作为爬虫编写者也应该主动控制爬虫访问速度。
访问 robots 协议的方式是:网站域名+\'/robots.txt\'。
二、处理爬虫协议
import urllib.robotparser url = \'https://ai.baidu.com\' rp = urllib.robotparser.RobotFileParser() rp.set_url(url + \'/robots.txt\') rp.read() info = rp.can_fetch("*", \'https://ai.baidu.com/product/minos\') print(info)
三、全站爬虫的基本架构
爬虫从一个 URL 开始访问,通常是网站的域名,并将获得网页中的链接提取出来,去重后放入待访问列表。重复此操作,知道访问完网站内全部网页。
需要注意的是,全站爬虫通常只爬取网站的内部链接
四、网页链接提取
from requests_html import HTMLSession session = HTMLSession() origin = \'https://ai.baidu.com\' r = session.get(origin) print(r.html.links)
五、链接过滤
使用 urllib 库过滤所有非内部链接,继续运行下面的代码,观察结果:
from urllib.parse import urlparse from requests_html import HTMLSession session = HTMLSession() origin = \'https://ai.baidu.com\' r = session.get(origin) print(r.html.links) domain = \'ai.baidu.com\' def is_inner_link(link): netloc = urlparse(link).netloc return (not netloc) or (netloc == domain) for link in r.html.links: print(is_inner_link(link), link)
除了过滤非内部链接外,还需要把已经访问过的链接、爬虫协议不允许的链接 和 你不想访问的链接都过滤掉。
六、百度AI爬虫实现
from requests_html import HTMLSession import urllib.robotparser from urllib.parse import urlparse session=HTMLSession() origin= \'https://www.xuexi.cn/\' domain=urlparse(origin).netloc def is_inner_link(link): netloc=urlparse(link).netloc return (not netloc) or (netloc==domain) visited = [] # 已访问链接列表 unvisited = [origin] # 待访问链接列表 # 解析爬虫协议 rp = urllib.robotparser.RobotFileParser() rp.set_url(origin + \'/robots.txt\') rp.read() def add_unvisited(link): # 过滤1:判断爬虫协议是否允许 allow = rp.can_fetch(\'*\', link) if not allow: return # 过滤2:判断是否为内链 if not is_inner_link(link): return # 过滤3:去掉非法链接 path = urlparse(link).path if not path.startswith(\'/\'): return # 过滤4:自定义过滤 if urlparse(link).path.startswith((\'/file\', \'/docs\', \'/support\', \'/forum\', \'/broad\', \'/paddlepaddle\', \'/market\', \'/download\', \'/facekit\', \'/sdk\', \'/customer\', \'/easydl\', \'//\')): return # 将 /tech/123 转换为 https://ai.baidu.com/tech/123 的形式 if link.startswith(\'/\'): link = origin + link # 过滤5:判断是否访问过,或已经添加到待访问列表 if (link in visited) or (link in unvisited): return unvisited.append(link) while len(unvisited): link=unvisited.pop() #用于移除列表中的一个元素 r=session.get(link) visited.append(link) if r.html and r.html.links and len(r.html.links): for url in r.html.links: add_unvisited(url) if r.html.find(\'head title\')[0]: print(r.html.find(\'head title\')[0].text,link) print(\'共爬取{}个链接\'.format(len(visited)))