【Scrapy框架之请求传参案例】 -- 2019-08-08 20:40:13

原文: http://106.13.73.98/__/141/

在某些情况下,我们爬取的数据不在同一个页面。

例如下面的案例1,我们要爬取一个电影网站,电影的排名、名称、主演分在一页,而其它的信息分在二级子页面中。这时,我们就需要用到请求传参。

案例1:爬取97电影网中所有热门电影的详细信息


97电影网热门电影URL:http://www.55xia.com/movie/hotest/

第一步,爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
from Test.items import TestItem


# 请求传参
class Test01Spider(scrapy.Spider):
    name = 'test01'
    start_urls = ['http://www.55xia.com/movie/hotest/']  # 97电影网热门电影


    def parse(self, response):
        # 获取所有电影的div
        div_list = response.xpath('//div[@class="container-fluid"]/div[@class="media"]')
        for div in div_list:
            # 准备item对象
            item = TestItem()
            item['ranking'] = div.xpath('.//a[2]/text()').extract_first().strip()  # 排行
            item['title'] = div.xpath('.//div[@class="media-body"]/div[2]/dl/dt/h4//text()').extract_first().strip()  # 名称
            starring_list = div.xpath('.//div[@class="media-body"]/div[2]/dl/dd/ul/li[2]//text()')  # 主演列表
            # 格式转换
            for index, starring in enumerate(starring_list):
                starring_list[index] = starring.extract().strip()
            item['starring'] = ' '.join(starring_list)  # 所有主演

            # 获取电影详情的url
            detail_page = div.xpath('.//div[@class="media-body"]/div[2]/dl/dt/h4/a/@href').extract_first()

            # 有的电影没有详情页面,没有的则不再获取其详情
            if detail_page:
                # 开始拼接电影详情页面的url
                detail_url = 'http://www.55xia.com' + detail_page
                # 手动发起请求
                yield scrapy.Request(url=detail_url, callback=self.get_detail_page, meta={'item': item})   # ⚠⚠
                # callback:指定回调函数,即解析的方法
                # meta={'item': item}:实现请求传参,⚠⚠
            # 没有详情页面的直接提交给管道
            else:
                yield item

        # 递归获取所有页面的内容
        page_url = 'http://www.55xia.com/movie/hotest?page=%d'
        for i in range(2, 6):
            url = format(page_url % i)
            yield scrapy.Request(url=url, callback=self.parse)


    # 自己定义的解析方法,用于解析新页面中电影的详情
    def get_detail_page(self, response):
        item = response.meta['item']
        item['director'] = > response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[1]/td[2]/a/text()').extract_first()  # 导演
        item['writers'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[2]/td[2]/a/text()').extract_first()  # 编剧
        item['type'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[3]/td[2]/a/text()').extract_first()  # 类型
        item['region'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[4]/td[2]/a/text()').extract_first()  # 地区
        item['language'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[5]/td[2]/text()').extract_first()  # 语言
        item['release_time'] = response.xpath('/html/body/div[1]/div/div/div[1]/div[1]/div[2]/table/tbody/tr[6]/td[2]/text()').extract_first()  # 上映时间
        # 提交给管道
        yield item

第二步,数据结构模板文件:

import scrapy

class TestItem(scrapy.Item):
    # define the fields for your item here like:
    ranking = scrapy.Field()  # 排行
    title = scrapy.Field()  # 名称
    starring = scrapy.Field()  # 主演
    director = scrapy.Field()  # 导演
    writers = scrapy.Field()  # 编剧
    type = scrapy.Field()  # 类型
    region = scrapy.Field()  # 地区
    language = scrapy.Field()  # 语言
    release_time = scrapy.Field()  # 上映时间

第三步,管道文件:

import json


class TestPipeline(object):
    # fp = None  # 用于文件操作符

    # 重写父类方法,用于打开文件
    def open_spider(self, spider):
        """此方法在运行爬虫文件时自动执行,注意:只会执行一次"""
        self.fp = open('./text.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        # 开始写入文件
        self.fp.write(json.dumps(item.__dict__['_values'], ensure_ascii=False) + '\n')
        # ensure_ascii=False 这样可使数据显示的为中文
        return item  # 如果你还有其它的(优先级低的)管道类,那么你一定要返回item
        
    # 重写父类方法,用于关闭文件
    def close_spider(self, spider):
        """此方法在结束爬虫文件时自动执行,注意:只会执行一次"""
        self.fp.flush()
        self.fp.close()

第四步,配置文件:

# 开启管道类
ITEM_PIPELINES = {
   'Test.pipelines.TestPipeline': 300,
}

展示效果图:
在这里插入图片描述

案例2:爬取彼岸图网的所有图片


第一步,爬虫文件:

# -*- coding: utf-8 -*-
import scrapy
from Test.items import TestItem

class Test01Spider(scrapy.Spider):
    name = 'test01'
    start_urls = ['http://pic.netbian.com/']  # 彼岸图网

    max_page = 100  # 你想要下载多少页图片
    present_page = 2  # 用于标识当前所在的页面
    url = 'http://pic.netbian.com/index_%d.html'  # 用于定位页面的url


    def parse(self, response):
        # 获取每张图片所在的li
        li_list = response.xpath('//ul[@class="clearfix"]/li')

        # 开始解析数据:
        for li in li_list:
            img_src = li.xpath('.//a//img/@src').extract_first()  # type: str  # 获取所有每张图片的路径
            item = TestItem()
            item['name'] = img_src.split('/')[-1]  # 保存图片名称
            img_url = self.start_urls[0] + img_src  # 每张图片的链接
            # 手动向每张图片链接发起请求
            yield scrapy.Request(img_url, callback=self.get_img, meta={'item': item})  # !
            # callback:指定回调函数,即解析的方法
            # meta={'item': item}:请求传参

        # 递归解析所有页面数据
        if self.present_page <= self.max_page:
            url = format(self.url % self.present_page)
            yield scrapy.Request(url, callback=self.parse)
            self.present_page += 1


    def get_img(self, response):
        item = response.meta['item']  # 提取传过来的参数
        item['img_data'] = response.body  # 获取图片bytes数据
        yield item  # 提交给管道

第二步,数据结构模板文件:

import scrapy

class TestItem(scrapy.Item):
    # define the fields for your item here like:
    name = scrapy.Field()  # 图片名称
    img_data = scrapy.Field()  # 图片bytes数据

第三步,管道文件:

import os

class TestPipeline(object):
    def open_spider(self, spider):
        # 定义好用于保存文件的路径
        self.dirname = r'C:\Users\zyg\Desktop\彼岸花\\'
        # 如果不存在此文件夹,则创建
        if not os.path.exists(self.dirname):
            os.mkdir(self.dirname)

    def process_item(self, item, spider):
        img_path = self.dirname + item['name']
        with open(img_path, 'wb') as fp:
            fp.write(item['img_data'])
        return item

第四步,配置文件:

# 开启管道类
ITEM_PIPELINES = {
   'Test.pipelines.TestPipeline': 300,
}

# 下载量较大,所以我们这里将开启的线程数加大
CONCURRENT_REQUESTS = 200

展示效果图:
在这里插入图片描述

原文: http://106.13.73.98/__/141/

猜你喜欢

转载自www.cnblogs.com/gqy02/p/11323701.html