最近在接触
Arcconf
之类的命令操作和Gitbook
,所以结合起来做个练习
1. 数据获取
页面构成大致如下,每个版块都分为标题、命令功能、命令格式、参数说明(表格)、使用指南、使用实例几个部分
2. 流程分析
数据方面难点应该是表格、命令和实例方面,表格正好可以使用之前自己写的
html
转markdown
(参见以前文章),命令的话这里也存在着可能有多行命令的情况所以要分开处理,实例因为又涉及代码又设计文字,而且html
中并没有明显的区分格式,所以直接将代码归一类,文字作为标题(反正代码都是跟命令走的嘛,看命令就能知道注释含义,就当做标题了),整个Html
文档的区分性不强,很多板块的class值都是一样的,里面也是各有特点,有些表格直接p标签显示,有些还要加pre、strong、em
甚至表格嵌套,所以xpath
直接抓取显然不明智(其实也可以直接全部获取,然后根据位置分门别类,感觉麻烦到不可能实现),所以这里主要采取re
正则处理,xpath
作为辅助处理一些小格式问题
3. 数据抓取
I. 网页内容获取
爬虫基础嘛,老一套,不是很隐秘的网站,所以header什么的其实不所谓,看习惯和心情吧
def get_html(url):
r = requests.get(url, headers=headers)
return r.text
II. 处理冗余标签
如上所述,很多不必要的标签会混淆,所以去除
def get_data_html(text):
text = re.sub('</strong>', '', re.sub('<strong.*?>', '', re.sub('</em>', '',
re.sub('<em.*?>', '', text))))
html = etree.HTML(text)
III. 获取标题、命令功能
这里只有标题是h4标签,所以很方便抓取,命令功能和也比较明显,用正则还是很方便的
title = re.findall('<h4.*?>(.*?)</h4>', text)#列表
function = re.findall('<h5.*?>命令功能</h5>.*?<p.*?>(.*?)</p>', text)#列表
IV. 获取命令格式、使用指南
因为这几项都是不唯一的,使用正则就不是很明智了(主要是自己也学的不好),然后我们用
xpath
结合contains
和parent
树结构获取,使用列表保存,之后要生成文件嘛,因为这两项的值都不唯一,所以重写一个方法获取相应数据,将数据以列表形式保存,返回给外层列表,即是列表包含列表的嵌套结构,这里的div
是父层元素的lxml
位置,可以直接调用这个来执行xpath
索引
def get_data_html(text):
order = []
order_divs = html.xpath('//h5[contains(text(), "命令格式")]/parent::div')
for div in order_divs:
order.append(get_order(div))
tutor_divs = html.xpath('//h5[contains(text(), "使用指南")]/parent::div')
tutor = []#保存使用指南的嵌套列表
for div in tutor_divs:
tutor.append(get_tutor(div))
def get_order(rule):
tmp = []
if len(rule.xpath('./p')) > 1:
for data in rule.xpath('./p'):
tmp.append(" ".join(data.xpath('.//text()')))
else:
tmp.append(" ".join(rule.xpath('./p//text()')))
return tmp
def get_tutor(div):
tmp = []
if div.xpath('.//p[@id]'):
tmp.extend(div.xpath('.//p[@id]//text()'))
if div.xpath('.//div[@class="idp-ltr-html-noticebody"]'):
tmp.extend(div.xpath('.//div[@class="idp-ltr-html-noticebody"]/p//text()'))
return tmp
V. 获取参数说明(表格)
之前有专门写过
html
转markdown
的程序,这里现般现用就行,所以主要两个任务:1. 获取到每个表格的html字符串 2. 对表格内的数据处理做一定修改,因为这里表格内的结构不一,有些表格甚至涉及嵌套规则
- 表格
class
唯一,所以直接获取 - 主要难点在于对于表格内多行数据怎么获取,这里在以前的基础上修改,针对多行数据李彤try、except捕获异常,有异常即说明数据不唯一,针对数据改变xpath索引为
'./td/ul/li//text()
或者'./td//text()'
等其他规则,但是因为这里部分表格在td之上有一层p标签,导致//text()获取不到之后的内容(其实还好,只有连个表格涉及这种情况,手动处理就好了嘿嘿嘿),所以更好的方法就是哈哈哈,对没错用re将p标签去除,只保留里面文字即可,像这样re.sub('<p.*?>', '', text)
print_data
和write_data
分别是打印和写入文件的操作,详情参考之前的文章
def get_data(table):
html = etree.HTML(table)
body = []
head = html.xpath('//table/thead/tr')
# 获取表头
title = [th.xpath('.//p//text()') for th in head][0]
# #根据表头长度确定符号线
line = ["----"] * len(title)
# 获取表格内容并用嵌套的列表保存
tbody = html.xpath('//table/tbody/tr')
for tr in tbody:
body.append(list_format(tr, len(title)))
return title, line, body
def list_format(rule, num):
tmp = []
for i in range(num):
try:
data = rule.xpath('./td/p//text()')[i]
tmp.append(data)
except:
data = " ".join(rule.xpath('./td[{}]/ul/li//text()'.format(i+1)))
tmp.append(data)
return tmp
def print_data(table):
title, line, body = get_data(table)
# 用"|"分隔获取到的数据并依次打印
result = [" | ".join(data) for data in body]
print("| " + "| ".join(title) + "|")
print("| " + " | ".join(line) + "|")
for data in result:
print("| " + data + "|")
def write_data(table, filename):
title, line, body = get_data(table)
result = [" | ".join(data) for data in body]
with open(filename.replace('/', '_'), 'a', encoding='utf-8') as f:
f.write("| " + "| ".join(title) + "|" + "\n")
f.write("| " + " | ".join(line) + "|" + "\n")
for data in result:
f.write("| " + data + "|" + "\n")
VI. 获取实例
实例用
xpath
确认到对应的div后分别用列表保存标题(文字)和代码即可
def get_data_html(text):
exams = []
exams_title = []
exams_html = html.xpath('//h5[contains(text(), "使用实例")]/parent::div')
for exam in exams_html:
tmp_title, tmp = get_exams(exam)
exams_title.append(tmp_title)
exams.append(tmp)
return title, function, order, tabs, tutor, exams_title, exams
def get_exams(div):
tmp, tmp_title = [], []
if div.xpath('./p[@id]'):
tmp_title.extend(div.xpath('./p[@id]//text()'))
if div.xpath('.//pre'):
for pre in div.xpath('.//pre'):
single = pre.xpath('./text()')
tmp.append("".join(single))
return tmp_title, tmp
至此我们已经拿到了所有数据,下一节介绍怎么写入
Gitbook
文件