scrapy案例——当当网的爬取二
项目名称:当当网的爬取一——爬取科幻小说的书籍数据
案例需求:
1.使用scrapy爬虫技术爬取当当网中科幻小说的书籍数据,包括(图片、标题、作者和价格)
2.将获取到的数据保存在本地josn文件中
3.将图片保存在本地文件夹中
4.实现分页爬取
分析
1.数据包的获取
2.准备工作:
# ROBOTSTXT_OBEY = True
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
#开启管道 ITEM_PIPELINES = { #管道可以有很多个,因此管道有优先级 范围1-1000 值越小优先级越高 'scrapy_dangdang.pipelines.ScrapyDangdangPipeline': 300, #DangDangDownloadPipeline 'scrapy_dangdang.pipelines.DangDangDownloadPipeline': 302, }
3.解析数据
同理
标题: //ul[@id="component_59"]/li/a/img/@alt同理: 价格: //ul[@id="component_59"]/li/p[@class="price"]/span[1]/text() 作者: //ul[@id="component_59"]/li/p[@class="search_book_author"]/span[1]/a[1]/text() 图片: //ul[@id="component_59"]/li/a/img/@src
#当当网爬取数据——科幻小说:图片、名字、价格
li_list=response.xpath('//ul[@id="component_59"]/li')
# url='http://'+response.xpath('/html/body/div[2]/div/div[3]/div[1]/div[1]/div[2]/div/ul/li/a/img/@src').extract()
# for li,urls in zip(li_list,url):
for li in li_list:
src=li.xpath('.//img/@data-original').extract_first()
# 第一张图片和其他图片的标签属性是不一样的 第一张图片的src是可以使用的,其他图片的地址是data-original'
if src:
src='http:'+src
else:
src = 'http:'+li.xpath('.//img/@src').extract_first()
# src = urls
# src = 'http//:' + li.xpath('.//img/@src').extract()
name = li.xpath('.//img/@alt').extract_first()
price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
author=li.xpath('.//p[@class="search_book_author"]/span[1]/a[1]/text()').extract_first()
print('标题:',name)
print('作者:',author)
print('价格:',price)
print('图片:',src)
print('=======================================')
#print(src,name,price)#列表——改动:.extract_first() 图片都一样——懒加载,将src变成data-original
#第一张图片地址为None,因为它没有data-original这个参数
#当当网管道封装
boook=ScrapyDangdangItem(src=src,name=name,price=price,author=author)
#将上面爬取的数据交给pipelines下载
# yield带有yield的函数不再是 - 个普通函数,而是一个生成器generator,可用于迭代
# yield是 - 个类似return的关键字,迭代 - -次遇到yield时就返回yield后面(右边)的值。重点是: 下一 - 次迭代时,从上一 - 次迭代遇到的yield后面的代码(下一 - 行)开始执行
# 简要理解: yield就是return返回 - -个值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始
#获取一个book就将book交给pipelines
yield boook
items.py
class ScrapyDangdangItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
#通俗的说就是你要下载的数据都有什么
#图片
src=scrapy.Field()
#名字
name=scrapy.Field()
#价格
price=scrapy.Field()
#作者
author=scrapy.Field()
4.翻页
第一页 http://category.dangdang.com/cp01.01.03.41.00.00.html
第二页 http://category.dangdang.com/pg2-cp01.03.41.00.00.00.html
第三页 http://category.dangdang.com/pg3-cp01.03.41.00.00.00.html
第四页 http://category.dangdang.com/pg4-cp01.03.41.00.00.00.html
所以总结:
http://category.dangdang.com/pg{}-'-cp01.03.41.00.00.00.html'
if self.page<10:#就不保存完了
self.page=self.page+1
url=self.base_url+str(self.page)+'-cp01.03.41.00.00.00.html'
print('+++++++++++++++++第{}页++++++++++++++++++'.format(self.page))
yield scrapy.Request(url=url,callback=self.parse)
5.保存至本地
def open_spider(self,spider):
print('+'*50)
self.fp=open('book.json','w',encoding='utf-8')
#items就是yield后面的book对象
def process_item(self, item, spider):
#以下这种模式不推荐,因为没传递过来一个对象,那么就打开一次文件,对文件操作过于频繁
# #(1)write方法必须要写一个字符串,而不能是其他的对象
# #(2)w模式 会每一个对象都打开一次文件 覆盖之前的内容 因此改成a
# with open('book1.json','a',encoding='utf-8') as fp:
# fp.write(str(item))
self.fp.write(str(item))
return item
# 在爬虫文件执行之后,才执行的方法
def close_spider(self,spider):
print('-'*50)
self.fp.close()
6.将图片下载至本地books文件夹中
def process_item(self, item, spider):
# url = 'http:' + item.get('src')
url = item.get('src')
filename = './books/' + item.get('name') + '.jpg'
if not os.path.exists('./books/'):
os.makedirs('./books/')
try:
urllib.request.urlretrieve(url=url, filename=filename)
except urllib.error.URLError as e:
print(f"Failed to retrieve {url}: {e.reason}")
return item
7.运行
from scrapy import cmdline
cmdline.execute(['scrapy','crawl','dang','--nolog'])
运行结果: