Beautifulsoup 爬取页面试题
假设有一个页面,页面中有n道选择题,每道选择题有若干个选项。题干部分用h6 标签标记。选项部分用的是td 下的div 标签。如下图所示:
整个页面是将如下的HTML 段落循环n次。
<div style="" class="qItemType qItemDetail1" value="10000#1" id="quest2436"> <div> <div class="breadcrumb"> <div> <ul style="list-style-type:none;"> <li><h6><a name="qnum1" style="color: red;">第1题.</a> 选一二三还是四五六呢。</h6></li> </ul> </div> <ul style="list-style-type:none"> <li> <table> <tbody><tr> <td> <div>A.一二三</div> </td> </tr> <tr> <td> <div>B.四五六</div> </td> </tr> </tbody></table> </li> </ul> </div> <div>
下面想要用beautifulsoup 库中的方法将页面上的题目和选项提取出来。
首先要引入需要用到的包:
from bs4 import BeautifulSoup from urllib import request,parse import re
然后可以用多种方法提取页面源码,:
#既可以打开一个html文件: soup = BeautifulSoup(open("page_source_code.html")) #或者直接传入HTML代码: soup = BeautifulSoup("<html>data</html>") #也可以发送并且拦截请求:
url = “http://fake.html” response = request.urlopen(url,timeout = 20) responseUTF8 = response.read().decode("utf-8") soup = BeautifulSoup(responseUTF8,'lxml')
总之,这样我们就得到了一个soup 对象。接下来只要根据对象的标签结构,通过一定方法定位到目标标签,就可以了。
方法一:下面这种方法比较基本,用的是“绝对路径”找寻标签
# 通过观察发现,题干部分全部都是h6标签,和h6标签下的a标签。页面其余部分没有用到h6标签,所以用.find_all方法来抓取所有的题干。得到一个标签 list h6lbs = soup.find_all('h6') # 定义一个二维数组,用来盛放抓取到的选择题。选择题的题干和选项可作为每一个数组成员的成员。 item_question = [] for i in range(len(h6lbs)): #定义一个一维数组,可以用来盛放题干和选项。先把刚刚拿到的题干保存进数组中 item = [] item.append(h6lbs[i].text) #通过以上的HTML 结构可以知道,找到题干后,题干标签的“太爷爷”的“三弟弟”是保存选项的地方,所以这里用了很多个.parent 和 .next_sibling方法,通过绝对路径的方式定位标签 tag1 = h6lbs[i].parent.parent.parent.next_sibling.next_sibling # check if this is choice question. or it must be a Yes/No questionnaire if tag1 is not None and tag1.li is not None: #刚刚说到太爷爷的三弟弟是存放选项的地方。太爷爷的三弟弟下的li标签下的table标签下的tbody标签下存放了多个选项,从这里开始需要遍历,将每一个选项都提取出 tag = h6lbs[i].parent.parent.parent.next_sibling.next_sibling.li.table.tbody for child in tag.children: #因为抓取出来有空对象,所以此处加入了一个判断。如果是None,就不保存了。 if child.string is None: tag_string = child.td.div.string #遍历每一个标签,取出其中的选项内容,用.string方法 #将取到的选项内容加入刚刚创建的一维数组中 item.append(tag_string) # print(item) #将每次得到的一维数组保存进二维数组中 item_question.append(item) print(item_question)
方法二(推荐):还可以用相对路径的方法定位标签:
#通过观察可以发现,每一个试题都是在一个div标签,并且value 为11111#1,11111#2,11111#3的标签下的。所以我们先将这个标签取到,作为一个参照。re.compile是正则表达式方法 the_tag = soup.find_all('div',value = re.compile('11111#\d')) print(len(the_tag)) #创建二维数组,用来保存试题 item_question = [] for tag in the_tag: #创建一维数组,用来保存题干和选项 item = [] #遍历刚刚选好的参照标签 for child_tag in tag.descendants: #每一个参照标签下的h6标签都是题干,提取出来保存。 if child_tag.name == "h6": item.append(child_tag.get_text("", strip=True)) #每一个参照标签下的td标签都是选项,提取出来保存 elif child_tag.name == "td": if child_tag.div is not None: item.append(child_tag.div.get_text("", strip=True)) item_question.append(item) print(item_question)