sitemap网站地图是seo网站优化常用的一项功能,虽然网上有很多sitemap自动生成工具,但大多要么有数量限制,要么生成结果不理想,今天就来教教大家用python开发一个属于自己的sitemap生成器。
sitemap生成器,主要通过爬虫爬取给定网站内的所有url链接,并把不属于目标网站的url链接剔除,最后生成xml或者txt格式的sitemap文件。
那么要实现一个爬虫需要注意什么呢?
首先需要一个调度端(启动/停止爬虫/监视爬虫的运行情况),在爬虫程序中有三个模块:
URL管理器:对将要爬取和已经爬取的url,从url管理器中取出待爬取的url,将其传送给网页下载器
网页下载器:将url指定的网页下载下来存储成字符串并传送给网页解析器
网页解析器:解析出有价值的数据,解析出指向其他网页的url并补充到url管理器
URL管理器-网页下载器-网页解析器-URL管理器:循环处理,只要url管理器中有url就会一直运行下去,最终把相关连网页中我们需要的数据全部爬取出来。
爬虫运行的流程图解:

下面我们就来根据爬虫的运行流程来打造自己专属的sitemap生成器:
1、global_val.py:自定义全局模块
制作sitemap时,有些数据是不固定的,同时同一个变量会在多处使用,这就需要用到自定义全局变量了。
(python3中自定全局和跨模块使用其实挺简单的,我们可以把需要自定义的全局变量写在一个模块里,然后在需要用到这些全局变量的模块中导入自定义全局变量模块,然后用"模块名.变量名"的方式给自定义全局变量赋值或者使用)
host = None num = None
2、爬虫主程序:
sitemap.py
import html_downloader
import html_outputer
import html_parser
import url_manager
import global_val
class SpiderMain(object):
def __init__(self):
self.urls = url_manager.UrlManager() # url管理器
self.downloader = html_downloader.HtmlDownloader() # 页面下载器
self.parser = html_parser.HtmlParser() # 网页解析器
self.outputer = html_outputer.HtmlOutPut() #内容输出器
def craw(self, root_url):
count = 1
self.urls.add_new_url(root_url) # 将需要爬取的网站 url 加入 url管理器,开始执行爬虫程序
while self.urls.has_new_url():
try:
new_url = self.urls.get_new_url() # 从 url管理器中获取新的待爬取 url
print('craw %d : %s' % (count, new_url))
html_cnt = self.downloader.download(new_url) # 下载指定 url 页面内容
new_urls, new_data = self.parser.parse(new_url, html_cnt) # 解析指定 url 页面内容,获取数据和新的 url
self.urls.add_new_urls(new_urls) # 将新获取的url 加入到 url管理器中
self.outputer.collect_data(new_data) # 将重页面中获取到自己需要的数据收集起来,方便后面一起输出
# 若有 url 爬取数量限制,则当达到数量限制时终止爬虫继续爬取数据
if 0 < global_val.num == count:
break
count = count + 1
except:
print('craw failed %s' % new_url) # 爬取失败提示
self.outputer.output_html()
if __name__=="__main__":
root_url = input("请输入要爬取的域名(如有设置https,请带上 https 协议):")
if root_url.find("https://") > -1 or root_url.find("http://") > -1:
global_val.host = root_url
else:
global_val.host = root_url
root_url = "http://" + root_url + "/"
max_num = input("请输入sitemap最大url数(不输入则不限制数量):")
if max_num and int(max_num) > 0:
global_val.num = int(max_num)
else:
global_val.num = -1
obj_spider = SpiderMain()
obj_spider.craw(root_url)3、url管理器
url_manager.py
import re
import global_val
class UrlManager(object):
def __init__(self):
self.new_urls = set()
self.old_urls = set()
def add_new_url(self, url):
base_url = global_val.host
# 将新的 url 加入 url管理器前先进行过滤处理
if url is None:
return
if url.find("http") > -1 and url.find(base_url) < 0:
return
if re.compile(r":[a-zA-Z0-9();]").findall(url) \
or url.find("#") > 0 or url.find(".xml") > 0 \
or url.find(".jpg") > 0 or url.find(".png") > 0 \
or url.find(".gif") > 0 or url.find(".zip") > 0 \
or url.find(".rar") > 0 or url.find(".bmp") > 0 \
or url.find(".doc") > 0 or url.find(".doc") > 0 \
or url.find(".pdf") > 0 or url.find(".xls") > 0 \
or url.find(".docx") > 0 or url.find(".xlsx") > 0:
return
if url not in self.new_urls and url not in self.old_urls:
self.new_urls.add(url)
def add_new_urls(self, urls):
if urls is None or len(urls) == 0:
return
for url in urls:
self.add_new_url(url)
def has_new_url(self):
"""判断url管理器中是否还有url"""
return len(self.new_urls) != 0
def get_new_url(self):
"""获取新的待爬取url"""
new_url = self.new_urls.pop()
self.old_urls.add(new_url)
return new_url4、网页下载器
html_downloader.py
import urllib.request from urllib.parse import quote class HtmlDownloader(object): def download(self, url): """ 通过urllib.request请求url ,请求有效则下载网页内容""" if url is None: return url = quote(url, safe="?#/:=@") response = urllib.request.urlopen(url) if response.getcode() != 200: return None return response.read()
5、页面解析器
html_parser.py
import re
import urllib.parse
import global_val
from bs4 import BeautifulSoup
class HtmlParser(object):
def parse(self, page_url, html_cnt):
"""网页数据解析"""
if page_url is None or html_cnt is None:
return
soup = BeautifulSoup(html_cnt, 'html.parser', from_encoding='utf-8')
new_urls = self._get_new_urls(page_url, soup)
new_data = self._get_new_data(page_url, soup)
return new_urls,new_data
def _get_new_urls(self, page_url, soup):
"""从页面数据中获取新的url"""
new_urls = set()
links = soup.find_all('a', href=re.compile(r".*"))
base_url = global_val.host
for link in links:
new_url = link['href']
new_url_full = urllib.parse.urljoin(page_url,new_url)
if new_url_full.find(base_url) < 0:
continue
new_urls.add(new_url_full)
return new_urls
def _get_new_data(self, page_url, soup):
"""从页面数据中获取需要爬取的url和title"""
res_data = {}
title_node = soup.find('title')
res_data['title'] = title_node.get_text()
res_data['url'] = page_url
return res_data6、内容输出器
HTML_outputer.py
import re
import time
import global_val
class HtmlOutPut(object):
def __init__(self):
self.datas = []
def collect_data(self, data):
if data is None:
return
self.datas.append(data)
def output_html(self):
root_url = global_val.host
if not re.compile(r"http://").findall(root_url) and not re.compile(r"https://").findall(root_url):
root_url = "http://" + root_url
filename = global_val.host + 'sitemap.xml'
# open打开文件时最好指定 encoding ,否则在输出数据到文件时会有各种坑
fw = open(filename, 'w', encoding="utf-8")
fw.write('<?xml version="1.0" encoding="UTF-8"?>')
fw.write('\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">')
for data in self.datas:
url = data['url']
fw.write('\n\t<url>')
fw.write('\n\t\t<loc>' + url + '</loc>')
fw.write("\n\t\t<lastmod>" + time.strftime('%Y-%m-%d', time.localtime(time.time())) + "</lastmod>")
fw.write('\n\t\t<changefreq>daily</changefreq>')
if re.findall(root_url + "$", data['url']) or re.findall(root_url + "/$", data['url']):
fw.write('\n\t\t<priority>1.0</priority>')
elif re.findall(root_url + "[^/]*/$", data['url']) or re.findall(root_url + "/[^/]*/$", data['url']):
fw.write('\n\t\t<priority>0.8</priority>')
elif re.findall(root_url + "[^/]*/[^/]*$", data['url']) or re.findall(root_url + "/[^/]*/[^/]*$", data['url']):
fw.write('\n\t\t<priority>0.6</priority>')
else:
fw.write('\n\t\t<priority>0.4</priority>')
fw.write('\n\t</url>')
fw.write('\n</urlset>') 到这里自己专属的sitemap生成器就算打造完成了,当然了,这里打造的sitemap生成器还是有很多地方可以继续完善的,比如 changefreq,priority,还有生成的sitemap文件类型,这里最主要的还是打造爬虫的这些思想,只要能把这些掌握吃透,继续完善还是轻而易举的,下面让我们来看看运行效果吧:

