html数据提取

需求分析

老婆单位最近要会计考试了,我问她有什么可以帮忙的没有,她向我提了一个需求:考试是在一个网上平台上,目前可以在平台上进行不限次数模拟考试,但是模拟考试每次题目只有20道,想把模拟考试的题目保存下来。我看了一个她的模拟考试页面,里面可以显示答案,想了一下,就是将网页中的题目、选项、答案都用保存到EXCEL中,便一口答应。

coding 过程

将模拟考试的的页面保存下来一份,开始分析结构和需要的字段,这里我使用xpath来解析html (主要是不会BeautifulSoup),由于 xpath 不熟练,还要看着文档一步一步调试,这一部分最耗时。 题目和答案都相对比较容易,但是选择题的选项是不固定的,99%都是4个选项,也有3个、5个和6个的,这样的话和题目对应起来的时候不能按4的倍数来搞,稍微费了一点脑筋。最后将得到的题目列表、答案列表、选项列表对应项合并起来,每个题目一个list 最后再使用pandas将结果写进EXCEL中。

代码

import pandas as pd
from lxml import etree
import os

def html_unpack(html):
    """将html 中包含的题目保存出来
    输出为元组,第一部分为选择题,第二部分为判断题
    """
    # 使用 xpath 来解析 html 中需要的字段
    s = etree.HTML(html)
    # 匹配选择题
    choice_questions = s.xpath('//div[@data-current="exam/exam/question/types/answer/choice:content"]/div/text()')
    # 匹配判断题
    judgement_questions = s.xpath('//div[@data-current="exam/exam/question/types/answer/judgement:content"]/div/text()')
    # 匹配全部答案
    answer =  s.xpath('//div[@class="show-answer"]/div/div/div/text()')
    # 匹配选择题的选项部分
    choice_options_part1 = s.xpath('//span[@class="pull-left option-num"]/text()')
    choice_options_part2 = s.xpath('//div[@class="answer-options m-left"]/text()')  

    # 将获取的数据规范一下,去掉换行
    answer_options = list(map(lambda x :x[0]+x[1], zip(choice_options_part1, choice_options_part2)))
    choice_questions = [[i.strip().replace('\u3000',' ')] for i in choice_questions if i.strip()]
    judgement_questions = [[i.strip()] for i in judgement_questions if i.strip()]
    answer = [[i.strip().replace("标准答案:","")] for i in answer]  

    # 将每道选择题的选项合并到一个列表中。由于选项不是固定4个,所以需要处理一下。
    answer_options_list = [[] for i in range(choice_options_part1.count('A.'))]
    j = -1
    for i in answer_options:
        if i[0] == "A":
            j += 1
        answer_options_list[j].append(i)

    # 将选择题按题目、答案、选项的顺序排好,格式为 [[question1, answer1, opention1], ...]
    result_choice = list(map(lambda x: x[0]+x[1]+x[2], zip(choice_questions, answer[:16], answer_options_list)))
    # 将判断题按题目、答案的顺序排好,格式为 [[question1, answer1], ...]
    result_judgement = list(map(lambda x: x[0]+x[1], zip(judgement_questions, answer[16:])))
    return result_choice, result_judgement

def read_html(path):
    with open(path, 'r', encoding = 'utf-8') as f:
        result = f.read()
    return result


def main():
    # 获取文件列表
    file_list = ['2/123/'+ i for i in os.listdir('2/123') if "html" in i]

    # 循环处理每个文件,并将处理后的结果汇总
    result_choice = []
    result_judgement = []
    for i in file_list:
        html = read_html(i)           # 调用读取函数
        result = html_unpack(html)    # 调用解析函数
        result_choice += result[0]    # 保存选择题
        result_judgement += result[1] # 保存判断题

    # 使用pandas 将处理好的文件写进 excel 中
    # 选择题的选项列不固定,有时有五个选项,有时有六个选项,这里有个小BUG,如果列名和选项个数不对应,会报错。
    choice_df = pd.DataFrame(result_choice, columns=['题目', '答案', 'option1', 'option2', 'option3', 'option4', 'option5', 'option6'])
    judgement_dfd = pd.DataFrame(result_judgement, columns=['题目', '答案'])
    # 数据按题目去重
    choice_df = choice_df.drop_duplicates('题目')
    judgement_dfd = judgement_dfd.drop_duplicates('题目')
    with pd.ExcelWriter('会计自测4.xls') as writer:
        choice_df.to_excel(writer,sheet_name='选择',index=None)
        judgement_dfd.to_excel(writer,sheet_name='判断',index=None)

if __name__ == "__main__":
    main()

输出

试着跑了125套模拟题,速度很快,3秒内出结果。就是保存125个模拟考页面比较麻烦,花了将近1个小时才保存完,因为这是一次性的需求,也懒得再去分析网站写爬虫了。

*****
Written by sigenzhe on 24 October 2019