绿色为数据流向
只有当调度器中不存在任何request时, 整个程序才会停止。(注:对于下载失败的URL,Scrapy也会重新下载. )
items.py
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
from scrapy.loader.processors import TakeFirst
class ScrapyprojectItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
class BookItem(scrapy.Item):
# 定义item类:
# 继承scrapy.item
# 所有字段都定义为 scrapy.Field() 不管是什么类型
# ItemLoader返回列表
# 输入输出处理器
name = scrapy.Field(output_processor=TakeFirst()) # 只提取列表中的第一个元素
content = scrapy.Field(output_processor=TakeFirst())
bookname = scrapy.Field(output_processor=TakeFirst())
# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request
from scrapy.loader import ItemLoader
from ScrapyProject.items import BookItem
"""
爬虫流程:
1. 确定start_url
2. 引擎将起始的url交给调度器(存储到队列;去重)
3. 调度器将url 地址发送给Downloader,Downloader发起Requests请求,从互联网上下载网页信息,并返回响应Response(就是这个信息)
4. 将下载的页面内容交给Spider,进行解析(parse函数),yield数据
5. 将处理好的数据items,交给管道pipline进行存储
"""
class BookSpider(scrapy.Spider):
# 爬虫的名称,必须唯一
name = 'book'
base_url = 'http://www.shicimingju.com'
# 限制:爬取的url地址必须是shicimingju.com
#allowed_domains = ['shicimingju.com']
# 起始的url地址,可以有多个 有两种方式指定
#1. start_urls 属性设置=[]
#2. 通过start_requests生成起始的url地址
start_urls = [
'http://www.shicimingju.com/book/sanguoyanyi.html',
'http://www.shicimingju.com/book/xiyouji.html',
'http://www.shicimingju.com/book/hongloumeng.html',
]
def parse(self, response):
# # 响应的url地址
# name = response.url.split("/")[-1]
# self.log('Saved file %s', name)
"""
如何编写好的解析代码:使用scrapy的交互式工具scrapy shell url
如何处理解析后的数据:通过yield返回解析数据的字典格式
如何下载小说章节详情页的链接并下载到本地
"""
# 0. 实例化item对象
# item = BookItem()
# l = ItemLoader(item=BookItem(), response = response)
# 1.获取所有章节的li标签
chapters = response.xpath('//div[@class="book-mulu"]/ul/li')
# 2. 遍历每一个li标签,获取章节的详细网址和章节名称
for chapter in chapters:
# 创建ItemLoader对象,将item与 用response/selector根据情况而定
l = ItemLoader(item=BookItem(), selector=chapter)
detail_url = chapter.xpath('./a/@href').extract_first() # 是列表,而且是select对象 转为 字符串,用extract_first()
# 根据xpath提取数据信息,并填充到item对象的name属性中
l.add_xpath('name','./a/text()')
# 将数据信息填充到item对象的bookname属性中
l.add_value('bookname',response.url.split('/')[-1].strip('.html'))
# name = chapter.xpath('./a/text()').extract_first()
# bookname = response.url.split('/')[-1].strip('.html')
# yield {
# 'detail_url':detail_url,
# 'name':name}
# 存到item里
# item['name'] = name
# item['bookname'] = bookname
# print('item对象: ' ,item)
# 将章节详情页的url提交到调度器的队列,通过Downloader下载器下载并交给self.parse_detail解析器进行解析数据
yield Request(url=self.base_url+detail_url,
callback=self.parse_chapter_detail, # 交给哪个解析器去解析
# meta={'name':name, 'bookname':bookname} # 作为原数据传给第二次要解析的函数 parse_chapter_detail
# meta={'item':item}
meta = {
'item':l.load_item()} # l.load_item():获取item对象
)
def parse_chapter_detail(self, response):
# .xpath('string(.)') 获取该标签和子孙标签的所有文本信息
# 如何将对象转成字符串:
# 转换一个: extract_first()/ get()
# 转换列表中的每一个对象:extract()/ get_all()
content = response.xpath('//div[@class="chapter_content"]')[0].xpath('string(.)').get()
item = response.meta['item']
item['content'] = content
yield item # item是类似于字典
# yield{
# 'name':response.meta['name'],
# 'content':content,
# 'bookname':response.meta['bookname']
# }
pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import os
class ScrapyprojectPipeline(object):
def process_item(self, item, spider):
""" 将章节内容写入对应的章节文件 """
# books/hongloueng
dirname = os.path.join('books',item['bookname'])
if not os.path.exists(dirname):
os.makedirs(dirname) # 递归创建目录
name = item['name']
# 文件名相对路径用join方法拼接,linux路径拼接符是/ , windows路径拼接符是\
filename = os.path.join(dirname, name)
with open (filename,'w',encoding='utf-8') as f:
f.write(item['content'])
print('写入文件%s成功' %(name))
return item
1、item.py文件中定义item类:
class BookItem(scrapy.Item):
# 定义item类:
# 继承scrapy.item
# 所有字段都定义为 scrapy.Field() 不管是什么类型
# ItemLoader返回列表
# 输入输出处理器
name = scrapy.Field(output_processor=TakeFirst()) # 只提取列表中的第一个元素
content = scrapy.Field(output_processor=TakeFirst())
bookname = scrapy.Field(output_processor=TakeFirst())
book.py文件中:
两种做法实现:
方法一、实例化item对象
# 实例化item对象
item = BookItem()
获取信息并存储到item中:
name = chapter.xpath('./a/text()').extract_first()
bookname = response.url.split('/')[-1].strip('.html')
item['name'] = name
item['bookname'] = bookname
print('item对象: ' ,item)
章节详情页的url提交到调度器的队列,通过Downloader下载器下载并交给self.parse_detail解析器进行解析数据
yield Request(url=self.base_url+detail_url,
callback=self.parse_chapter_detail,
meta={
'item':item}
)
方法二、使用ItemLoader
# 创建ItemLoader对象 用response/selector根据情况而定
l = ItemLoader(item=BookItem(), selector=chapters)
根据xpath提取数据信息,并填充到item对象的属性中
l.add_xpath('name','./a/text()')
l.add_value('bookname',response.url.split('/')[-1].strip('.html'))
填充数据有三种方法:
章节详情页的url提交到调度器的队列,通过Downloader下载器下载并交给self.parse_detail解析器进行解析数据
yield Request(url=self.base_url+detail_url,
callback=self.parse_chapter_detail,
meta = {
'item':l.load_item()} # l.load_item():获取item对象
)