目标:通过scrapy爬取当当网热销月榜所有页面数据
新学:xpath解析数据,多页下载,(多)管道下载,管道封装
url = "http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-%s"%(page)
page===>1~25
所需数据{书名,作者,出版社,价格,图片链接}
对应xpath:
name://div[@class="bang_list_box"]/ul/li/div[@class="name"]/text()
author://div[@class="bang_list_box"]/ul/li/div[@class="publisher_info"][1]/a[1]/text()
press://div[@class="bang_list_box"]/ul/li/div[@class="publisher_info"][2]/a[1]/text()
price://div[@class="bang_list_box"]/ul/li/div[@class="price"]/p[1]/span[1]/text()
src://div[@class="bang_list_box"]/ul/li/div[@class="pic"]/a/img/@src
爬虫文件:
import scrapy
class DangdangbookSpider(scrapy.Spider):
name = 'dangdangbook'
allowed_domains = []
start_urls = ['http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-1']
page = 1
base_url = 'http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent30-0-0-1-%s'
def parse(self, response):
msg_dict = {}
msg_list = response.xpath('//div[@class="bang_list_box"]/ul/li')
for msg in msg_list:
msg_dict['name'] = msg.xpath('./div[@class="name"]/a/text()').extract_first()
msg_dict['author'] = msg.xpath('./div[@class="publisher_info"][1]/a[1]/text()').extract_first()
msg_dict['press'] = msg.xpath('./div[@class="publisher_info"][2]/a[1]/text()').extract_first()
msg_dict['price'] = msg.xpath('./div[@class="price"]/p[1]/span[1]/text()').extract_first()
msg_dict['src'] = msg.xpath('./div[@class="pic"]/a/img/@src').extract_first()
yield msg_dict
if self.page<25:
self.page+=1
url = self.base_url%(self.page)
yield scrapy.Request(url=url,callback=self.parse)
分析:
- 得到所有数据列表逐个提取数据为json保存在msg_dict中
- yield msg_dict 迭代器,每次遍历会将得到的该字典传给管道文件进行下载,直到一页遍历结束
- 至此,已经完成第一页的下载,self.page 自动加1直到第25页,url随之变化
- 发生变化后,回调parse函数,scrapy.Request(url=url,callback=self.parse),实现多页下载
管道文件:pinlines.py
import json
import urllib.request
from itemadapter import ItemAdapter
class DangdangbookPipeline:
def open_spider(self,spider):
self.fp = open("dd_sell_well_books.json","w",encoding="utf-8")
def process_item(self, item, spider):
self.fp.write(str(item))
return item
def close_spider(self,spider):
self.fp.close()
class DangDangImgPipeline:
def process_item(self, item, spider):
url = item.get("src")
filename = 'dd_imgs/' + item.get("name").replace("/","-") + '.jpg'
urllib.request.urlretrieve(url=url,filename=filename,)
return item
分析:
- 参数item即为爬虫文件传来的值,即上面的 yield msg_dict
- 本来默认只有函数process_item,为了重复开闭文件(I/O),定义open_spider(self,spider)和close_spiders(self,spider),起到管道封装作用
- 两个函数均需要传入spider参数
- open_spider在开启爬虫前被执行,这里用来开启前将文件打开,以便多次重复写入
- close_spider在关闭爬虫时被执行,这里用来爬取结束时自动关闭文件
- 本来默认的生成的类只有DangdangbookPipeline,这里为了学习多管道下载,创建一个管道DangDangImgPipeline用于下载src的图片,创建新管道的方法仿照默认类即可
- 将下载的图片另存在dd_imgs文件夹中,图片以name+jpg命名,path="dd_imgs/"+name+"jpg"
- 防止name中有"/"导致路径不存在===>item.get("name").replace("/","-")
- 下载图片直接使用urllib库,urllib.request.urlrettieve(url=url,filename=filename)
- 想要能通过管道下载文件,需将settings.py的ITEM_PIPELINES字典的注释去掉,如下
- 想通过多管道下载,需在settings.py的ITEM_PIPELINES字典添加'DangDangBook.pipelines.DangDangImgPipeline':301,即项目名称.piplines.新创建的类:优先级
settings.py
ITEM_PIPELINES = {
'DangDangBook.pipelines.DangdangbookPipeline': 300,
'DangDangBook.pipelines.DangDangImgPipeline':301,
}
说明:优先级即管道下载的优先顺序,从1~1000,越小越优先