本文是下面两篇文章的续篇
本系列包括如下内容
- 抓取豆瓣top250一页多个字段
- 整合成列表
- 存储为json文件
- 定义成函数形式
- 多页抓取之构造url
- 多页抓取之翻页
- 抓取二级页面数据
- 通过生成器优化代码
- 改写为类的形式
本文主要讲
- 通过生成器优化代码
- 改写为类的形式
通过生成器优化代码
使用生成器可以大大改进代码的可扩展性,比如下面我们将抓取一级页面250个电影信息,翻页方法使用生成器改写为
import requests # 导入网页请求库
from bs4 import BeautifulSoup # 导入网页解析库
import json
def start_requests(url):
r = requests.get(url)
return r.content
def parse(text):
soup = BeautifulSoup(text, 'html.parser')
movie_list = soup.find_all('div', class_ = 'item')
for movie in movie_list:
mydict = {}
mydict['title'] = movie.find('span', class_ = 'title').text
mydict['score'] = movie.find('span', class_ = 'rating_num').text
quote = movie.find('span', class_ = 'inq')
mydict['quote'] = quote.text if quote else None
star = movie.find('div', class_ = 'star')
mydict['comment_num'] = star.find_all('span')[-1].text[:-3]
yield mydict # 这里使用生成器
def get_all(): # 获取所有页封装成一个函数
for i in range(2):
url = 'https://movie.douban.com/top250?start={}&filter='.format(i * 25)
text = start_requests(url)
result = parse(text)
yield from result # 返回一个生成器
对yield
和yield from
没有了解的读者可以先看这篇文章
这样做的好处是扩展性很好,get_all()
的结果就是一个包含所有信息的生成器,只要一个循环就可以自由调用所有抓取到的信息。而且生成器的特性是调用时才会运行,所以相当于每次抓取到信息就直接一步到位输出到自己想要的位置,而没有了 中间先存储一步,再提取 的资源浪费。
当你要print时,只需要
def main():
for info in get_all():
print(info)
if __name__ == '__main__':
main()
当你需要存到list,再存成json文件时可以
def main():
result_list = list(get_all())
s = json.dumps(result_list, indent = 4, ensure_ascii=False)
with open('movies.json', 'w', encoding = 'utf-8') as f:
f.write(s)
if __name__ == '__main__':
main()
当你需要每一条存储到数据库的时候,可以
def main():
for info in get_all():
put_into_database_code
if __name__ == '__main__':
main()
可以想象如果不使用生成器需要怎么做,有时候要构造一个list,有时候就不要,改一个需求,代码要改的地方很多,这种重新设计是比较低效的。
对于翻页方法和二级页面抓取来说,思路也是类似的,读者可以自己去尝试一下。
改写为类的形式
当需要设置一些全局变量的时候,可以考虑将代码改写为类的形式。下面我们以翻页为例
import requests # 导入网页请求库
from bs4 import BeautifulSoup # 导入网页解析库
import json
class Doubantop(object):
def __init__(self):
self.baseurl = 'https://movie.douban.com/top250'
self.result_list = []
def start_requests(self, url):
r = requests.get(url)
return r.content
def parse(self, text):
soup = BeautifulSoup(text, 'html.parser')
movie_list = soup.find_all('div', class_ = 'item')
for movie in movie_list:
mydict = {}
mydict['title'] = movie.find('span', class_ = 'title').text
mydict['score'] = movie.find('span', class_ = 'rating_num').text
quote = movie.find('span', class_ = 'inq')
mydict['quote'] = quote.text if quote else None
star = movie.find('div', class_ = 'star')
mydict['comment_num'] = star.find_all('span')[-1].text[:-3]
self.result_list.append(mydict)
nextpage = soup.find('span', class_ = 'next').a
if nextpage:
nexturl = self.baseurl + nextpage['href']
text = self.start_requests(nexturl)
self.parse(text)
def write_json(self, result):
s = json.dumps(result, indent = 4, ensure_ascii=False)
with open('movies.json', 'w', encoding = 'utf-8') as f:
f.write(s)
def start(self):
text = self.start_requests(self.baseurl)
self.parse(text)
self.write_json(self.result_list)
douban = Doubantop()
douban.start()
这样做的好处是,result_list
和baseurl
两个变量不是所有代码的全局变量,只是这个类中的全局变量,相当于把数据封装到这里。当代码更加复杂,涉及到多个类的时候,这样组织代码可以分工明确,变量之间不会交叉混乱。
多个类的例子可以看这篇文章感受一下
至此,代码改进系列结束,看完的读者应该可以随心所欲设计代码了。接下来就要开始熟练解析库、将数据存储成各种格式、了解各种反爬策略的学习了。
专栏信息
专栏主页:python编程
专栏目录:目录
爬虫目录:爬虫系列目录
版本说明:软件及包版本说明