1. 编写基类Crawler
根据项目任务,我们需要分别从当当和豆瓣两个网站搜索并爬取网页来获取数据,并写入文件,虽然需要单独编写两个类分别实现对当当和豆瓣网的爬虫操作,但两个类还是有一些共性的部分,比如:搜索之前都需要知道图书的名字、爬取数据后都需要保存到文件中去等等。可以将共性部分抽取出来,设计Crawler基类,如下图所示。

Crawler类包括了两个属性,一个是表示书籍名称的字符串book_name,另一个是存储书籍信息的列表book_data,此外,它还有一个构造函数,以及将图书数据写入文件的成员方法save_book( ),该方法需要一个字符串类型的形参file_name,表示要写入文件的路径与名称。
2. 改写父类的__str__方法
子类或者说派生类继承了基类/父类的方法后可以直接使用,也可以修改。既可以在继承的基础新增功能,也可以重写父类方法,实现与父类完全不同的功能。
直接打印一个实例对象时会输出该对象的地址,但如果想自定义输出内容,就可以通过改写父类object的__str__( )方法来实现,代码如下:

3. 继承父类
在本案例中,我们需要分别爬取当当网和豆瓣网的图书检索信息,所以编写DangDang类和DouBan类用于分别操作当当网和豆瓣网检索图书信息结果页面的HTML文档信息,它们共同的基类/父类是Crawler类,继承关系如下图所示。

从上面的UML类图中可以看出,search_book( )方法和parse_data( )方法是两个子类新增的,在父类中没有,且两个子类中实现的功能也不一样,一个是针对当当网,另一个是针对豆瓣网。
此外,因为爬取的当当网数据保存结果要以JSON格式写入文件,所以在DangDang类中重写了父类Crawler的save_book( )方法,覆盖父类逐行文本写入的方式,取而代之的是直接写入JSON字符串。
派生类/子类的语法格式如下:
class DerivedClassName(BaseClassName):
<statement-1>
……
<statement-N>
如果要在子类中直接引用父类的方法,可以使用super( )方法。我们来写一个简单的DangDang类,继承自Crawler类,只有构造函数。
