嘻嘻,这个网站内容好像挺有意思,不过,下一秒它们就是我的了
运行环境:python3.6
爬虫框架:scrapy
数据库:sqlite
web框架:Django,bootstrap
安装依赖:
python -m pip install scrapy
python -m pip install django
python -m pip install Pillow
运行方式:
启动爬虫
cd bookspider
python start.py
启动网站
cd djangotest
python manage.py migrate
python manage.py runserver
本项目数据库表使用Django数据库迁移命令自动生成,为了保证爬虫爬取到的数据可以用于自己的Web项目,因此定义的scrapy中item和Django中的数据模型必须存在一定的对应关系,具体如下:
Book表:
class Book(models.Model):
#title对应item中的bookName
title = models.CharField(max_length=80, verbose_name='书名')
#cover对应item中的cover
cover = models.ImageField(upload_to='cover/',verbose_name='封面')
#author对应item中的author
author = models.CharField(max_length=50, verbose_name='作者')
#intror对应item中的intro
intro = models.TextField(verbose_name='简介')
#外键关联tag,对应category
tag = models.ForeignKey('Tag',verbose_name='标签')
Chapter表:
class Chapter(models.Model):
#number对应item的number
number = models.IntegerField(verbose_name='章节号')
#title对应item的chapterName
title = models.CharField(max_length=50,verbose_name='章节名')
#content对应item的chapterContent
content = models.TextField(verbose_name='内容')
#外键关联book表
book = models.ForeignKey('Book',verbose_name='书名')
Tag表:
class Tag(models.Model):
#Tag其实就是item中的category,因此tagname对应categoryName
tagname = models.CharField(max_length=30,verbose_name='标签名')
对应关系搞好了后,爬虫下载下来的数据便可以直接用于自身web项目,无需多做别的操作。
爬虫基本是直接移植了我之前写的全书网爬虫,主要业务逻辑没变,只是将最后分类存储为txt文本改变为存储到数据库,完善了部分代码,加入了文章简介、作者信息的提取。
class QuanshuwangSpider(scrapy.Spider):
name = 'quanshuwang'
allowed_domains = ['quanshuwang.com']
start_urls = ['http://quanshuwang.com/']
def parse(self, response):
#提取网页上每个分类
categorys = response.xpath("//ul[@class='channel-nav-list']/li/a")
#循环遍历每个分类
for category in categorys:
#获取分类url链接
categoryUrl = category.xpath("./@href").extract()[0]
#获取分类名称
categoryName = category.xpath("./text()").extract()[0]
#传递
yield scrapy.Request(categoryUrl,meta={"categoryName":categoryName},callback=self.getNext)
提取到各分类信息后,记录下分类的名称,然后循环遍历各分类,getNext方法用于遍历每个分类下的所有子页面,并提取所有书本的url
def getNext(self,response):
#接收上面传过来的类别名称
categoryName = response.meta["categoryName"]
#获取下一页url
nextUrl = response.xpath("//a[@class='next']/@href").extract()[0]
#获取当前页面上所有图书的url
urls = response.xpath("//ul[@class='seeWell cf']/li/span/a[1]/@href").extract()
for url in urls:
yield scrapy.Request(url,meta={"categoryName":categoryName},callback=self.getBooks)
#递归终止条件:不存在下一页则结束
if not response.xpath("//a[@class='next']/@href").extract():
pass
else:
#若存在下一页则调用自己,继续提取下一页
yield scrapy.Request(nextUrl,meta={"categoryName":categoryName},callback=self.getNext)
接下来用getBooks方法爬取每本图书的详情页,利用xpath可以很方便的提取到书名,作者,简介,封面图片等各类信息:
def getBooks(self,response):
#接收参数
categoryName = response.meta["categoryName"]
#xpath提取各类信息
bookName = response.xpath("//div[@class='b-info']/h1/text()").extract()[0]
bookUrl = response.xpath("//div[@class='b-oper']/a[@class='reader']/@href").extract()[0]
author = response.xpath("//div[@class='bookDetail']/dl[@class='bookso']/dd[1]/text()").extract()[0].strip()
intro = response.xpath("//div[@id='waa']/text()").extract()[0].strip()
imgUrl = response.xpath("//a[@class='l mr11']/img/@src").extract()[0]
#保存封面图片(应该写在pipelines.py里面的,为了方便就直接写这里了)
filename = bookName + '.jpg'
dirpath = './cover'
if not os.path.exists(dirpath):
os.makedirs(dirpath)
filepath = os.path.join(dirpath, filename)
urllib.request.urlretrieve(imgUrl, filepath)
cover = 'cover/' + filename
# 继续下一个页面
yield scrapy.Request(bookUrl,meta={"categoryName":categoryName,
'bookName':bookName,
'bookUrl':bookUrl,
'author':author,
'intro':intro,
'cover':cover
},callback=self.getChapter)
getChapter方法用于提取书本各章节的顺序以及名称等信息,并获取到所有章节内容对应的url
def getChapter(self,response):
#接收参数
categoryName = response.meta["categoryName"]
bookName = response.meta["bookName"]
bookUrl = response.meta["bookUrl"]
author = response.meta["author"]
intro = response.meta["intro"]
cover = response.meta["cover"]
#提取页面上所有章节
chapters = response.xpath("//div[@class='clearfix dirconone']//li/a")
#number用于记录章节顺序,防止错位
number = 0
#循环遍历每个章节
for chapter in chapters:
number += 1
#获取章节名称、url
chapterName = chapter.xpath("./text()").extract()[0]
chapterUrl = chapter.xpath("./@href").extract()[0]
#继续传递
yield scrapy.Request(chapterUrl,meta={
'categoryName':categoryName,
'bookName': bookName,
'bookUrl': bookUrl,
'chapterName': chapterName,
'chapterUrl': chapterUrl,
'author': author,
'intro': intro,
'cover': cover,
'number':number
},callback=self.getContent)
getContent方法为提取信息的最后一步,这一步可以获取到章节的详细内容,生成并返回item
def getContent(self,response):
#接收传过来的参数
categoryName = response.meta["categoryName"]
bookName = response.meta["bookName"]
bookUrl = response.meta["bookUrl"]
chapterName = response.meta["chapterName"]
chapterUrl = response.meta["chapterUrl"]
author = response.meta["author"]
intro = response.meta["intro"]
cover = response.meta["cover"]
number = response.meta["number"]
#提取章节的文本内容并调整格式
chapterContent = "".join(response.xpath("//div[@id='content']/text()").extract())
chapterContent = chapterContent.replace(r'''
''',"<br>")
chapterContent = chapterContent.replace(r" ", " ")
#生成并返回item
item = BookSpiderItem()
item["categoryName"] = categoryName
item["bookName"] = bookName
item["bookUrl"] = bookUrl
item["chapterName"] = chapterName
item["chapterUrl"] = chapterUrl
item["chapterContent"] = chapterContent
item["author"] = author
item["intro"] = intro
item["cover"] = cover
item["number"] = number
return item
至此爬虫的主要代码已经基本写完了,剩下的就是信息存储到数据库的各种sql语句了,若程序执行时间够久,理论上可以爬取到此网站上的全部书本。
在上述爬虫爬取到数据之后,便可以直接将存有数据的数据库移动到Django项目中,从而达到建立自己的图书网站的目的。 本次使用了Django官方推荐的通用视图类,网站主要分为三个页面:主页(IndexView)、图书详情页(BookView)以及章节详情页(ChapterView),前端模板页面使用了bootstrap框架,整个网站风格比较简洁,运行效果如下:
一个专属的,没有广告的图书网站就诞生了。→_→