Python 实战爬虫分析案例(自用)

2022-05-18 14:56:03 浏览数 (1)

代码语言:javascript复制
# coding:utf-8
# 引入json库
import json
# 引入正则库,字符串搜索
import re
# 引入提取网页的xpath库
from lxml import etree


def write(path, data):
    with open(path, 'w', encoding='utf-8') as f:
        _data = json.dumps(data, ensure_ascii=False)
        f.write(_data)
    return True

# 先定义一个列表,作为第一层列表使用.(后面还有排序,去重什么的)
list_end = []
# 这里进入一个循环,即为html的文件名 *.html ,左等右不等
for i in range(1, 48):
    # 定义html的文件路径
    path = 'data/%s.html' % i
    # 以utf-8的格式打开html文件
    f = open(path, 'r', encoding='utf-8')
    # 读取html文件
    st = f.read()
    # 将html文件加载到etree中并赋值,之后可使用xpath操作
    html = etree.HTML(st)
    # 定义一个临时列表
    all_list = []
    # 获取 题目id,非字符串,为一个element对象
    _id = html.xpath('/html/body/div[2]/div[2]/div/table/tbody/tr')
    # 获取 标题和答案(这里放在一起写了,因为分开写合并的时候逻辑很混乱,很麻烦)
    title = html.xpath('//*//font')
    # 定义两个变量为接下来的循环做铺垫
    a = 0
    # 为分隔符做准备
    c = '、'
    # 以id的长度进行循环 id 和 title的值都为个数 *4...,因为id有空集(下文会去除)
    # print(len(_id))
    # print(len(title))
    for index in range(len(_id)):
        # 每次循环a的值 1(下面会有),即为遍历title,
        new_title = title[a]
        # 判断title是标题还是选项(因为标题全部都含有顿号....本来想用数字判断..写的太长逻辑没接上....
        # 所以这就要求选项中不可含有顿号,否则会被识别成标题,这之后的所以选项都会乱掉的....
        # 嘻嘻后来我找到方法了.....折腾了很长时间)
        if '、' in new_title.text and bool(re.search(r'd', new_title.text)):
            # print(c)
            # 判断是否含有题号,有则删除题号
            c = c.split(sep='、', maxsplit=-1)
            # 如果首项有为数字则为题号
            if c[0].isdigit():
                # 删除首项
                c.remove(c[0])
                # 再恢复原来的状态
                # print(c)
                c = '、'.join(c)
                # print(c)
            # 是标题的话就传入列表中,为什么这样写呢?
            # 是因为这是从第二个循环开始写的
            # 因为标题进入1次,选项要进入4次
            # 而我们只需要保存一次就行了
            # 所以我们就是第一次循环的时候,保存一个0的值,
            # 第二次循环到标题才会保存接下来赋予的标题和选项的值
            all_list.append(c)
            # print(all_list)
            # 这就是赋予标题的值
            # 这个text是因为 上面说过了,这个new_titile并不是一个字符串,而是一个对象类型
            c = new_title.text
        # 判断不是标题的时候执行逻辑
        else:
            # 判断是否是标题中是否单独存在数字或者顿号,如果有就提示
            if bool(re.search(r'd', new_title.text)) or '、' in new_title.text:
                print('请确认这不是标题 || ', new_title.text)
            # 这里就是单竖杠将标题和选项隔开,然后选项之间是双竖杠隔开
            c  = '|'   new_title.text
            # 因为标题会由很多的空格和换行符,这里要全部去掉('、'不要去,前面用到了)
            # print(c)
            c = c.replace('n', '').replace('r', '').replace('t', '')
        # # print(new_title)
        # 将a的值 1,也就是遍历的过程
        a  = 1
    # # print(all_list)
    # 将上面赋予列表的第一个没有意义的'第一个列表'去掉
    all_list.remove(all_list[0])
    # 因为这时候标题只剩了99个了,也就是最后一个标题没有获取到
    # 然后这里就是再加一个没有用的东西,用于和选项合并的时候不会报错
    # 后面加了判断,就是后面如果发现这个东西,就进入下一次循环
    # 这条数据就废弃了,但是因为数据有很多条,就是样本足够多的话,肯定可以再找到相同的数据的
    all_list.append('##$%#%$#%$#$@$$%^#%$@#%$!#^$%$#^%')
    # print(all_list)
    # 以上为标题和选项

    # 以下为id的获取
    # 再次给a赋值为0,再次进行循环(a是老工具人了hh)
    a = 0
    # 再定义一个临时列表(好像这是第三个了....)
    list_temp = []
    # 再循环一遍id的长度....
    for index in range(len(_id)):
        # 获取到id
        # 因为range是从1开始的,但我们取列表需要从0开始,所以用a(好像可以直接循环,但是那时候我没写.)
        # 这个获取到的东西是 {'id': 'topicid_XXXXXXX'} 或者为 {}
        id_num = dict(_id[a].attrib)
        # 将获取到的东西传入临时列表,准备做去除空集处理
        list_temp.append(id_num)
        # 遍历
        a  = 1

    # 以下为去除空集 用emd的原因是我认为快到end了但是感觉还没到...
    list_emd = []
    # 即为对上面的临时列表进行循环
    for x in list_temp:
        # 去除标题的所有空id集合,使其总数为100
        # print(x)
        if bool(x):
            # print(x)
            # 去除空元素
            list_emd.append(x)
    # print(len(list_emd))

    # 再循环,这时循环的是去掉空集之后的标题,即为100
    for index in range(len(list_emd)):
        # print(index)
        # 将题目与选项合并后的东西传入emd中...即为列表套字典(格式需要)
        list_emd[index]['question_txt'] = all_list[index]
        # 添加占位符, 方便手动输入答案
        list_emd[index]['answer'] = '正确答案填在此'
        list_emd[index]['answer_check_1'] = '检验成员1的答案'
        list_emd[index]['answer_check_2'] = '检验成员2的答案'
        list_emd[index]['answer_check_3'] = '检验成员3的答案'
        list_emd[index]['answer_check_4'] = '检验成员4的答案'
        list_emd[index]['answer_check_5'] = '检验成员5的答案'

        # 没啥用,就是好看,做个提示而已(跟原来格式保持一致....)(但是没啥用)
        list_emd[index]['answer_txt'] = '使用说明:将正确答案填入answer的引号中就可,多选不用间隔,示例 *A* *ABCD*'
        list_emd[index]['id'] = '3'
        del list_emd[index]['id']
    # print(len(list_emd))
    # 将每次循环html文件得到的内容传入列表中,即为列表套列表套字典..
    list_end.append(list_emd)

