在前文书写代码的时候,脚本已经实现了基本的功能,从一个HTML中提取出我们想要的信息。但是,这样写出的代码至少有以下问题:
- 可封装性差
- 接口定义不清楚
- 迭代困难
google的serp页面经常会遇到一些小调整,我们的代码必然要经常进行更新,那么我们就需要保证代码的简洁性。在这个项目中,我们把握第一个原则:面向对象编程。
那么什么是面向对象?首先看下面的代码:
class GoogleSpider(object):
def __get_total_page(self):
pass
def search_text(self):
pass
def search_videos(self):
pass
def search_wiki(self):
pass
...
这个代码,无法是将我们刚刚写出的函数封装在一个GoogleSpider类中,看似是用了面向对象的写法,但实际上类中的方法和数据并没有发生关联,还需要再进一步。
前文中,我们的输出会输出爬取内容的内容,即type
字段。
在这里,我们类中提前定义四种类型。
完整的面向对象代码,如下所示:
class GoogleSpider(AttribDict):
__exclude_keys__ = {'soup'}
def __init__(self, soup: BeautifulSoup):
self.videos = []
self.wiki = []
self.page = 0
self.news = []
self.main = []
self.soup = soup
def __get_total_page(self):
"""获取当前页面总数"""
pages_ = self.soup.find('span', id='xjs').findAll('td')
maxn = 0
for p in pages_:
try:
if int(p.text) > maxn:
maxn = int(p.text)
except:
pass
self.page = maxn
def __search_main(self):
"""解析主要搜索结果"""
# 获取所有的主要搜索结果
result_containers = self.soup.findAll('div', class_='g')
for container in result_containers:
# title提取
try:
title = container.find('h3').text
# 对应链接提取
url = container.find('a')['href']
# 对应描述提取
des = container.find('span', class_='aCOpRe').text
self.main.append({
'title': title,
'url': url,
'des': des,
})
except Exception:
continue
def __search_wiki(self):
"""解析wiki内容"""
container = self.soup.find('div', class_='kp-wholepage')
# 如果container为None,则返回空列表
if container is None:
return []
# Title
title = container.find('h2', attrs={'data-attrid': 'title'}).find('span').text
# Subtitle
try:
subtitle = container.find(
'div', attrs={'data-attrid': 'subtitle'}).text
except AttributeError:
subtitle = None
# Description
des = container.find('div', class_='kno-rdesc').find('span').text
# 获取Wiki链接
url = container.find('div', class_='kno-rdesc').find('a')['href']
# Details内容
try:
# div.wp-ms对应不同的四个card
table = container.findAll(
'div', class_='wp-ms')[2].findAll('tr', class_='kno-nf-nr')[1:]
except IndexError:
table = []
details = []
for row in table:
name = row.find('span').text.strip(': ')
detail_ = row.findAll('span')[1:]
detail = ''
for _ in detail_:
detail += _.text + ' ' # 以 key value的形式输出结果
details.append({
'name': name,
'detail': detail.strip()
})
result = {
'title': title,
'subtitle': subtitle,
'des': des,
'url': url,
'details': details,
}
self.wiki = [result]
def __search_news(self):
try:
cards = self.soup.find('g-scrolling-carousel').findAll('g-inner-card')
except AttributeError:
return []
for card in cards:
title = card.find('div', role='heading').text
href = card.find('a')['href']
result = {
'title': title,
'href':href,
}
self.news.append(result)
def __search_videos(self):
try:
cards = self.soup.find('div', id='search').findAll('div', class_='VibNM')
except AttributeError:
return []
for card in cards:
title = card.find('div', role='heading').text
href = card.find('a')['href']
result = {
'title': title,
'href':href,
}
self.videos.append(result)
def search(self):
self.__get_total_page()
self.__search_main()
self.__search_news()
self.__search_videos()
self.__search_wiki()
spider = GoogleSpider(soup)
spider.search()
可以看到,通过面向对象的封装之后,最直观的就是注释变少,也就是输入和输出都不需要显示指定了,其次是我们可以使用更加多的设计模式来进一步完善我们这个GoogleSpider
类。
Top comments (0)