爬虫学习记录-12

  • scrapy shell的使用

直接在Windows终端中输入scrapy shell 域名,注意:不用进入Python和ipython的环境

语法:①response.body、②response.text、③response.url、④response.status、

           ⑤response.xpath()、    (xpath返回的是列表)

  • scrapy_yield使用

我们通过当当网案例来练习scrapy框架和yield的使用,此案例的需求是从当当网下载100页的图书数据。首先是创建scrapy项目,创建爬虫文件,然后是手动修改url,在上个视频中学到pipelines和items,分别是下载数据和定义数据机构的,在此次案例中我们将用到他们

下面来定义数据结构,数据结构通俗的说就是你要下载的数据都有什么,在items.py中定义你要下载的数据

src = scrapy.Field()
name = scrapy.Field()
price = scrapy.Field()

接下来我们就要尝试去爬取我们想要的数据,首先我们要先在接口中定位我们想要的数据,定位到我们想要的数据后就可以for循环遍历下载了,但在这里我们使用一个更高效的方法,因为所有的seletor的对象都可以再次调用xpath方法且src、name和price共享li标签,所以我们可以先写此行代码然后再for循环,又因为xpath返回的是列表,所以要提取data

li_list = response.xpath('//ul[@id="component_59"]/li')
for li in li_list:
    src = li.xpath('.//img/@src').extract_first()
    name = li.xpath('.//img/@alt').extract_first()
    price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
    print(src,name,price)

但在运行爬虫之后发现爬取的图片url都是相同的,经查找发现是反爬懒加载的原因,经查看网页发现此种懒加载并不是从src变成src2,而是变成data-original,以后再定位数据时如果有data-original就不用src。但是再细心的观察会发现第一本书的图片url为none,经查找原因发现是第一张图片只有src而没有data-original,第一张图片和其他图片不一样,图片url就是src,所以我们可以使用一个ifelse语句判断一下

if src:
    src = src
else:
    src = li.xpath('.//img/@src').extract_first()

拿到了数据之后我们就要将数据保存起来,这里就可以用到pipelines。数据是通过items定义的数据的结构,定义完了我们要使用它:在自定义的爬虫文件中定义一个对象book,需要注意的是我们如果想要使用这个类需要先导入from scrapy_dangdangwang.items import ScrapyDangdangwangItem,虽然会报错(编译器的原因)但不影响使用

book = ScrapyDangdangwangItem(src=src,name=name,price=price)

这个对象交给pipelines下载,如何交呢,这里就会用到yield,yield简单理解就是return一个返回值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始,也就是获得一个对象,返回给管道一个,获得一个返回一个

yield book

  • scrapy管道封装

接下来就要用管道保存,首先如果想要使用管道,就必须在settings中开启管道,即将其注释解开,管道可以有多个且有优先级,优先级的范围是1-1000,值越小优先级越高

ITEM_PIPELINES = {
   "scrapy_dangdangwang.pipelines.ScrapyDangdangwangPipeline": 300,
}

然后就是在pipelines中将数据下载,函数process_item(self, item, spider)中的items就是yield的book对象,在函数中将数据下载到文件中

def process_item(self, item, spider):
    fp = open('book.json','w',encoding='utf-8')
    fp.write(item)
    return item

运行后发现会报错:TypeError: write() argument must be str, not ScrapyDangdangwangItem,write方法只能写字符串,不能写其他对象,因此我们需要强制转换fp.write(str(item))。再次运行没有报错,但点开book.json文件发现只下载了最后一本书的数据,经思考后是因为w模式是刷新写,每一个对象打开一次文件,覆盖之前内容,w要改成a。再次运行发现还是只下载了最后一本书的数据,经查看代码发现是以下两行代码没有写进for循环。

book = ScrapyDangdangwangItem(src=src,name=name,price=price)
yield book

虽然成功下载了,但不推荐这种方法,因为每传递过来一个对象就要打开一次文件,对文件的操作过于频繁。我们可以使用open_spider()和close_spider()方法,这两个方法分别是在爬虫文件开始执行前就执行的一个方法和在爬虫文件执行完之后再执行的一个方法,代码做如下修改

def open_spider(self,spider):
    self.fp = open('book.json','a',encoding='utf-8')
def process_item(self, item, spider):
    self.fp.write(str(item))
    return item
def close_spider(self,spider):
    self.fp.close()

  • scrapy多条管道下载

为了提高下载效率,我们可以开启多条管道同时下载,一条管道下载数据,一条管道下载图片。

(1).定义管道类:在定义一个类DangDangDownoadPipeline,下载图片可以用urlretieve方法