# print(list_end)

# new_list = sorted(all_list, key=(lambda r: r['id']))
# 将列表套列表套字典转换为列表套字典
list_end = sum(list_end, [])


# 去重
def deletedup(li):
    # 定义一个集合
    seen = set()
    # 定义空集
    new_list_2 = []
    print(li)
    # 对于传入的参数进行循环处理
    for d in li:
        # 即为利用id来去重
        # d1 = d['id']
        d2 = d['question_txt']
        # 这里就是上文所说的去掉
        str1 = d['question_txt']
        # 其实也不是去掉,就是进入下一个循环,就是不传.
        if str1 == '##$%#%$#%$#$@$$%^#%$@#%$!#^$%$#^%':
            continue
        # print(d1)
        # print(seen)
        # 如果没有这个元素才传入,有就不传
        if d2 not in seen:
            # print(d1)
            # 传入元素
            new_list_2.append(d)
            # 传入集合中,是不是集合意义不大,因为不同页面的题号不同,所有肯定不一样
            seen.add(d2)
    # 返回去重之后的列表
    return new_list_2


# 入口函数
if __name__ == '__main__':
    # 对于得到的列表去重
    list_tools = deletedup(list_end)
    # 打印信息,完成任务,下一步去格式化就好啦!
    # print(new_list_2)
    # new_list = sorted(list_tools, key=lambda r: r['id'])
    print(list_tools)
    # 打印警告信息和统计信息
    print('总计:', len(list_tools), '条数据', 'n注意:请划到顶部确认那些东西是不是标题!!!!')
    out_result = write('result.json', list_tools)
    print('是否成功写入result.json文件:', out_result)
    

0 人点赞