利用Python爬虫离线构建Excel VBA本地帮助文档
针对办公环境网络隔离导致VBA开发不便的问题,本文介绍如何利用Python结合Selenium与Requests库,从微软官方文档网站抓取VBA API数据。通过遍历导航树获取链接,下载主体页面内容,并基于jstree构建本地离线查询工具,实现无网环境下的VBA参考查阅。
项目背景与目标
初衷在于解决办公电脑与互联网物理隔离环境下,本地Office软件缺失内置帮助工具导致的VBA编程困难问题(注:经后续发现Office 07版本其实已自带该功能,此处属于无效劳动)。为此,计划通过编写爬虫程序从官方网站抓取相关文档,构建本地参考资料库。
目标抓取网站地址为:https://docs.microsoft.com/zh-cn/office/vba/api/overview/
所用技术栈包括:
Python 3.7版本,以及requests和selenium两个核心库。
前端技术方面:采用了jquery框架,并引入jstree组件以支持生成无限层级的树状菜单结构。
技术选型与设计思路
整体设计思路如下:
首先对目标页面结构进行分析,页面主要划分为左侧导航栏和右侧内容显示区两部分。
其次,利用Selenium对左侧导航栏进行深度遍历,提取所有节点及其对应的超链接,并将结果转换为jstree所需的数据格式进行存储。
# 导航层级为
<ul>
<li>
<a>...
<span>....
最后,使用Requests库遍历所有获取到的链接,抓取对应的主体页面内容。
核心功能实现
具体实现步骤:
#
# parent 上级节点
# wait_text 上级节点对应的xpath路径的文本项
# level,limit 仅方便测试使用
#
def GetMenuDick_jstree(parent,level,wait_text,limit=2):
if level >= limit: return []
parent.click()
l = []
num = 1
new_wati_text = wait_text + '/following-sibling::ul' # 只需要等待ul出来就可以了/li[' + str(ele_num) + ']'
try:
wait.until(EC.presence_of_element_located((By.XPATH,new_wati_text)))
# 查询子节点所有的 a节点和span节点(子菜单)
childs = parent.find_elements_by_xpath('following-sibling::ul/li/span | following-sibling::ul/li/a')
for i in childs:
k = {}
if i.get_attribute('role') == None:
k['text'] = i.text
# 如果是子菜单,进行深度遍历
k['children'] = GetMenuDick_jstree(i,level+1,new_wati_text + '/li[' + str(num) + ']/span',limit)
else:
# 网页访问的Url无Html后缀,需要加上。去除无相关地址,形成相对路径。
url_text = str(i.get_attribute('href')).replace('https://docs.microsoft.com/zh-cn/office/', '',1) + '.html'
k['text'] = i.text
k['a_attr'] = {"href":url_text,"target":"showframe"}
lhref.append(str(i.get_attribute('href')))
num = num + 1
l.append(k)
parent.click() # 最后收起来
except Exception as e:
print('error message:',str(e),'error parent:' ,parent.text,' new_wati_text:',new_wati_text,'num:',str(num))
lerror.append(parent.text)
finally:
return l
# data菜单,lhref为后续需要访问的地址。
# 找到第一个excel节点,从excel开始
data = []
lhref = []
lerror = []
k = {}
browser.get(start_url)
browser.set_page_load_timeout(10) #超时设置
xpath_text = '//li[contains(@class,"tree")]/span[text()="Excel"][1]'
cl = browser.find_element_by_xpath(xpath_text)
k = {'text':'Excel'}
k['children'] = GetMenuDick_jstree(cl,1,xpath_text,20)
data.append(k)
# Writing JSON data
with open(r'templete\data.json', 'w', encoding='utf-8') as f:
json.dump(data, f)
完成上述步骤后,已掌握Excel VBA下的所有菜单信息及其对应的URL地址。接下来需要获取各页面的主体内容。
内容抓取的具体思路为:
1、遍历所有已获取的URL列表。
2、根据URL解析出对应的文件名。
#
# 根据网页地址,得到文件名,并创建相应文件夹
#
def create_file(url):
t = 'https://docs.microsoft.com/zh-cn/office/'
# 替换掉字眼,然后根据路径生成相应文件夹
url = url.replace(t,"",1)
lname = url.split('/')
# 先判断有没有第一个文件夹
path = lname[0]
if not os.path.isdir(path):
os.mkdir(path)
for l in lname[1:-1]:
path = path + '\\' + str(l)
if not os.path.isdir(path):
os.mkdir(path)
if len(lname) > 1:
path = path + '\\' + lname[-1] + '.html'
return path
3、访问URL获取页面主体信息,并将其保存至本地文件。
# requests模式
# 循环遍历,如果错误,记录下来,以后再执行
had_lhref = []
error_lhref = []
num = 1
for url in lhref:
try:
had_lhref.append(url)
path = create_file(url)
resp = requests.get(url,timeout=5,headers = headers) # 设置访问超时,以及http头
resp.encoding = 'utf-8'
html = etree.HTML(resp.text)
c = html.xpath('//main[@id="main"]')
# tostring获取标签所有html内容,是字节类型,要decode为字符串
content = html_head + etree.tostring(c[0], method='html').decode('utf-8')
with open(path,'w', encoding='utf-8') as f:
f.write(content)
except Exception as e:
print('error message:',str(e),'error url:',url)
error_lhref.append(url)
if num % 10 == 0 :
print('done:',str(num) + '/' + str(len(lhref)),'error num:' + str(len(error_lhref)))
#time.sleep(1) # 睡眠一下,防止被反
num = num + 1
本地主页构建与交互
目前菜单信息与页面内容均已就绪,接下来需要构建本地主页。此处选用jstree组件,涉及两个HTML文件:index.html和menu.html。
index.html:采用frameset框架结构,实现左右区域的相对隔离。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>参考文档</title>
<script src="js/jquery.min.js"> </script>
</head>
<frameset rows="93%,7%">
<frameset cols="20%,80%" frameborder="yes" framespacing="1">
<frame src="menu.html" name="menuframe"/>
<frame id="showframe" name="showframe" />
</frameset>
<frameset frameborder="no" framespacing="1">
<frame src="a.html" />
</frameset>
</frameset>
</html>
menu.html:
1、引入data.json文件,以便支持离线调用。若直接使用ajax.get读取json,会因跨域策略导致失败;
2、jstree默认会阻止<a>标签的跳转事件,因此需要通过监听"change.tree"事件来手动处理页面跳转逻辑。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery.min.js"></script>
<link rel="stylesheet" href="themes/default/style.min.css" rel="external nofollow" />
<script src="js/jstree.min.js"></script>
<script type="text/javascript" src="data.json"></script>
</head>
<body>
<div>
<form id="s">
<input type="search" id="q" />
<button type="submit">Search</button>
</form>
<div id="container">
</div>
<div id="container"></div>
<script>
$(function () {
$('#container').jstree({
"plugins": ["search", "changed"],
'core': {
'data': data,
}
});
});
$('#container').on("changed.jstree", function (e, data) {
//console.log(data.changed.selected.length); // newly selected
//console.log(data.changed.deselected); // newly deselected
if (data.changed.selected.length > 0){
// 说明转换了,获取url
var url = data.node.a_attr.href
// console.log(url)
if (url == "#"){
}else{
parent[data.node.a_attr.target].location.href = url
}
}else{
}
})
$("#s").submit(function (e) {
e.preventDefault();
$("#container").jstree(true).search($("#q").val());
});
</script>
</div>
</body>
</html>
综上所述,最终生成了本地版的Excel VBA参考工具网页。值得注意的是,部分Office版本其实已自带本地VBA帮助文档,导致此次开发工作略显多余。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