class DangDangDownoadPipeline:
    def process_item(self, item, spider):
        url = item.get('src')
        filename = './books/' + item.get('name') + '.jpg'
        urllib.request.urlretrieve(url=url,filename=filename)
        return item

(2).在settings中开启管道:在settings中添加如下代码

"scrapy_dangdangwang.pipelines.DangDangDownoadPipeline":301

但在运行后报错:ValueError: unknown url type: '//img3m2.ddimg.cn/15/26/25168092-1_b_6.jpg',经查找后发现是因为src前面没有加协议,属于无效路径,因此我们可以在url前面拼接上http:

url = 'http:' + item.get('src')

  • scrapy多页数据下载

因为每一页爬取的业务逻辑都是一样的,所以我们只需要将执行页的请求再次调用parse方法即可,首先我们要找到每页url的规律,找到规律之后将url拼接起来。首先定义base_url,因为要遍历1到100页,所以需要一个if循环,根据C语言的基础,这里需要给page定义一个初始值1,只要小于100就不断加加,在if循环里拼接url

if page < 100:
    page = page + 1
    url = base_url + str(page) + '-cp01.01.02.00.00.00.html'

接下来的难点在于如何调用parse方法:yield scrapy.Request,scrapy.Request就是scrapy的get请求,scrapy.Request()后面括号里有两个参数,一个是url,另一个是callback,callback是你要执行的方法,需要注意的是方法后面不需要加(),个人感觉这一步有点递归的意思

yield scrapy.Request(url=url,callback=self.parse)

还需要注意的是如果我们要实现多页下载,需要allowed_domains的范围,一般情况下只写域名,即创建时的allowed_domains

运行后只下载了一页的数据并且报错:TypeError: DangdangSpider.parse() missing 1 required positional argument: 'response',报错是因为我后续尝试中在parse后面加了(),之下载一页数据是因为没有修改allowed_domains的范围,终端也提示:DEBUG: Filtered offsite request to 'category.dangdang.com': <GET http://category.dangdang.com/pg2-cp01.01.02.00.00.00.html>,再次运行后基本没问题,但偶尔会报错OSError: [Errno 22] Invalid argument,根据出错的书名里好像都有个*,我估计可能是因为文件名中不能有*的原因,因为只下载了json数据而没有下载图片,图片涉及图片名的问题,我*替换后报错消失且图片下载成功,这是我第一个遇到的视频之外且自己解决的问题,太激动了!!!!

filename = './books/' + item.get('name').replace('*','x') + '.jpg'

参考文章:Python文件操作错误:OSError: [Errno 22] Invalid argument(关于Windows下文件名中的敏感字符)_蜡笔小新星的博客-CSDN博客

在敲代码的时候我还有一个问题:为什么在parse函数下的变量前面都要加self.?经查阅后发现变量前加self.相当于全局变量,而不加self.相当于局部变量,只能在变量定义的函数内部使用

完整代码参考:

class DangdangSpider(scrapy.Spider):
    name = "dangdang"
    allowed_domains = ["category.dangdang.com"]
    start_urls = ["http://category.dangdang.com/cp01.01.02.00.00.00.html"]
    base_url = 'http://category.dangdang.com/pg'
    page = 1

    def parse(self, response):
        #src = //ul[@id="component_59"]/li//img/@src
        #name = //ul[@id="component_59"]/li//img/@alt
        #price = //ul[@id="component_59"]/li//p[@class="price"]/span[1]/text()
        li_list = response.xpath('//ul[@id="component_59"]/li')
        for li in li_list:
            src = li.xpath('.//img/@data-original').extract_first()
            if src:
                src = src
            else:
                src = li.xpath('.//img/@src').extract_first()
            name = li.xpath('.//img/@alt').extract_first()
            price = li.xpath('.//p[@class="price"]/span[1]/text()').extract_first()
            print(src,name,price)
            book = ScrapyDangdangwangItem(src=src,name=name,price=price)
            yield book
#http://category.dangdang.com/pg1-cp01.01.02.00.00.00.html
        if self.page < 100:
            self.page = self.page + 1
            url = self.base_url + str(self.page) + '-cp01.01.02.00.00.00.html'
            yield scrapy.Request(url=url,callback=self.parse)
class ScrapyDangdangwangPipeline:
    def open_spider(self,spider):
        self.fp = open('book.json','a',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 DangDangDownoadPipeline:
    def process_item(self, item, spider):
        url = 'http:' + item.get('src')
        filename = './books/' + item.get('name').replace('*','x') + '.jpg'
        urllib.request.urlretrieve(url=url,filename=filename)
        return item
class ScrapyDangdangwangItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    src = scrapy.Field()
    name = scrapy.Field()
    price = scrapy.Field()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值